package adecWatt.model;

import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.TreeSet;

import adecWatt.model.unit.Building;
import adecWatt.model.unit.Workspace;

public class Circuits {

    static public enum State {
	PLUG, LOOP, SHORTCUT, UNPLUG;
    }
    static public String SHORTCUT = "Shortcut";
    static public String UNPLUG = "Unplug";
    static public String CIRCUITLESS = "Circuitless";

    static public Comparator<String> lineComparator =
	new Comparator<String> () {
	public int compare (String s1, String s2) {
	    if (s1 == s2)
		return 0;
	    try {
	    	int i1 = Integer.parseInt (s1);
	    	int i2 = Integer.parseInt (s2);
	    	return i1-i2;
	    } catch (Exception e) {
	    }
	    return s1.compareTo (s2);
	}
    };

    static public class Circuit {
	HashSet<Acc> nodes = new HashSet<Acc> ();
	TreeSet<String> names = new TreeSet<String> (lineComparator);
	int consumption;
	Circuit (HashSet<Acc> nodes, String name, int consumption) {
	    add (nodes, name, consumption);
	}
	void add (HashSet<Acc> nodes, String name, int consumption) {
	    this.nodes.addAll (nodes);
	    this.consumption += consumption;
	    if (name == null || name.isEmpty ())
		return;
	    names.add (name);
	}
    }

    private HashSet<Acc> loop = new HashSet<Acc> ();
    private HashMap<Comp, Circuit> allCircuits = new HashMap<Comp, Circuit> ();
    private HashSet<Comp> plugedComps;

    public Set<Comp> keySet () { return allCircuits.keySet (); }
    public Circuit get (Comp lastComp) { return allCircuits.get (lastComp); }
    public HashSet<Comp> getPlugedComps () { return plugedComps; }
    public boolean inLoop (Acc acc) { return loop.contains (acc); }

    public Circuits (HashMap<Acc, Comp> linked, HashSet<Comp> plugedComps) {
	this (linked);
	this.plugedComps = plugedComps;
    }
    public Circuits (HashMap<Acc, Comp> linked) {
	loopStart:
	for (Acc beginAcc : linked.keySet ()) {
	    Comp beginComp = (Comp) beginAcc.getContainer ();
	    int consumption = (int) beginAcc.getWatt (beginComp);
	    HashSet<Acc> nodes = new HashSet<Acc> ();
	    nodes.add (beginAcc);
	    String name = beginAcc.getCircuit (beginComp);
	    Comp lastComp = null;
	    parseThread:
	    for (;;) {
		Comp nextComp = linked.get (beginAcc);
		if (nextComp == null)
		    break;
		lastComp = nextComp;
		for (String nextAccId : nextComp.getEmbeddedIds ())
		    try {
			Acc nextAcc = nextComp.findEmbedded (nextAccId);
			if (nextAcc.getConnectedTo () == null)
			    continue;
			if (nodes.contains (nextAcc)) {
			    loop.addAll (nodes);
			    continue loopStart;
			}
			nodes.add (nextAcc);
			beginAcc = nextAcc;
			continue parseThread;
		    } catch (Exception e) {
		    }
		break;
	    }
	    Circuit circuit = allCircuits.get (lastComp);
	    if (circuit == null) {
		allCircuits.put (lastComp, new Circuit (nodes, name, consumption));
		continue;
	    }
	    circuit.add (nodes, name, consumption);
	}
    }

    static public Circuits getCircuits (Workspace workspace, String plugId) {
	Hashtable<String, Comp> namedComps = new Hashtable<String, Comp> ();
	for (Item<?, ?, ?> item : workspace.getInheritedEmbeddedValues ())
	    try {
		namedComps.put (item.getId (), (Comp) item);
	    } catch (Exception e) {
	    }
	Building building = workspace.getBuilding ();
	if (building != null)
	    for (Item<?, ?, ?> item : building.getInheritedEmbeddedValues ())
		try {
		    namedComps.putIfAbsent (item.getId (), (Comp) item);
		} catch (Exception e) {
		}
	HashSet<Comp> plugedComps = new HashSet<Comp> ();
	HashMap<Acc, Comp> linked = new HashMap<Acc, Comp> ();
	for (Comp beginComp : namedComps.values ()) {
	    if (beginComp.isReserved ())
		continue;
	    for (String accId : beginComp.getEmbeddedIds ())
		try {
		    Acc beginAcc = beginComp.findEmbedded (accId);
		    if (beginAcc.getDirectUnit ().isDescendingFrom (plugId))
			plugedComps.add (beginComp);
		    Comp nextComp = namedComps.get (beginAcc.getConnectedTo ());
		    linked.put (beginAcc, nextComp);
		} catch (Exception e) {
		}
	}
	return new Circuits (linked, plugedComps);
    }

    static public class CircuitState {
	public String name;
	public State state;
	public CircuitState (State state, String name) {
	    this.name = name;
	    this.state = state;
	}
    }

    public CircuitState getState (Comp comp, Acc acc) {
	if (acc == null)
	    return null;
	String circuitName = acc.getCircuit (comp);
	if (loop.contains (acc))
	    return new CircuitState (State.LOOP, circuitName);
	for (Comp lastComp : allCircuits.keySet ()) {
	    Circuit circuit = allCircuits.get (lastComp);
	    if (!circuit.nodes.contains (acc))
		continue;
	    switch (circuit.names.size ()) {
	    case 0:
		return null;
	    case 1:
		// XXX limitation pour doublette ?
		// if (circuitName == null)
		//     return null;
		return new CircuitState (lastComp.getLine () == null ? State.UNPLUG : State.PLUG, circuit.names.first ());
	    default:
		return new CircuitState (State.SHORTCUT, circuitName);
	    }
	}
	return circuitName == null ? null : new CircuitState (State.UNPLUG, circuitName);
    }
}
