/* Copyright or (C) or Copr. GET / ENST, Telecom-Paris, Ludovic Apvrille
 * 
 * ludovic.apvrille AT enst.fr
 * 
 * This software is a computer program whose purpose is to allow the
 * edition of TURTLE analysis, design and deployment diagrams, to
 * allow the generation of RT-LOTOS or Java code from this diagram,
 * and at last to allow the analysis of formal validation traces
 * obtained from external tools, e.g. RTL from LAAS-CNRS and CADP
 * from INRIA Rhone-Alpes.
 * 
 * This software is governed by the CeCILL  license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 * 
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 * 
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 * 
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
 */




package graph;

import java.awt.*;
import java.util.ArrayList;

/**
* Class AUTMappingGraph
* Creation : 05/03/2008
** @version 1.0 05/03/2008
* @author Ludovic APVRILLE
 */
public class AUTMappingGraph  extends AUTGraph {
	
	public AUTMappingGraph() {
	}
    
	public void renameTransitions() {
		String tmp, tmp1;
		int index, index1;
		
		for(AUTTransition aut1 : transitions) {
			tmp = aut1.transition;
			// we remove three "__" (name of HwNode, name of Design, name of task)
			index = tmp.indexOf("__");
			
			if (index != -1) {
				tmp = tmp.substring(index+2, tmp.length());
				index = tmp.indexOf("__");
				if (index != -1) {
					tmp = tmp.substring(index+2, tmp.length());
					tmp1 = tmp;
					index = tmp.indexOf("__");
					if (index != -1) {
						tmp = tmp.substring(index+2, tmp.length());
						if (tmp.startsWith("wr__")) {
							index = tmp.lastIndexOf("__");
							if ((index != -1) && (index > 3)) {
								aut1.transition = "wr" + tmp.substring(index, tmp.length());
							}
						}
						
						if (tmp.startsWith("rd__")) {
							index = tmp.lastIndexOf("__");
							if ((index != -1) && (index > 3)) {
								aut1.transition = "rd" + tmp.substring(index, tmp.length());
							}
						}
						
						if (tmp.startsWith("wait__")) {
							index = tmp.lastIndexOf("__");
							if ((index != -1) && (index > 3)) {
								aut1.transition = "wait" + tmp.substring(index, tmp.length());
							}
						}
						
						if (tmp.startsWith("notify__")) {
							index = tmp.lastIndexOf("__");
							if ((index != -1) && (index > 3)) {
								aut1.transition = "notify" + tmp.substring(index, tmp.length());
							}
						}
						
						if (tmp.startsWith("notified__")) {
							index = tmp.lastIndexOf("__");
							if ((index != -1) && (index > 3)) {
								aut1.transition = "notified" + tmp.substring(index, tmp.length());
							}
						}
						
						if (tmp.startsWith("sendReq__")) {
							index = tmp.lastIndexOf("__");
							if ((index != -1) && (index > 3)) {
								// Must add the name of the task
								tmp = tmp.substring(index, tmp.length());
								index1 = tmp1.indexOf("__");
								tmp1 = tmp1.substring(0, index1);
								if ((index = tmp.indexOf('<')) > -1) {
									tmp = tmp.substring(0, index) + "__" + tmp1 + tmp.substring(index, tmp.length());
								} else {
									tmp = tmp + "__" + tmp1;
								}
								aut1.transition = "sendReq" + tmp;
							}
						}
						
						if (tmp.startsWith("waitReq__")) {
							index = tmp.lastIndexOf("__");
							if ((index != -1) && (index > 3)) {
								aut1.transition = "waitReq" + tmp.substring(index, tmp.length());
							}
						}
					}
					
				}
			}
			
        }
	}
	
	public void mergeReadTransitions() {
		mergeTransitions("rd__");
	}
	
	public void mergeWriteTransitions() {
		mergeTransitions("wr__");
	}
	
	// Transitions to be removed are named "i"
	public void mergeTransitions(String keyword) {
		if (states == null) {
			computeStates();
		}
		
		reinitMet();
		
		// ExploringPaths, starting from state 0
		mergeTransitionsInPath(states.get(0), keyword);
	}
	
