import java.applet.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
import java.io.*;
import java.net.*;


/*
    Machine class - implements state machine abstraction - a collection of
    state objects and operations on them.
*/


class Machine implements StateIdResolver
{
    /*
        CLASS CONSTANTS
    */


    /*
        return values from some calls
    */


    /* normal status */

    final static int    STATUS_NORMAL            = 1;

    /* cannot start simulation - no input string */

    final static int    STATUS_NO_INPUT          = 2;

    /* cannot start simulation - no initial state */

    final static int    STATUS_NO_INITIAL        = 3;

    /* cannot start simulation - no accepting state */

    final static int    STATUS_NO_ACCEPTING      = 4;

    /* string was accepted by the machine */

    final static int    STATUS_ACCEPTED          = 5;

    /* string was NOT accepted by the machine */

    final static int    STATUS_NOT_ACCEPTED      = 6;

    /* bad token was encountered */

    final static int    STATUS_BAD_TOKEN         = 7;

    /* bad grammar was encountered */

    final static int    STATUS_BAD_GRAMMAR  = 8;

    /* file specified for loading was not found */

    final static int    STATUS_FILE_NOT_FOUND    = 9;

    /* file specified for saving could not be created */

    final static int    STATUS_FILE_CREATE_ERROR = 10;

    /* file read error */

    final static int    STATUS_READ_ERROR        = 11;

    /* file write error */

    final static int    STATUS_WRITE_ERROR       = 12;

    /* input file improperly constructed error */

    final static int    STATUS_BAD_INPUT_FILE    = 13;

    final static int	STATUS_MACHINE_WAITING   = 14;


    /* maximum number of states */

    final static int    STATES = 10;

    /*
        code characters used to mark the file as created by this program
    */

    final static int    CODE1 = (int) 'F';
    final static int    CODE2 = (int) 'S';
    final static int    CODE3 = (int) 'M';
    final static int    CODE4 = (int) 'S';

    /*
        codes characters used to delimit fields in the files
    */

    /* beginning of input string data */

    final static int    BOI = 0x7D;

    /* beginning of state data */

    final static int    BOS = 0x7E;

    /* end of machine data (end of file) */

    final static int    EOM = 0x7F;


    /*
        INSTANCE VARIABLES
    */


    /* array of state objects */

    private State       states [];

    /* number of states currently present */

    private int         numStates;

    /* state we are currently removing */

    private State       removing;

    /* initial state */

    private State       initialState;

    /* first state of a new transition*/

    private State       firstState;

    /* last state of a new transition */

    private State       lastState;

    /* state being dragged */

    private State       dragState;

    /* current state during simulation */

    private State       currState;

    /* number of accepting states */

    private int         acceptingStates;

    /* current input string object */

    //I private InputString currInputString;

    /* new input string */

    //I private InputString		newInputString;

    private InputSignal		inputSignal;

    private InputActivation	inputActivation;


    /*
        PUBLIC METHODS
    */


    /*
        constructor method
    */

    Machine (InputSignal inp)
    {
        /* initialize instance variables */

        inputSignal = inp;

        removing = initialState = currState = firstState = lastState =
        dragState = (State) null;
        //I currInputString = newInputString  = (InputString) null;

        acceptingStates = 0;
        numStates       = 0;

        states = new State [STATES];

        for (int i = 0; i < STATES; i ++)
            states [i] = (State) null;

    } /* end Machine */


    /*
        this routine implements StateIdResolver interface. returns the state
        corresponding to the specified id.
    */

    public State resolveId (int id)
    {
        if (id < 0 || id >= STATES)
            return (State) null;

        return states [id];

    } /* end resolveId */


    /*
        display state machine on the specified graphics context.
    */

    public synchronized void paint (Graphics g, int offset)
    {
        /* if a state was marked for removal - call its remove method to delete
           its image */

        if (removing != (State) null)
        {
            removing.remove (g);
            removing = (State) null;
        }

        /* if new input string was specified - call the current string's remove
           method to delete its image and then set the current string to the
           new instance. */

        //I if (newInputString != (InputString) null)
        //I {
        //I     currInputString.remove (g, offset);
        //I     currInputString = newInputString;
        //I     newInputString = (InputString) null;
        //I }

        /* paint all existing states on the screen. notice that we traverse
           the array in the reverse order, so that if states overlap on the
           screen, the ones with the lowest ids will end up on top and will
           correspond to the ones that get selected when user clicks on them */

        for (int i = STATES - 1; i >= 0; i --)
            if (states [i] != (State) null)
                states [i].paint (g);

        /* if input string was set - draw it */

        //I if (currInputString != (InputString) null)
        //I     currInputString.paint (g, offset);

    } /* end paint */


