package models.dataConstraintModel;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import models.algebra.Expression;
import models.algebra.Term;
import models.algebra.Type;
import models.algebra.Variable;

public class ResourceHierarchy {
	private ResourceHierarchy parent = null;
	private Set<ResourceHierarchy> children = null;
	private String resourceName = null;
	private Type resourceStateType = null;
	private int numParameters = 0;
	protected Expression initialValue;
	protected String initText;
	
	public ResourceHierarchy(String resourceName) {
		this.parent = null;
		this.children = new HashSet<>();
		this.resourceName = resourceName;
		this.numParameters = 0;
	}
	
	public ResourceHierarchy(String resourceName, Type resourceStateType) {
		this.parent = null;
		this.children = new HashSet<>();
		this.resourceName = resourceName;
		this.numParameters = 0;
		this.resourceStateType = resourceStateType;
	}

	public ResourceHierarchy(ResourceHierarchy parent, String resourceName) {
		this.parent = parent;
		this.children = new HashSet<>();
		this.resourceName = resourceName;
		this.numParameters = 0;
		if (parent != null) {
			parent.addChild(this);
			if (parent.getResourceStateType() == null) {
				parent.setResourceStateType(DataConstraintModel.typeJson);
			}
		}
	}

	public ResourceHierarchy(ResourceHierarchy parent, String resourceName, Type resourceStateType) {
		this.parent = parent;
		this.children = new HashSet<>();
		this.resourceName = resourceName;
		this.numParameters = 0;
		this.resourceStateType = resourceStateType;
		if (parent != null) {
			parent.addChild(this);
			if (parent.getResourceStateType() == null) {
				parent.setResourceStateType(DataConstraintModel.typeJson);
			}
		}
	}
	
	public ResourceHierarchy(ResourceHierarchy parent, int numParameters) {
		this.parent = parent;
		this.children = new HashSet<>();
		this.resourceName = null;
		this.numParameters = numParameters;
		if (parent != null) {
			parent.addChild(this);
		}
	}
	
	public ResourceHierarchy(ResourceHierarchy parent, Expression parameterExp) {
		this.parent = parent;
		this.children = new HashSet<>();
		this.resourceName = null;
		this.numParameters = 1;
		if (parent != null) {
			parent.addChild(this);
			if (parent.getResourceStateType() == null) {
				Type paramType = null;
				if (parameterExp instanceof Variable) {
					paramType = ((Variable) parameterExp).getType();
				} else if (parameterExp instanceof Term) {
					paramType = ((Term) parameterExp).getType();			
				}
				if (paramType != null) {
					if (paramType.equals(DataConstraintModel.typeInt)) {
						parent.setResourceStateType(DataConstraintModel.typeList);
					} else if (paramType.equals(DataConstraintModel.typeString)) {
						parent.setResourceStateType(DataConstraintModel.typeMap);
					}
				}
			}
		}
	}
	
	public ResourceHierarchy(ResourceHierarchy parent, Expression parameterExp, Type resourceStateType) {
		this.parent = parent;
		this.resourceName = null;
		this.numParameters = 1;
		this.resourceStateType = resourceStateType;
		if (parent != null) {
			parent.addChild(this);
			if (parent.getResourceStateType() == null) {
				Type paramType = null;
				if (parameterExp instanceof Variable) {
					paramType = ((Variable) parameterExp).getType();
				} else if (parameterExp instanceof Term) {
					paramType = ((Term) parameterExp).getType();			
				}
				if (paramType.equals(DataConstraintModel.typeInt)) {
					parent.setResourceStateType(DataConstraintModel.typeList);
				} else if (paramType.equals(DataConstraintModel.typeString)) {
					parent.setResourceStateType(DataConstraintModel.typeMap);
				}
			}
		}
	}
	
	public ResourceHierarchy(ResourceHierarchy parent, int numParameters, Type resourceStateType) {
		this.parent = parent;
		this.resourceName = null;
		this.numParameters = numParameters;
		this.resourceStateType = resourceStateType;
		if (parent != null) {
			parent.addChild(this);
		}
	}

	public ResourceHierarchy getParent() {
		return parent;
	}

	public void setParent(ResourceHierarchy parent) {
		this.parent = parent;
		if (parent != null) {
			parent.addChild(this);
		}
	}
	
	public Set<ResourceHierarchy> getChildren() {
		return children;
	}

	protected void addChild(ResourceHierarchy child) {
		children.add(child);
	}

	public ResourceHierarchy getRoot() {
		if (parent == null) return this;
		return parent.getRoot();
	}
	
	public boolean isAncestorOf(ResourceHierarchy another) {
		if (another == null) return false;
		if (this == another) return true;
		return isAncestorOf(another.getParent());
	}

	public String getResourceName() {
		if (resourceName == null) return parent.getResourceName();
		return resourceName;
	}

	public void setResourceName(String resourceName) {
		this.resourceName = resourceName;
	}

	public Type getResourceStateType() {
		return resourceStateType;
	}

	public void setResourceStateType(Type resourceStateType) {
		this.resourceStateType = resourceStateType;
		if (initialValue != null) {
			if (initialValue instanceof Term) {
				((Term) initialValue).setType(resourceStateType);
			}
		}
	}
	
	public int getNumParameters() {
		return numParameters;
	}

	public int getTotalNumParameters() {
		if (parent == null) return numParameters;
		return numParameters + parent.getTotalNumParameters();
	}	

	public void setNumParameters(int numParameters) {
		this.numParameters = numParameters;
	}
	
	public Expression getInitialValue() {
		return initialValue;
	}

	public void setInitialValue(Expression initialValue) {
		this.initialValue = initialValue;
	}

	public String getInitText() {
		return initText;
	}

	public void setInitText(String initText) {
		this.initText = initText;
	}
	
	public String toString() {
		if (parent == null) return resourceName;
		if (resourceName != null) {
			return parent.toString() + "." + resourceName;
		} else {
			return parent.toString() + ".{}";
		}
	}

	public String toString(List<Expression> pathParams) {
		if (parent == null) return resourceName;
		if (resourceName != null) {
			return parent.toString(pathParams) + "." + resourceName;
		} else {
			Expression lastParam = pathParams.get(pathParams.size() - 1);
			pathParams = pathParams.subList(0, pathParams.size() - 1);
			return parent.toString(pathParams) + ".{" + lastParam +"}";
		}
	}
	
	public String toResourcePath(List<String> pathParams) {
		if (parent == null) return resourceName;
		if (resourceName != null) {
			return parent.toResourcePath(pathParams) + "/" + resourceName;
		} else {
			String lastParam = pathParams.get(pathParams.size() - 1);
			pathParams = pathParams.subList(0, pathParams.size() - 1);
			return parent.toResourcePath(pathParams) + "/" + lastParam;
		}
	}
}