
package com.wickedcooljava.sci.component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;


/**
 * @author brian
 */
public class MetaComponent<T> implements Component<T> {

	public interface MetaVisitor<T> {
		public void visit(Component<T> component);
	}
	
	private HashMap<OutputPort<T>,WireImpl<T>> sourceWireMap;
	private ArrayList<Wire<T>> wires;
	private HashSet<Component<T>> subComponents;
	private ArrayList<InputPort<T>> externalInputs = new ArrayList<InputPort<T>>();
	private ArrayList<OutputPort<T>> externalOutputs = new ArrayList<OutputPort<T>>();

	public void accept(MetaVisitor<T> v) {
		for (Component<T> item : subComponents) {
			v.visit(item);
		}
	}
	
	public int getInputSize() {
		return externalInputs.size();
	}

	public int getOutputSize() {
		return externalOutputs.size();
	}
	
	public InputPort<T> getInputPort(int index) {
		return externalInputs.get(index);
	}

	public OutputPort<T> getOutputPort(int index) {
		return externalOutputs.get(index);
	}
	
	public MetaComponent() {
		subComponents = new HashSet<Component<T>>();
		wires = new ArrayList<Wire<T>>();
		sourceWireMap = new HashMap<OutputPort<T>,WireImpl<T>>();
	}

	public void addConnection(OutputPort<T> src, InputPort<T>... targets) {
		for (InputPort<T> target : targets) {
			addConnection(src, target);
		}
	}
	
	public void addConnection(OutputPort<T> src, InputPort<T> target) {
		// add ports' parent components to my subcomponent list
		subComponents.add(src.getParent());
		subComponents.add(target.getParent());
		WireImpl<T> srcWire = sourceWireMap.get(src);
		if (srcWire == null) {
			// make a new wire
			srcWire = new WireImpl<T>(src);
			sourceWireMap.put(src, srcWire);
		}
		// TODO: Remove any old wire connections using the target?
		// add the target port to the wire
		srcWire.addTargetPort(target);
	}

	private void propagateSignals() {
		for (Wire<T> w : wires) {
			w.propagateSignal();
		}
	}
	
	private void processSubComponents() {
		for (Component<T> item : subComponents) {
			item.process();
		}
	}
	
	public void process() {
		processSubComponents();
		propagateSignals();
	}
	
	public void processRepeat(int count) {
		if (count <= 0) {
			return;
		}
		for (int i=0; i<count; i++) {
			process();
		}
	}

	public void addExternalPort(InputPort<T> port) {
		subComponents.add(port.getParent());
		externalInputs.add(port);
	}
	
	public void addExternalPort(OutputPort<T> port) {
		subComponents.add(port.getParent());
		externalOutputs.add(port);
	}

}