	public void setInputActivation(InputActivation inpac)
	{
		inputActivation = inpac;
	}


    /*
        add a new state at specified coordinates.
    */

    public boolean addState (int x, int y)
    {
        /* if maximum number of states already exists - exit */

        if (numStates == STATES)
            return false;

        numStates ++;

        int i;

        /* look for the first empty slot in the states array */

        for (i = 0; i < STATES; i ++)
            if (states [i] == (State) null)
                break;

        /* create new state */

        states [i] = new State (i, x, y, inputSignal);

        return true;

    } /* end addState */


    /*
        remove state at specified coordinates. if more than one state exist
        at that location, the one with the lowest id will be removed.
    */

    public boolean removeState (int x, int y)
    {
        /* make sure we have something to remove */

        if (numStates == 0)
            return false;

        numStates --;

        int i;

        /* find the first state that acknowledges that the point lays inside
           of its space */

        for (i = 0; i < STATES; i ++)
            if (states [i] != (State) null && states [i].inside (x, y))
                break;

        if (i == STATES)
            return false;

        /* we cannot completely get rid of it yet, as we will have to remove
           the states drawings. this will be done at the next call to paint,
           so just set the removing to states object. */

        removing = states [i];

        /* strip the state from its initial and accepting parameters and
           invalidate it. invalidation is needed to remove the transitions
           whose destination this state is. state knows only of transitions
           starting with it, but it cannot invalidate the ones ending at it.
           transitions will check if their traget state is valid during update
           calls and invalidate themselves if necessary. */

        makeNormal (removing);
        removing.makeInvalid ();

        states [i] = (State) null;

        return true;

    } /* end removeState */


    /*
        mark the state at specified coordinates as initial. if more than one state
        exist at that location, the one with the lowest id will be marked.
    */

    public boolean makeStateInitial (int x, int y)
    {
        /* cannot have more than one initial state */

        if (initialState != (State) null)
            return false;

        initialState = locateState (x, y);

        if (initialState == (State) null)
            return false;

        initialState.makeInitial ();

        return true;

    } /* end makeStateInitial */


    /*
        mark the state at specified coordinates as accepting. if more than one
        state exist at that location, the one with the lowest id will be marked.
    */

    public boolean makeStateAccepting (int x, int y)
    {
        State state = locateState (x, y);

        if (state == (State) null)
            return false;

        state.makeAccepting ();
        acceptingStates ++;

        return true;

    } /* end makeStateAccepting */


    /*
        mark the state at specified coordinates as normal. if more than one state
        exist at that location, the one with the lowest id will be marked.
    */

    public boolean makeStateNormal (int x, int y)
    {
        State state = locateState (x, y);

        if (state == (State) null)
            return false;

        makeNormal (state);

        return true;

    } /* end makeStateNormal */


    /*
        select the first state for adding the transition. if more than one state
        exist at that location, the one with the lowest id will be marked.
    */


    public State selectFirstState (int x, int y)
    // CLK
    // Returns the state that was clicked onto, so that
    // it might be sent a highlightOff(), if the user
    // doesn't select a second state.
    {
        firstState = locateState (x, y);

        if (firstState == (State) null)
            return (State) null;

        /* highlight the state and update the hint line */

        firstState.highlightOn ();

        return firstState;

    } /* end selectFirstState */


    /*
        select the last state for adding the transition. if more than one state
        exist at that location, the one with the lowest id will be marked.
    */


    public boolean selectLastState (int x, int y)
    {
        lastState = locateState (x, y);

        if (lastState == (State) null)
            return false;

        if (firstState != (State) null)
            firstState.highlightOff ();

        return true;

    } /* end selectLastState */


    /*
        add transition between the first and the last states.
    */