	public void mergeTransitionsInPath(AUTState state, String keyword) {
		//TraceManager.addDev("in state: " + state.id);
		AUTState state1;
		AUTTransition autnext;
		
		if (state.met) {
			return;
		}
		
		state.met = true;
		for(AUTTransition aut1: state.outTransitions) {
			state1 = states.get(aut1.destination);
			if((state1.getNbOutTransitions() == 1) && (state1.getNbInTransitions() ==1)) {
				autnext = state1.outTransitions.get(0);
				mergeTheTwoTransitions(aut1, autnext, keyword);
			}
			mergeTransitionsInPathIterative(state1, keyword);
		}
	}
	
	public void mergeTransitionsInPathIterative(AUTState state, String keyword) {
		ArrayList<AUTState> list = new ArrayList<AUTState>();
		list.add(state);
		AUTState mystate;
		AUTState state1;
		AUTTransition autnext;
		
		while (list.size()>0) {
			mystate = list.get(0);
			list.remove(0);
			mystate.met = true;
			for(AUTTransition aut1: mystate.outTransitions) {
				state1 = states.get(aut1.destination);
				if((state1.getNbOutTransitions() == 1) && (state1.getNbInTransitions() ==1)) {
					autnext = state1.outTransitions.get(0);
					mergeTheTwoTransitions(aut1, autnext, keyword);
				}
				if (!state1.met) {
					list.add(state1);
				}
			}
		}
	}
	
	public void mergeTheTwoTransitions(AUTTransition aut1, AUTTransition aut2, String keyword) {
		int index1 = aut1.transition.indexOf(keyword);
		if (index1 == -1) {
			return;
		}
		
		int index2 = aut2.transition.indexOf(keyword);
		if (index2 == -1) {
			return;
		}
		
		//TraceManager.addDev("Found two transitions to merge: " + aut1.transition + " and " + aut2.transition);
		
		// Must check that this is the same channel
		String tmp1 = aut1.transition.substring(index1 + keyword.length(), aut1.transition.length());
		index1 = tmp1.indexOf('<');
		if (index1 == -1) {
			return;
		}
		tmp1 = tmp1.substring(0, index1);
		
		String tmp2 = aut2.transition.substring(index2 + keyword.length(), aut2.transition.length());
		index2 = tmp2.indexOf('<');
		if (index2 == -1) {
			return;
		}
		tmp2 = tmp2.substring(0, index2);
		
		//TraceManager.addDev("The two channel names are:" + tmp1 + " and " + tmp2);
		
		if (!tmp1.equals(tmp2)) {
			return;
		}
		
		// So, we have the same channels
		// Get the values
		
		Point p1 = getValues(aut1.transition);
		if ((p1.x == -1) || (p1.y == -1)) {
			return;
		}
		
		Point p2 = getValues(aut2.transition);
		if ((p2.x == -1) || (p2.y == -1)) {
			return;
		}
		
		if (p2.y != p1.y) {
			return;
		}
		
		aut1.transition = "i";
		tmp2 = aut2.transition.substring(0, aut2.transition.indexOf('<'));
		tmp2 += "<" + (p1.x + p2.x) + ", " + p2.y + ">";
		aut2.transition = tmp2;
		
		//TraceManager.addDev("The second transition is now:" + aut2.transition);
		
	}
	
	public Point getValues(String values) {
		Point p = new Point();
		
		int index1 = values.indexOf('<');
		int index2 = values.indexOf(',');
		int index3 = values.indexOf('>');
		int value1 = -1;
		int value2 = -1;
		
		if ((index1 > -1) && (index2 > index1) && (index3 > index2)) {
			try {
				value1 = Integer.decode(values.substring(index1+1, index2).trim()).intValue();
				value2 = Integer.decode(values.substring(index2+1, index3).trim()).intValue();
			} catch (NumberFormatException nfe) {
				value1 = -1;
				value2 = -1;
			}
			
		}
		
		p.x = value1;
		p.y = value2;
		
		return p;
		
	}
	
	
	public void splitTransitions() {
		String tmp;
		int index1, index2;
		ArrayList<AUTTransition> tmptransitions = new ArrayList<AUTTransition>();
		
		for(AUTTransition aut1 : transitions) {
			tmp = aut1.transition;
			index1 = tmp.indexOf('<');
			if (index1 > -1) {
				index2 = tmp.indexOf('>');
				if (index2 > index1) {
					splitTransition(tmptransitions, aut1, index1, index2);
				}
			}
		}
		
		for(AUTTransition aut2 : tmptransitions) {
			transitions.add(aut2);
		}
	}
	
