package concurrentModel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import model.Model;
import model.ModelInterface;
import model.State;
import model.Transition;

public class ConcurrentSystemModelMaker {
	static HashMap<String, ConcurrentState> states;
	static List<Transition> updatePart = new ArrayList<Transition>();
	static List<String> controllableActions ;
	static int updateNumber = 0;

	private static ConcurrentState makeModel(State e, State[] reqMoni) {
		ConcurrentState c = new ConcurrentState(e, reqMoni,
				updateNumber);
		states.put(c.getName(), c);
		return c;
	}

	private static void makeErrorModel() {
		ConcurrentState c = new ConcurrentState("ERROR");
		states.put("ERROR", c);
	}

	public static void setConcurrentModel(ConcurrentModel cm) {
		states = cm.states;
	}

	public static void setControllableAction(List<String> ca) {
		controllableActions = ca;
	}

	private static void compose(State e, State[] initialStates,
			ConcurrentState c) {
		for (int i = 0; i < e.getToTransitionNum(); i++) {
			transition(e.getToTransition(i), initialStates, c);
		}
	}

	private static void transition(Transition te, State[] moni,
			ConcurrentState c) {
		Transition[] tm = new Transition[moni.length];
		State[] toMoni = new State[moni.length];
		Transition tc = new Transition(te.toString());
		tc.setFrom(c);
		c.addToTransition(tc);
		updatePart.add(tc);
		if (controllableActions.contains(te.toString()))
			tc.setIsControllable();
		//System.out.println("transition:"+tc+","+tc.isControllable());
		for (int i = 0; i < moni.length; i++) {
			tm[i] = moni[i].getToTransition(te.toString());
			if (tm[i] != null) {
				toMoni[i] = tm[i].getTo();
				if (toMoni[i].getName().equals("ERROR")) {
					tc.setTo(states.get("ERROR"));
					states.get("ERROR").addFromTransition(tc);
					return;
				}
			} else {
				toMoni[i] = moni[i];
			}
		}
		ConcurrentState targetM = getConcurrentStateFromMap(te.getTo(),
				toMoni);
		if(targetM!=null){
			targetM.addFromTransition(tc);
			tc.setTo(targetM);
		}else{
			targetM = makeModel(te.getTo(), toMoni);
			targetM.addFromTransition(tc);
			tc.setTo(targetM);
			compose(te.getTo(), toMoni, targetM);

		}
	}

	public static ConcurrentState getConcurrentStateFromMap(State env,
			State[] moni) {
		String stateName = env.getName();
		for (int i = 0; i < moni.length; i++) {
			stateName += moni[i].getName();
		}

		return states.get(stateName);
	}

	public static ConcurrentModel makeConccurrentSystem(ModelInterface env,
			ModelInterface[] moni) {
		states=new HashMap<String,ConcurrentState>();
		State[] initialStates = new State[moni.length];
		for (int i = 0; i < moni.length; i++) {
			initialStates[i] = moni[i].getInitialState();
		}

		ConcurrentState c = makeModel(env.getInitialState(), initialStates);
		ConcurrentModel cm = new ConcurrentModel();

		makeErrorModel();
		compose(env.getInitialState(), initialStates, c);
		cm.setModelMaterials(states,c);
		return cm;
	}

	public static ConcurrentModel makeConcurrentSystem(Model e,
			Model[] reqs, List<String> cas) {
		ConcurrentModel cm = null;

		return makeConccurrentSystem(e,reqs);
	}

	public static Model attachTransition(Model e, String from, String t,
			String to) {
		Transition tr = new Transition(t, e.getState(from), e.getState(to));
		e.getState(from).addToTransition(tr);
		e.getState(to).addFromTransition(tr);
		return e;
	}

	private static List<ConcurrentState> getCandidate(Transition tr,
			ConcurrentModel cm) {
		List<ConcurrentState> l = new ArrayList<ConcurrentState>();
		for (int i = 2; i < cm.getSize(); i++) {
			ConcurrentState cs = cm.getConcurrentState(i);
			if (cs.getEnv()!=null&&cs.getEnv().equals(tr.getFrom().toString())) {
				l.add(cs);
			}
		}
		return l;
	}

	public static ConcurrentModel modelUpdate(ConcurrentModel cm, Model e,
			Model[] moni, String from, String t, String to) {
		updateNumber++;
		updatePart = new ArrayList<Transition>();
		states=cm.getStates();
		e = attachTransition(e, from, t, to);
		Transition target = e.getState(from).getToTransition(t);
		setConcurrentModel(cm);
		List<ConcurrentState> lcs = getCandidate(target, cm);
		for (int i = 0; i < lcs.size(); i++) {
			State[]m=new State[moni.length];
			for(int j=0;j<moni.length;j++){
				m[j] = moni[j].getState(lcs.get(i).reqMoni[j]);
			}
//			for(State s:m)System.out.println(s);
			transition(target, m, lcs.get(i));
		}
		ConcurrentModel newCm=new ConcurrentModel(states, updatePart);
		newCm.setInitialState(cm.getInitialState());
		return newCm;
	}

}