    public void addTransition (InputSignal tokens)
    {
        if (firstState != (State) null && lastState != (State) null)
        {
            firstState.addTransition (lastState, tokens);
            firstState = lastState = (State) null;
        }

    } /* end addTransition */


    /*
        remove transition between the first and the last states.
    */

    public void removeTransition ()
    {
        if (firstState != (State) null && lastState != (State) null)
        {
            firstState.removeTransition (lastState);
            firstState = lastState = (State) null;
        }

    } /* end removeTransition */

	public void renameState(String newName)
	{
		System.out.println("renameState bekam " + newName);
		if (firstState != (State) null)
		{
			firstState.setLabel(newName);
		} else System.out.println("machine.renameState: firstState ist null!");

	} /* end renameState */


    /*
        select the state for dragging. if more than one state have these
        coordinates, the one with the lowest id will be selected.
    */

    public boolean selectDragState (int x, int y)
    {
        dragState = locateState (x, y);

        if (dragState == (State) null)
            return false;

        dragState.dragStart (x, y);

        return true;

    } /* end selectDragState */


    /*
        stop dragging the current state.
    */

    public void deselectDragState (int x, int y)
    {
        if (dragState != (State) null)
            dragState.dragStop (x, y);

        dragState = (State) null;

    } /* deselectDragState */


    /*
        drag the current drag state.
    */

    public void dragState (int x, int y)
    {
        if (dragState != (State) null)
            dragState.drag (x, y);

    } /* end dragState */


    /*
        set the input string of tokens from the specified character string.
    */

    //I public void setInputString (String s)
    //I {
        /* if one already exists cannot replace it now as it will have to be
           removed from the screen in paint() call. just set the newInputString,
           current one will be set to that value in paint() */

    //I     if (currInputString != (InputString) null)
    //I     {
    //I         newInputString = new InputString (s);
    //I         return;
    //I     }

    //I     currInputString = new InputString (s);

    //I } /* end setInputString */


    /*
        make sure simulation can be started (necessary conditions exist), stop
        the previous one if necessary.
    */

    public int startSimulation ()
    {
        /* make sure we have input string, initial state and at least one
           accepting state */

		// not needed the way we see things - CLK.
        //I  if (currInputString == (InputString) null)
        //I      return STATUS_NO_INPUT;

        if (initialState == (State) null)
            return STATUS_NO_INITIAL;

		// not needed the way we see things - CLK.
        // if (acceptingStates <= 0)
        //     return STATUS_NO_ACCEPTING;

        /* stop previous simulation */

        stopSimulation ();

        return STATUS_NORMAL;

    } /* end startSimulation */


    /*
        stop simulation in progress.
    */

    public void stopSimulation ()
    {
        /* rewind token string */

        //I currInputString.rewind ();

        /* if some state holds token - make it give up the token and turn off
           the highlight */

        if (currState != (State) null)
        {
            currState.giveToken ();
            currState.highlightOff ();
            currState = (State) null;
        }

    } /* end stopSimulation */


    /*
        run one round of simulation.
    */


    public int runSimulation ()
    {
        /* if no state is marked as current - make the initial state current */

        if (currState == (State) null)
            currState = initialState;

        /* else make the current state give up the token, and by doing so it will
           tell us what the next state is. */

        else
        {
            currState = currState.giveToken ();
        }

        currState.activeOn();

		InputSignal tokens = new InputSignal(Simulator.ESTIMATED_INPUTS, 1);

        /* Go through the checkboxes of inputActivation and extract their
           values into the array. */

       	for (int i = 0; i < inputActivation.checkPanel.countComponents(); i++)
        {
            Checkbox cb = (Checkbox) inputActivation.checkPanel.getComponent (i);

			if (cb.getState())
				tokens.addElement(cb.getLabel());
	    }

        /* if current state is accepting - the machine accepted the string.
           highlight the state to signify success. */

        //if (currState.isAccepting ())
        //{
        //    currState.highlightOn ();
        //    return STATUS_ACCEPTED;
        //}

        /* Give the activated inputs to current state. If it refuses to take one there
           is a problem with the grammar - either there was zero or more than
           one transitions that fired for this token. */

        switch (currState.takeTokens (tokens))
        {
        	case State.SEVERAL_ACCEPTED:
        		return STATUS_BAD_GRAMMAR;
        	case State.NO_ONE_ACCEPTED:
        		if (currState.isAccepting())
        			return STATUS_ACCEPTED;
        		else
	        		return STATUS_MACHINE_WAITING;
        }

        if (currState.isAccepting())
        	return STATUS_ACCEPTED;
        else
        	return STATUS_NORMAL;

    } /* end runSimulation */


