package models.terms;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

import lombok.Getter;
import models.algebra.Symbol;

@Getter
public class DependencyTerm extends EvaluatableTerm{

	private EvaluatableTerm dependingTerm;
	private List<Resource> dependedResources = new ArrayList<>();
	private List<EvaluatableTerm> argumentTerms = new ArrayList<>();
	
	
//	public DependencyTerm(EvaluatableTerm dependingTerm, EvaluatableTerm dependedTerm, EvaluatableTerm argumentTerm) {
//		super(
//				new Symbol(":", 3), 
//				dependedTerm.getOrder() == argumentTerm.getOrder() ? dependedTerm.getOrder() : dependedTerm.getOrder() - 1,
//				dependingTerm.getSize() + argumentTerm.getSize() + dependedTerm.getSize()
//		);
//		this.dependingTerm = dependingTerm;
//		this.dependedTerms.add(dependedTerm);
//		this.argumentTerms.add(argumentTerm);
//		addChild(dependingTerm);
//		addChild(dependedTerm);
//		addChild(argumentTerm);
//	}
	
	public DependencyTerm(EvaluatableTerm dependingTerm, List<Resource> dependedTerms, List<EvaluatableTerm> argumentTerms) {
		super(
				new Symbol(":", 1 + dependedTerms.size() + argumentTerms.size()), 
				dependedTerms.get(0).getOrder() == argumentTerms.get(0).getOrder() ? dependedTerms.get(0).getOrder() : argumentTerms.get(0).getOrder() - 1,
				dependingTerm.getSize() + argumentTerms.get(0).getSize() + dependedTerms.get(0).getSize()
		);
		boolean dependedTermsOrderCheck = dependedTerms.stream().allMatch(e -> e.getOrder() == dependedTerms.get(0).getOrder());
//		boolean argumentTermsOrderCheck = argumentTerms.stream().allMatch(e -> e.getOrder() == argumentTerms.get(0).getOrder());
		if (! (dependedTermsOrderCheck)) {
			throw new RuntimeException("Orders are not all equals");
		}
		if (dependedTerms.size() != argumentTerms.size()) {
			throw new RuntimeException("Size not equals");
		}
		this.dependingTerm = dependingTerm;
		this.dependedResources = new ArrayList<>(dependedTerms);
		this.argumentTerms = new ArrayList<>(argumentTerms);
		addChild(dependingTerm);
		for (int i = 0; i < dependedTerms.size(); i++) {
			addChild(dependedTerms.get(i));
			addChild(argumentTerms.get(i));
		}
	}
	
	public DependencyTerm(EvaluatableTerm dependingTerm, EvaluatableTerm ...terms) {
		this(
				dependingTerm, 
				IntStream.range(0, terms.length).filter(i -> i % 2 == 0).mapToObj(i -> (Resource) terms[i]).toList(), 
				IntStream.range(0, terms.length).filter(i -> i % 2 == 1).mapToObj(i -> terms[i]).toList()
			);
	}
	
	@Override
	public boolean isLinearRightNormalized() {
		return isLinearRightNormaled(0);
	}
	
	@Override
	public EvaluatableTerm linearRightNormalize() {
		DependencyTerm newTerm = (DependencyTerm) clone();
		newTerm.selfLinearRightNormalize();
		return newTerm;
	}
	
	@Override
	public void selfLinearRightNormalize() {
//		if(dependingTerm instanceof ResourceVariable || dependingTerm instanceof SetEvaluatableTerm) {
//			argumentTerm.selfLinearRightNormalize();
//			return;
//		}
//		DependencyTerm dependencyTerm = (DependencyTerm) dependingTerm;
//		if(! dependencyTerm.isLinearRightNormalized()) {
//			dependencyTerm.selfLinearRightNormalize();
//		}
//		if(! isLinearRightNormalized()) {
//			EvaluatableTerm childArgumentTerm = dependencyTerm.getArgumentTerm();
//			DependencyTerm nextDependencyTerm = new DependencyTerm(childArgumentTerm, dependedVariable, argumentTerm);
//			this.dependingTerm = dependencyTerm.getDependingTerm();
//			this.dependedVariable = dependencyTerm.getDependedVariable();
//			this.argumentTerm = nextDependencyTerm;
//			this.setChild(2, nextDependencyTerm);
//			this.setChild(0, dependencyTerm.getDependingTerm());
//			this.setChild(1, dependencyTerm.getDependedVariable());
//		}
//		argumentTerm.selfLinearRightNormalize();
		
	}
	
	private boolean isLinearRightNormaled(int depth) {
//		if(dependingTerm instanceof ResourceVariable || dependingTerm instanceof SetEvaluatableTerm) {
//			return dependingTerm.getOrder() == dependedVariable.getOrder();
//		}
//		DependencyTerm dependencyTerm = (DependencyTerm) dependingTerm;
//		if(
//				dependencyTerm.isLinearRightNormaled(depth + 1) && 
//				dependencyTerm.getDependedVariable().getOrder() - 1 == dependedVariable.getOrder() && 
//				dependencyTerm.getOrder() == dependedVariable.getOrder() &&
//				argumentTerm.getOrder() <= dependencyTerm.getOrder() &&
//				depth == 0
//		) {
//			return true;
//		}
//		if(
//				dependencyTerm.isLinearRightNormaled(depth + 1) && 
//				dependencyTerm.getDependedVariable().getOrder() - 1 == dependedVariable.getOrder() && 
//				dependencyTerm.getOrder() == dependedVariable.getOrder() &&
//				argumentTerm.getOrder() < dependencyTerm.getOrder()
//		) {
//			return true;
//		}
		
		return false;
	}
	
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append('[');
		sb.append(getDependingTerm().toString());
		sb.append(" : ");
		for (int i = 0; i < dependedResources.size(); i++) {
			sb.append(dependedResources.get(i).toString());
			sb.append(" -> ");
			sb.append(argumentTerms.get(i).toString());
			sb.append(", ");
		}
		sb.deleteCharAt(sb.length() - 1);
		sb.deleteCharAt(sb.length() - 1);
		sb.append(']');
		return sb.toString();
	}
	
	@Override
	public String toStringWithOrder() {
		StringBuilder sb = new StringBuilder();
		sb.append('[');
		sb.append(getDependingTerm().toStringWithOrder());
		sb.append(" : ");
		for (int i = 0; i < dependedResources.size(); i++) {
			sb.append(dependedResources.get(i).toStringWithOrder());
			sb.append(" -> ");
			sb.append(argumentTerms.get(i).toStringWithOrder());
			sb.append(", ");
		}
		sb.deleteCharAt(sb.length() - 1);
		sb.append(']');
		sb.append('(');
		sb.append(order);
		sb.append(')');
		return sb.toString();
	}
	
	@Override
	public boolean equals(Object another) {
		if(! (another instanceof DependencyTerm)) {
			return false;
		}
		DependencyTerm term = (DependencyTerm) another;
		
		return dependingTerm.equals(term.getDependingTerm()) && 
				dependedResources.stream().sorted().toList().equals(term.getDependedResources().stream().sorted().toList()) &&
				argumentTerms.stream().sorted().toList().equals(term.getArgumentTerms().stream().sorted().toList());
	}

	@Override
	public int hashCode() {
		return ("DT" + toString()).hashCode();
	}

	@Override
	public Object clone() {
		return new DependencyTerm(
				(EvaluatableTerm) dependingTerm.clone(),
				new ArrayList<>(dependedResources),
				new ArrayList<>(argumentTerms)
		);
	}


}
