package models.dataFlowModel;

import java.util.AbstractMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import models.Node;
import models.algebra.Expression;
import models.algebra.Type;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.Selector;

public class ResourceNode extends Node {
	protected ResourceNode parent = null;
	protected Set<ResourceNode> children = null;
	protected ResourceHierarchy resourceHierarchy = null;
	protected Map<DataTransferChannel, ResourcePath> inSide = null;
	protected Map<DataTransferChannel, ResourcePath> outSide = null;
	protected List<Selector> selectors = null;

	public ResourceNode(ResourceNode parent, 
						ResourcePath outSideResource, 
						DataTransferChannel outSideChannel) {
		this.parent = parent;
		this.children = new HashSet<>();
		this.inSide = new HashMap<>();
		this.outSide = new HashMap<>();
		this.outSide.put(outSideChannel, outSideResource);
		this.resourceHierarchy = outSideResource.getResourceHierarchy();
		this.selectors = new ArrayList<>();
		if (resourceHierarchy.getNumParameters() > 0) {
			if (outSideChannel != null) {
				selectors.addAll(outSideChannel.getSelectors());
			} else {
				List<Expression> pathParams = outSideResource.getPathParams();
				selectors.add(new Selector(pathParams.get(pathParams.size() - 1)));
			}
		}
	}

	public ResourceNode(ResourceNode parent, 
						ResourcePath resource,
						DataTransferChannel channel, boolean isInside) {
		this.parent = parent;
		this.children = new HashSet<>();
		this.inSide = new HashMap<>();
		this.outSide = new HashMap<>();
		if (isInside) {
			this.inSide.put(channel, resource);
		} else {
			this.outSide.put(channel, resource);
		}
		this.resourceHierarchy = resource.getResourceHierarchy();
		this.selectors = new ArrayList<>();
		if (resourceHierarchy.getNumParameters() > 0) {
			if (channel != null) {
				selectors.addAll(channel.getSelectors());
			} else {
				List<Expression> pathParams = resource.getPathParams();
				selectors.add(new Selector(pathParams.get(pathParams.size() - 1)));
			}
		}
	}
	
	public ResourceNode(ResourceNode parent, 
						Map<DataTransferChannel, ResourcePath> outSide, 
						Map<DataTransferChannel, ResourcePath> inSide) {
		this.parent = parent;
		this.children = new HashSet<>();
		this.inSide = inSide;
		this.outSide = outSide;
		this.selectors = new ArrayList<>();
		for (Map.Entry<DataTransferChannel, ResourcePath> outsideEnt: outSide.entrySet()) {
			DataTransferChannel outSideChannel = outsideEnt.getKey();
			ResourcePath outSideResource = outsideEnt.getValue();
			if (this.resourceHierarchy  == null) {
				this.resourceHierarchy = outSideResource.getResourceHierarchy();
			}
			if (resourceHierarchy.getNumParameters() > 0) {
				if (outSideChannel != null) {
					selectors.addAll(outSideChannel.getSelectors());
				} else {
					List<Expression> pathParams = outSideResource.getPathParams();
					selectors.add(new Selector(pathParams.get(pathParams.size() - 1)));
				}
			}
		}
	}
	
	public ResourceHierarchy getResourceHierarchy() {
		return resourceHierarchy;
	}
	
	public String getResourceName() {
		return resourceHierarchy.getResourceName();
	}
	
	public Type getResourceStateType() {
		return resourceHierarchy.getResourceStateType();
	}
	
	public int getNumberOfParameters() {
		return resourceHierarchy.getTotalNumParameters();
	}
	
	public ResourceNode getParent() {
		return parent;
	}
	
	public Set<ResourceNode> getChildren() {
		return children;
	}

	public void addChild(ResourceNode child) {
		children.add(child);
		child.parent = this;
	}
	
	public ResourcePath getPrimaryResourcePath() {
		if (outSide.size() > 0) return outSide.values().iterator().next();
		return inSide.values().iterator().next();
	}

	public Collection<ResourcePath> getInSideResources() {
		return inSide.values();
	}
	
	public ResourcePath getInSideResource(DataTransferChannel channel) {
		return inSide.get(channel);
	}
	
	public Collection<ResourcePath> getOutSideResources() {
		return outSide.values();
	}
	
	public ResourcePath getOutSideResource(DataTransferChannel channel) {
		return outSide.get(channel);
	}
	
	public Set<DataTransferChannel> getOutSideChannel() {
		return outSide.keySet();
	}
	
	public void addInSideResource(DataTransferChannel channel, ResourcePath inResource) {
		inSide.put(channel, inResource);
	}
	
	public void addOutSideResource(DataTransferChannel channel, ResourcePath outResource) {
		outSide.put(channel, outResource);
	}

	public List<Selector> getSelectors() {
		return selectors;
	}

	public List<Selector> getAllSelectors() {
		List<Selector> selectors = new ArrayList<>();
		if (parent != null) {
			selectors.addAll(parent.getAllSelectors());
		}
		selectors.addAll(this.selectors);
		return selectors;
	}
	
//	public boolean equals(Object another) {
//		if (this == another) return true;
//		if (!(another instanceof ResourceNode)) return false;
//		return resourcePath.equals(((ResourceNode)another).resourcePath);
//	}
//	
//	public int hashCode() {
//		return resourcePath.hashCode();
//	}
//	
//	public String toString() {
//		return resourcePath.toString();
//	}
}