	public boolean splitTransition(ArrayList<AUTTransition> tmptransitions, AUTTransition transition, int index1, int index2) {
		Point p;
		int value;
		int destination = transition.destination;
		//String tmp = transition.transition.substring(index1+1, index2);
		String newTransition;
		
		if (!transition.transition.startsWith("wr__") && !transition.transition.startsWith("rd__"))  {
			return false;
		}
		
		p = getValues(transition.transition);
		
		//TraceManager.addDev("p.x=" + p.x +  " p.y=" + p.y);
		
		if ((p.x == -1) || (p.y == -1)) {
			return false;
		}
		
		if (p.x == p.y) {
			// Just need to transform the transition i.e. remove the <x, y>
			transition.transition = transition.transition.substring(0, index1);
			return true;
		}
		
		if (p.x < p.y) {
			// Nothing to be done...
			return true;
		}
	
		// And so, p.x > p.y
		newTransition = transition.transition.substring(0, index1);
		transition.transition = newTransition;
		
		//TraceManager.addDev("new Transition =" + newTransition);
		
		transition.destination = nbState;
		AUTTransition tr = null;
		
		value = (p.x / p.y) - 1;
		if ((p.x % p.y) != 0) {
			value ++;
		}
		
		//TraceManager.addDev("value" + value);
		
		for(int i=0; i<value; i++) {
			if ((i == (value-1)) && ((p.x % p.y) != 0)) {
				tr = new AUTTransition(nbState + i, newTransition + "<" + (p.x % p.y) + ", " + p.y + ">", nbState + i + 1);
			} else {
				tr = new AUTTransition(nbState + i, newTransition, nbState + i + 1);
			}
			tmptransitions.add(tr);
		}
		tr.destination = destination;
		nbState = nbState + value;
		nbTransition = nbTransition + value;
		
		return true;
	}
	
	public void removeInternalTransitions() {
		AUTTransition transition;
		ArrayList<AUTState> tmpStates = new ArrayList<AUTState>();
		AUTState deststate;
		
		if (states == null) {
			computeStates();
		}
		
		for(AUTState state: states) {
			if (state.getNbOutTransitions() == 1) {
				transition = state.outTransitions.get(0);
				deststate = states.get(transition.destination);
				if (transition.transition.equals("i")) {
					for(AUTTransition aut1: state.inTransitions) {
						aut1.destination = transition.destination;
						deststate.inTransitions.add(aut1);
					}
					transitions.remove(transition);
					tmpStates.add(state);
					nbTransition = nbTransition -1;
					nbState = nbState - 1;
				}
			}
		}
		
		for(AUTState state: tmpStates) {
			states.remove(state);
		}
		
		if (tmpStates.size() > 0) {
			updateStateIds();
		}
	}
	
	public void updateStateIds() {
		AUTState state;
		
		for(int i=0; i<states.size(); i++) {
			state = states.get(i);
			state.id = i;
			for(AUTTransition aut1: state.inTransitions) {
				aut1.destination = i;
			}
			for(AUTTransition aut1: state.outTransitions) {
				aut1.origin = i;
			}
		}
	}
	
	public void putFirstStateFirst() {
		
		computeStates();
		AUTState state = findFirstOriginState();
		if (state == null) {
			//TraceManager.addDev("no first state");
			return;
		}
		
		//TraceManager.addDev("First state = " + state.id);
		
		if ((state != null) && (state != states.get(0))){
			//TraceManager.addDev("First state = " + state.id);
			states.remove(state);
			states.add(0, state);
			updateStateIds();
			putTransitionsFromInitFirst();
		}
	}
	
    
}