    /*
        load new state machine from the specified file.
    */

    public synchronized int loadFile (String fileName)
    {
        FileInputStream file;

        /* open the file, catching the file not found exception */

        try
        {
            file = new FileInputStream (fileName);
        }
        catch (FileNotFoundException e)
        {
            return STATUS_FILE_NOT_FOUND;
        }

        /* read from the file catching the I/O error exception */

        try
        {
            /* make sure the file was created by this program */

            if (file.read () != CODE1 || file.read () != CODE2 ||
                file.read () != CODE3 || file.read () != CODE4)
            {
                return STATUS_BAD_INPUT_FILE;
            }

            int val = 0;

            /* while delimiter is not End Of Machine - keep reading */

            while (val != EOM)
            {
                /* read the next delimiter */

                val = file.read ();

                /* if the delimiter marks Beginning Of State information */

                if (val == BOS)
                {
                    /* read the byte encoding state's id */

                    val = file.read ();

                    if (val < 0 || val >= STATES)
                        return STATUS_BAD_INPUT_FILE;

                    /* create new state with this id. call the constructor
                       that will initialize the state with the information
                       from the file. */

                    states [val] = new State (val, file);
                }

                /* if the delimiter marks Beginning Of Input string */

                else if (val == BOI)
                {
                    /* create new input string and call the constructor that
                       will initialize the string with the information from the
                       file. */

                    //I currInputString = new InputString (file);
                }

                /* any delimiter other than EOM means garbage */

                else if (val != EOM)
                    return STATUS_BAD_INPUT_FILE;
            }

            /* resolve state IDs in transitions. transitions got initialized
               with state IDs, not objects since some of them were read from the
               file before the corresponding states. this step will resovle
               that. */

            acceptingStates = 0;
            numStates       = 0;

            for (int i = 0; i < STATES; i ++)
            {
                if (states [i] != (State) null)
                {
                    numStates ++;

                    /* initialize the initialState and acceptingStates variables
                        according to state information */

                    if (states [i].isInitial ())
                        initialState = states [i];

                    if (states [i].isAccepting ())
                        acceptingStates ++;

                    states [i].resolveId ((StateIdResolver) this);
                }
            }
        }
        catch (IOException e)
        {
            return STATUS_READ_ERROR;
        }

        return STATUS_NORMAL;

    } /* end loadFile */


    /*
        save current state machine data into specified file.
    */

    public int saveFile (String fileName)
    {
        FileOutputStream file;

        /* open the output file catching the I/O error exception. */

        try
        {
            file = new FileOutputStream (fileName);
        }
        catch (IOException e)
        {
            return STATUS_FILE_CREATE_ERROR;
        }

        /* write to file catching the I/O error exception. */

        try
        {
            /* write out file signature */

            file.write (CODE1);
            file.write (CODE2);
            file.write (CODE3);
            file.write (CODE4);

            /* write out state information */

            for (int i = 0; i < STATES; i ++)
            {
                if (states [i] == (State) null)
                    continue;

                /* write Beginning Of State delimiter and state id */

                file.write (BOS);
                file.write (i);

                /* make state write its own information, throw I/O exception
                   if the operation failed */

                if (! states [i].saveFile (file))
                    throw new IOException ();
            }

            /* write out input string */

            //I if (currInputString != (InputString) null)
            //I {
            //I     /* write Beginning Of State delimiter */

            //I     file.write (BOI);

                /* make input string write its own information, throw I/O
                   exception if the operation failed */

            //I     if (! currInputString.saveFile (file))
            //I         throw new IOException ();
            //I }

            /* write End Of Machine delimiter */

            file.write (EOM);
        }
        catch (IOException e)
        {
            return STATUS_WRITE_ERROR;
        }

        return STATUS_NORMAL;

    } /* end saveFile */


