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 TransitionParser {
	int trNum = 0; // debug
	List<ConcurrentModel> modelList;
	ModelInterface controller;
	boolean isController = false;
	List<String> transitionRecordsOfModel;
	List<String> transitionRecordsOfController;
	List<String> additionalTransitionSequences;
	List<String> controllableActions;
	String transitionSequence = "";
	String crlf = System.getProperty("line.separator");
	List<String> eliminatedLabel;
	int currentIndex;
	int MAX_LEVEL;

	public TransitionParser(List<ConcurrentModel> modelList) {
		this.modelList = modelList;
		this.transitionRecordsOfModel = new ArrayList<String>();
		MAX_LEVEL = modelList.size();
		currentIndex = 0;
	}

	public TransitionParser(List<ConcurrentModel> model,
			ModelInterface controller) {
		this.modelList = model;
		this.controller = controller;
	}

	public TransitionParser(ConcurrentModel model) {
		this.modelList = new ArrayList<ConcurrentModel>();
		this.modelList.add(model);
	}

	public void setController(ModelInterface c) {
		this.controller = c;
	}

	private void doAllTransition(State current, String record,
			List<State> history) {
		if (current.toString().equals("ERROR")) {
			//System.out.println(++trNum);// debug
			record += "ERROR";
			if (isController) {
				this.transitionRecordsOfController.add(record);
			} else {
				this.transitionRecordsOfModel.add(record);
			}
			return;
		} else if (history.contains(current)) {
			//System.out.println(++trNum);// debug
			record += "LOOP";
			// System.out.println(record);//debug
			if (isController) {
				// this.transitionRecordsOfController.add(record);
			} else {
				// this.transitionRecordsOfModel.add(record);
			}
			return;
		}
		history.add(current);
		int transitionVariation = current.getToTransitionNum();
		for (int i = 0; i < transitionVariation; i++) {
			Transition t = current.getToTransition(i);
			this.doAllTransition(t.getTo(), record + t.toString() + crlf,
					new ArrayList<State>(history));
		}
	}

	void setEliminatedLabel(List<String> eliminatedLabel) {
		this.eliminatedLabel = eliminatedLabel;
	}

	boolean checkModelSimulateController(List<String> eLabel) {
		setEliminatedLabel(eLabel);
		int rank = -1;
		for (int i = 0; i < modelList.size(); i++) {
			if (this.checkSimulate(this.transitionRecordsOfController,
					this.modelList.get(i)) == true) {
				rank = i;
			}
		}
		return rank == -1;
	}

	boolean checkErrorSequensesSimulateController(List<String> s) {
		List<String> ss = new ArrayList<String>();
		for (int i = 0; i < s.size(); i++) {
			String[] seq = s.get(i).split(crlf);
			// String[] b=this.additionalTransitionSequences.split(crlf);
			String b = this.additionalTransitionSequences
					.get(this.additionalTransitionSequences.size() - 1);
			int c = seq.length - 1;
			String a = seq[c];

			while (!a.equals(b)) {
				//System.out.println(a);// debug
				a = seq[--c];
			}

			String sWithoutError = "";
			for (int j = 0; j < c; j++) {
				sWithoutError += seq[j] + crlf;
			}
			//System.out.println(sWithoutError);// debug
			ss.add(sWithoutError);
		}
		setEliminatedLabel(new ArrayList<String>());
		return this.checkSimulate(ss, controller);
	}

	boolean checkSimulate(List<String> transitionSequenses, ModelInterface model) {
		boolean isSimulateWithNoError = true;
		State current = model.getInitialState();
		for (int i = 0; i < transitionSequenses.size(); i++) {// 全遷移列を対象にループを回す
			String[] tr = transitionSequenses.get(i).split(crlf);
			for (int j = 0; j < tr.length; j++) {
				if (!(tr[j].equals("ERROR")) && !(tr[j].equals("LOOP"))) {// 遷移列内の全遷移を順番にチェック
					if (current.containsToTransition(tr[j])) {
						//System.out.println("Choise of actions (controller's one is:+ tr[j] + ")");
						for (int k = 0; k < current.getToTransitionNum(); k++) {// 遷移によって辿り着いた状態からERRORに行く可能性をチェック
							if (current.getToTransition(k).getTo().toString()
									.equals("ERROR")) {
								System.out
										.println("==============Caution!! This is ERROR state==============");
								isSimulateWithNoError = false;
							}
						}
						current = current.getToStateByTransition(tr[j]);

					} else if (eliminatedLabel.contains(tr[j].toString())) {
					} else {
						return false;
					}
				}
			}
		}
		return isSimulateWithNoError;
	}

	void startAllTransitionOfModel(int modelLevel) {
		this.transitionRecordsOfModel = new ArrayList<String>();
		this.isController = false;
		doAllTransition(modelList.get(modelLevel).getInitialState(), "",
				new ArrayList<State>());
	}

	void startAllTransitionOfController() {
		this.transitionRecordsOfController = new ArrayList<String>();
		this.isController = true;
		doAllTransition(controller.getInitialState(), "",
				new ArrayList<State>());
	}

	List<String> getTransitionRecords(int level) {
		this.startAllTransitionOfModel(level);
		return this.transitionRecordsOfModel;
	}

	String toAdditionalTransitionSequences() {
		String a = "";
		for (int i = 0; i < this.additionalTransitionSequences.size(); i++) {
			a += this.additionalTransitionSequences.get(i) + this.crlf;
		}
		return a.trim();
	}// */

	void setAdditionaltransitionSequences(List<String> ts) {
		this.additionalTransitionSequences = ts;
	}

	boolean checkContainingSequences(String target) {
		return target.contains(this.toAdditionalTransitionSequences());
	}// */

	String getTransitionSeqence() {
		return transitionSequence;
	}

	void setModelList(List<ConcurrentModel> modelList) {
		this.modelList = modelList;
	}

	void setControllableActions(List<String> c) {
		this.controllableActions = c;
	}

	void pasteController(List<String> l) {
		//System.out.println("pasteController");
		this.setEliminatedLabel(l);
		for (ModelInterface model : modelList) {
			pasteCToState(controller.getInitialState(), model.getInitialState());
		}
	}

	void pasteController() {
		this.setEliminatedLabel(new ArrayList<String>());
		// pasteCToState(controller.getInitState(),model.getInitState());
		for (ModelInterface model : modelList) {
			eraseContoller();
			pasteCToStateWithHash(controller.getInitialState(),
					model.getInitialState());
		}
	}

	void pasteCToState(State c, State m) {
		if (c.isController() || m.toString().equals("ERROR")) {
			return;
		}
		c.setIsController();
		m.setIsController();
		for (int i = 0; i < c.getToTransitionNum(); i++) {
			if (eliminatedLabel.contains(c.getToTransition(i).toString())) {
				pasteCToState(c.getToTransition(i).getTo(), m);
			} else {
				if (!c.getToTransition(i).isController()) {
					c.getToTransition(i).setIsController();
					m.getToTransition(c.getToTransition(i).toString())
							.setIsController();
					if (!c.getToTransition(i)
							.toString()
							.equals(m.getToTransition(
									c.getToTransition(i).toString()).toString()))
						System.out.println("ERROR");
					pasteCToState(c.getToTransition(i).getTo(), m
							.getToTransition(c.getToTransition(i).toString())
							.getTo());
				}
				// pasteCToTransition(c.getToTransition(i),m.getToTransition(c.getToTransition(i).toString()));
			}
		}
	}

	void pasteCToStateWithHash(State c, State m) {
		HashMap<State, ContSet> map = new HashMap<State, ContSet>();
		ContSet cu = new ContSet(c, m), next;
		cu.getController().setIsController();
		cu.getModel().setIsController();
		int i = 0;
		while (cu.getController().hasNext()) {
			i++;
			Transition t = (Transition) cu.getController().next();
			if (cu.getModel().getToTransition(t.toString()) == null) {
//				System.out.println("Caution!:" + t + " in " + cu.getModel());
//				for (int j = 0; j < cu.getModel().getToTransitionNum(); j++) {
//					System.out.println("  " + cu.getModel().getToTransition(j));
//				}
				cu.getModel().setIsController();
			} else {
				if (cu.getController().hasNext() && !map.containsValue(cu)) {
					map.put(cu.getController(), cu);
				}
				next = new ContSet(t.getTo(), cu.getModel()
						.getToStateByTransition(t.toString()));
				next.getController().setIsController();
				next.getModel().setIsController();
				if (!next.getController().hasNext() && !map.isEmpty()) {
					next = map.remove(map.keySet().toArray()[0]);
				} else if (map.containsKey(next.getController())) {
					map.remove(next.getController());
				}
				cu = next;
			}
		}
	}

	class ContSet {
		State controller, model;

		ContSet(State c, State m) {
			this.controller = c;
			this.model = m;
		}

		State getController() {
			return this.controller;
		}

		State getModel() {
			return this.model;
		}
	}

	void pasteCToTransition(Transition c, Transition m) {
		if (c.isController()) {
			return;
		}
		c.setIsController();
		m.setIsController();
		if (!c.toString().equals(m.toString()))
			System.out.println("ERROR");
		pasteCToState(c.getTo(), m.getTo());
	}

	void eraseContoller() {
		Model m = (Model) this.controller;
		for (int i = 0; i < m.getSize(); i++) {
			State s = m.getState(i);
			s.eraseIsController();
			s.reset();
			for (int j = 0; j < s.getFromTransitionNum(); j++) {
				s.getFromTransition(j).eraseIsController();
			}
			for (int j = 0; j < s.getToTransitionNum(); j++) {
				s.getToTransition(j).eraseIsController();
			}
		}

	}

	void eraseDead() {
		for (ModelInterface model : modelList) {
			State er = model.getErrorState();
			er.eraseIsDead();
			for (int i = 0; i < er.getFromTransitionNum(); i++) {
				eraseDFromTransition(er.getFromTransition(i));
			}
		}
	}

	private void eraseDFromTransition(Transition t) {
		if (!t.isDead())
			return;
		t.eraseIsDead();
		this.eraseDFromState(t.getFrom());
	}

	private void eraseDFromState(State s) {
		if (!s.isDead())
			return;
		s.eraseIsDead();
		for (int i = 0; i < s.getFromTransitionNum(); i++) {
			if (s.getFromTransition(i).isDead())
				eraseDFromTransition(s.getFromTransition(i));
		}
	}

	void pasteDead() {
		for (ModelInterface model : modelList) {
			State er = model.getErrorState();
			er.setIsDead();
			for (int i = 0; i < er.getFromTransitionNum(); i++) {
				// System.out.println("here is "+i+" times");
				pasteDToTransition(er.getFromTransition(i));
			}
		}
		//System.out.println("pasteDeadEnd");// debug
	}

	void pasteDeadFromConcurrentSystem() {
		for (ModelInterface model : modelList) {
			State er = model.getErrorState();
			//System.out.println("ERROR:" + er + level);
			er.setIsDead();
			for (int i = 0; i < er.getFromTransitionNum(); i++) {
				//System.out.println("here is "+i+" times");
				pasteDToTransition(er.getFromTransition(i));
			}
		}
		//System.out.println("pasteDeadEnd");
	}

	void pasteUpdatedDead() {
		int j=MAX_LEVEL;
		for (ModelInterface model : modelList) {
			List<Transition> l = model.getUpdatedPart();
			//System.out.println("update pasteDeadEnd for model"+j--);
			for (int i = 0; i < l.size(); i++) {
				if (l.get(i).getTo().isDead()) {
				//	System.out.println("  pasteDead "+l.get(i)+"->"+l.get(i).getTo());
					pasteDToTransition(l.get(i));
				}
			}
		}
	}

	void pasteDToTransition(Transition m) {
		if (m.isDead())
			return;
		// System.out.println("This is dead transition:["+m+"]");//debug
		m.setIsDead();
		this.pasteDToState(m.getFrom());
	}

	void pasteDToState(State m) {
		if (m.isDead())
			return;
		boolean dead = true;
		//System.out.println("state "+m);
		for (int i = 0; i < m.getToTransitionNum(); i++) {
			Transition t = m.getToTransition(i);
			//System.out.println("  Transition:"+t+" isControllable:"+t.isControllable()+" isDead:"+t.isDead());
			if (!t.isControllable() && t.isDead()) {
				m.setIsDead();
				//System.out.println("Dead state:["+m+"]");//debug
				for (int j = 0; j < m.getFromTransitionNum(); j++) {
					pasteDToTransition(m.getFromTransition(j));
				}
				return;
			} else if (!t.isDead()) {
				dead = false;
			}
		}

		if (dead)
			m.setIsDead();
		//System.out.println(" isDead:"+m.isDead());
		if (m.isDead()) {
			for (int i = 0; i < m.getFromTransitionNum(); i++) {
				pasteDToTransition(m.getFromTransition(i));
			}
		}
	}

	boolean checkCanSimulate(ModelInterface rs, int level) {
		boolean isDead = false;
		int count=0;
		for (int i = 0; i < rs.getSize(); i++) {
			State s = rs.getState(i);
			//if(s.isController()) 
				//System.out.println((++count)+"state:"+s+",isDead:"+s.isDead()+" isController:"+s.isController());				
			if (s.isController() && s.isDead()) {
				isDead = true;
				//System.out.println("level "+level+" controller cannot be generated:" + s);
				return isDead;
			}
		}
		if (!isDead) {
			//System.out.println("controller can be generated");
			return isDead;
		}
		return isDead;
	}

	public int checkSimulate(ModelInterface controller) {
		this.setController(controller);
		return checkSimulate();
	}

	public int checkSimulate() {
		pasteDeadFromConcurrentSystem();
		pasteController();
		for (int i = currentIndex; i < MAX_LEVEL; i++) {
			if (!checkCanSimulate(this.modelList.get(i),MAX_LEVEL-i)) {
				currentIndex = i;
				//System.out.println("first checkSimulate MAX_LEVEL"+MAX_LEVEL+","+(MAX_LEVEL-i));
				break;
			}
		}
		return MAX_LEVEL - currentIndex;
	}

	public int checkUpdatedSimulate() {
		pasteUpdatedDead();
		pasteController();
		for (int i = currentIndex; i < MAX_LEVEL; i++) {
			currentIndex = i;
			if (!checkCanSimulate(this.modelList.get(i),(MAX_LEVEL-i))) {
				//System.out.println("checkUpdatedSimulateEnd:"+(MAX_LEVEL-i));
				break;
			}
		}
		return MAX_LEVEL - currentIndex;
	}

	public void checkSimulateOld(ConcurrentModel rs) {
		eraseDead();
		eraseContoller();
		pasteDead();
		pasteController();
		checkCanSimulate(rs,-1);
	}

}