	public void renameToken(String oldName, String newName)
	{
		for (int i = 0; i < numStates; i++)
			states[i].renameToken(oldName, newName);
	}

	public void removeToken(String toBeRemoved)
	{
		for (int i = 0; i < numStates; i++)
			states[i].removeToken(toBeRemoved);
	}

	public void createVHDL()
	{
		boolean allEncompassing = false;

		System.out.print("architecture [your_architecture] of [your_design] is \n\n");
		System.out.print("...\n\n");

		System.out.print("type state_type is (");
		for (int i = 0; i < numStates; i++)
		{
			System.out.print(states[i].getLabel());
			if (i != numStates - 1)
				System.out.print(", ");
		}
		System.out.print(");\n");
		System.out.print("signal current_state, next_state: state_type;\n\n");

		System.out.print("begin  -- [your_architecture]\n\n");

		System.out.print("    CONTROL: process(current_state, ");
		for (int i = 0; i < numStates; i++)
		{
			System.out.print(states[i].getLabel());
			if (i != numStates - 1)
				System.out.print(", ");
		}
		System.out.print(")\n");
		System.out.print("    begin\n");

		System.out.print("        case current_state is\n");
		for (int i = 0; i < numStates; i++)
		{
			System.out.print("            when ");
			System.out.print(states[i].getLabel());
			System.out.print(" => ");

			Vector transitions = states[i].getTransitions();
			int number = transitions.size();

			if (number <= 0)
			{
				System.out.print("next_state <= " + states[i].getLabel());
				System.out.print(";\n\n");
			}
			else
			{
				for (int j = 0; j < number; j++)
				{
					Transition trns    = (Transition) transitions.elementAt(j);
					InputSignal tokens = trns.getTokens();
					State ziel         = trns.destination();

					if (tokens.contains((Object) "All"))
					{
						allEncompassing = true;
						System.out.print("next_state <= ");
						System.out.print(ziel.getLabel());
						System.out.print(";\n");
					}
					else
					{
						if (j == 0)
							System.out.print("\n                if (");
						else
							System.out.print("                elsif (");
						int k = 0;
						while (k < tokens.size())
						{
							if (k >= 1)
								System.out.print("\n                OR ");
							System.out.print(tokens.elementAt(k) + " = '1'");
							k++;
						}
						System.out.print(") then\n");
						System.out.print("                    next_state <= ");
						System.out.print(ziel.getLabel());
						System.out.print(";\n");
					}
				}
				if (! allEncompassing)
				{
					System.out.print("                else\n");
					System.out.print("                    next_state <= " + states[i].getLabel());
					System.out.print(";\n");
					System.out.print("                end if;\n\n");
				}
				else
					allEncompassing = false;
			}
		}

		// VHDL-example:
		//	when eins => if (input_1 = '1') then
		//				     next_state <= zwei;
		//				 elsif (...) then
		//				     next_state <= drei;
		//				 else
		//			    	 next_state <= eins;
		//				 end if;

		System.out.print("        end case;\n");
		System.out.print("    end process;\n\n");

		System.out.print("    SYNCH: process(clk, reset)\n");
		System.out.print("    begin\n");
		System.out.print("        if (reset = '0') then  -- low-active\n");
		System.out.print("            current_state <= ");
		if (initialState != (State) null)
			System.out.print(initialState.getLabel() + "\n");
		System.out.print("        elsif (clk'event and clk='1')  -- rising flank\n");
		System.out.print("            current_state <= next_state;\n");
		System.out.print("        end if;\n");
		System.out.print("    end process;\n\n");

		System.out.print("end [your_architecture];\n");
	}


    /*
        PRIVATE METHODS
    */


    /*
        return state object at the specified location. if more that one state
        are located in the same space, return the one with the lowest id.
    */

    private State locateState (int x, int y)
    {
        for (int i = 0; i < STATES; i ++)
            if (states [i] != (State) null && states [i].inside (x, y))

                return states [i];

        return (State) null;

    } /* end locateState */


    /*
        remove accepting and initial attributes from the state. update our
        counters as necessary.
    */

    private void makeNormal (State state)
    {
        if (state.isAccepting ())
            acceptingStates --;

        if (state.isInitial ())
            initialState = (State) null;

        state.makeNormal ();

    } /* end makeNormal */

} /* end Machine */

