/*
    Finite State Machine Simulator

    Author:					Kyril Faenov
    Schänder & Veränderer:	Carsten Kelling
    Version:				1.2.4
    Date:					18.05.96

    Notes:

    Todo:

    - make sure while dialog are up menu items cannot be selected
    - add audio hints in addition to hint line
    - do better recovery if file save/load fails
*/


/*
    IMPORTS
*/


import java.applet.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
import java.io.*;
import java.net.*;


/*
    INTERFACES
*/


/*
    StateIdResolver interface - classes complying to this interface must
    implement resolveId routine, which finds state object based on specified
    id.
*/


interface StateIdResolver
{
    /*
        return state object with specified id.
    */

    State resolveId (int id);

} /* end StateIdResolver */


/*
    PUBLIC CLASSES
*/


/*
    Simulator class - this is the top-level applet class. It does all of
    the initialization, event processing, re-paint initiation and dialog
    window management.
*/


public class Simulator extends Applet
{
    /*
        CLASS CONSTANTS
    */


    /*
        constants secifying what to do on the next mouse click
    */

    /* regular state */

    final static int    CLICK_NORMAL                  = 0;

    /* adding new state */

    final static int    CLICK_STATE_ADD               = 1;

    /* deleting state */

    final static int    CLICK_STATE_REMOVE            = 2;

    /* marking state as initial */

    final static int    CLICK_STATE_INITIAL           = 3;

    /* marking state as accepting */

    final static int    CLICK_STATE_ACCEPTING         = 4;

    /* marking state as normal */

    final static int    CLICK_STATE_NORMAL            = 5;

    /* selecting first state for new transition */

    final static int    CLICK_TRANSITION_FIRST_STATE  = 6;

    /* selecting last state for new transition */

    final static int    CLICK_TRANSITION_LAST_STATE   = 7;

    /* running simulation */

    final static int    CLICK_RUN_SIMULATION          = 8;

    /* selecting first state of the transition to be removed */

    final static int    CLICK_TRANSITION_REMOVE_FIRST = 9;

    /* selecting last state of the transition to be removed */

    final static int    CLICK_TRANSITION_REMOVE_LAST  = 10;

    /* CLK rename a state */

    final static int    CLICK_RENAME_STATE            = 11;

    /* applet's dimensions */

    final static int    DIMENSION   = 500;

    /* height of the button control area */

    final static int    CONTROL_HEIGHT = 75;

    /* Default size of instances of InputSignal */

    final static int	ESTIMATED_INPUTS = 20;


    /*
        INSTANCE VARIABLES
    */


    /* state machine */

    private Machine     machine;

    /* hint line support module */

    private Help        help;

    /* specifies what to do on the next mouse click (see the constants above) */

    private int         clickState;

    /* if set - clear the application panel on the next re-draw */

    private boolean     clearAll;


    /* if set - draggin of a state is in progress */

    private boolean     dragging;

	public State		ersterZustand; // CLK


    /*
        sub-dialog classes
    */

    private QuitDialog			quitDialog;
    private AboutBox			aboutBox;
    private TokenSelection		tokenSelection;
    private InputActivation		inputActivation;  // CLK
    private FileInput			fileInput;
    private NameInput			stateNameInput;   // CLK
    private NameInput			inputNameInput;   // CLK
    private InputMaintenance	inputMaintenance; // CLK
    private InputSignal         inputSignal;      // CLK
    private PatienceBox			patienceBox;      // CLK
    private Panel				buttons;


    /*
        PUBLIC METHODS
    */


    /*
        called when applet is being initialized
    */

    public void init ()
    {
        /* initialize instance variables */

        clickState = 0;
        clearAll   = true;
        dragging   = false;
        System.out.println("Still alive (1)!");

        inputSignal = new InputSignal(ESTIMATED_INPUTS,1);

        inputSignal.addElement((String) "m_obf");
        inputSignal.addElement((String) "ibf");
        inputSignal.addElement((String) "m_reg_ack");
        inputSignal.addElement((String) "m_halt_ack");
        inputSignal.addElement((String) "disp_d7");
        inputSignal.addElement((String) "m_oe");
        inputSignal.addElement((String) "m_we");
        System.out.println("Still alive (2)!");


        /* create help and machine class instances */

        help    = new Help ();
        machine = new Machine (inputSignal);

        /* set applet size */

        resize (DIMENSION, DIMENSION);

        /* create button bar panel */

        buttons = new Panel ();

        /* set layout within the panel to 5 elements per row */

        buttons.setLayout (new GridLayout (0, 5));

        /* add all buttons */

        buttons.add (new Button ("New"));
        buttons.add (new Button ("Load"));
        buttons.add (new Button ("Save"));
        buttons.add (new Button ("About"));
        buttons.add (new Button ("Quit"));

        buttons.add (new Button ("Add State"));
        buttons.add (new Button ("Del State"));
        buttons.add (new Button ("Mark Initial"));
        buttons.add (new Button ("Mark Accepting"));
        buttons.add (new Button ("Mark Normal"));
        buttons.add (new Button ("Add Transition"));
        buttons.add (new Button ("Del Transition"));

        buttons.add (new Button ("Define Inputs"));
        buttons.add (new Button ("Set Inputs"));
        buttons.add (new Button ("Run"));
        buttons.add (new Button ("Stop"));
        buttons.add (new Button ("Rename State"));
        buttons.add (new Button ("VHDL-Output"));

        /* add button panel to applet panel */

        add (buttons);

        /* resize bar panel and position it at the top of the applet */

        buttons.resize (DIMENSION, CONTROL_HEIGHT);
        buttons.move (0, 0);
        buttons.show ();

        /* create dialog panels, add them to applet panel and resize to
           desired dimensions. for some reason resizing within the dialog
           itself seems to have no effect. i guess i am missing something
           here. note that dialogs are initialized hidden and will not show
           up until show() method is invoked on them. */

        aboutBox       = new AboutBox (this);
        aboutBox.setTitle("About this program");
        quitDialog     = new QuitDialog (this);
        quitDialog.setTitle("Quitting applet");
        tokenSelection = new TokenSelection (this, inputSignal);
        tokenSelection.setTitle("Select input signals");
        inputActivation = new InputActivation (this, inputSignal);
        inputActivation.setTitle("Toggle input signals");
        fileInput      = new FileInput (this);
        fileInput.setTitle("File access");

        System.out.println("Still alive (3)!");

        inputMaintenance = new InputMaintenance(this, inputSignal);
        inputMaintenance.setTitle("Define input signals");

        System.out.println("Still alive (4)!");

        stateNameInput = new NameInput((Container) this, "Rename a state");
        stateNameInput.setMode(stateNameInput.STATE);

        inputNameInput = new NameInput((Container) inputMaintenance, "Name of input signal");
        inputNameInput.setMode(inputNameInput.INPUT);

        patienceBox = new PatienceBox("Working...");

        System.out.println("Still alive (5)!");


        add (aboutBox);
        add (quitDialog);
        add (tokenSelection);
        add (inputActivation);
        add (fileInput);
        add (stateNameInput);
        add (inputNameInput);
        add (inputMaintenance);
        add (patienceBox);

        //aboutBox      .resize (220, 100);
        //quitDialog    .resize (200, 150);
        //tokenSelection.resize (100, 250);
        //inputActivation.resize (250, 250);
        //fileInput     .resize (400, 120);
        aboutBox.pack();
        quitDialog.pack();
        tokenSelection.pack();
        inputActivation.pack();
        fileInput.pack();
        stateNameInput.pack();
        inputNameInput.pack();
        inputMaintenance.pack();
        patienceBox.pack();

        Rectangle bounds = bounds();
        Rectangle abounds;

        abounds = tokenSelection.bounds();
        tokenSelection.move (bounds.x + (bounds.width  - abounds.width) / 2,
                             bounds.y + (bounds.height - abounds.height) / 2);

		//tokenSelection.reshape(bounds.x, bounds.y + CONTROL_HEIGHT, 150, 250);

		abounds = inputActivation.bounds();
        inputActivation.move (bounds.x + (bounds.width  - abounds.width) / 2,
                              bounds.y + (bounds.height - abounds.height) / 2);

        abounds = quitDialog.bounds ();
        quitDialog    .move (bounds.x + (bounds.width  - abounds.width) / 2,
                             bounds.y + (bounds.height - abounds.height) / 2);

        abounds = aboutBox.bounds();
        aboutBox.resize(abounds.width + 40, abounds.height);
        aboutBox      .move (bounds.x + (bounds.width  - abounds.width) / 2,
                             bounds.y + (bounds.height - abounds.height) / 2);

        abounds = fileInput.bounds();
        fileInput     .move (bounds.x + (bounds.width  - abounds.width) / 2,
                             bounds.y + (bounds.height - abounds.height) / 2);

        abounds = stateNameInput.bounds();
        stateNameInput.move (bounds.x + (bounds.width  - abounds.width) / 2,
                             bounds.y + (bounds.height - abounds.height) / 2);

        abounds = inputNameInput.bounds();
        inputNameInput.move (bounds.x + (bounds.width  - abounds.width) / 2,
                             bounds.y + (bounds.height - abounds.height) / 2);

        abounds = inputMaintenance.bounds();
        inputMaintenance.move (bounds.x + (bounds.width  - abounds.width) / 2,
                               bounds.y + (bounds.height - abounds.height) / 2);

        abounds = patienceBox.bounds();
        patienceBox   .move (bounds.x + (bounds.width  - abounds.width) / 2,
                             bounds.y + (bounds.height - abounds.height) / 2);

        /* let the dialogs know about the machine - they will have
           to communicate user commands and input to it */

        tokenSelection  .setMachine (machine, help, patienceBox);
        fileInput       .setMachine (machine, help);
        stateNameInput  .setMachine (machine, help, inputSignal, inputMaintenance);
        inputNameInput  .setMachine (machine, help, inputSignal, inputMaintenance);
        inputMaintenance.setMachine (machine, help, inputSignal, inputNameInput);

        machine.setInputActivation (inputActivation);

        /* display initial message on the hint line */

        help.setHelp (help.INITIAL);

        /* make applet show now */

        show ();

    } /* end init */

	public synchronized void stop()
	{
        aboutBox.hide();
        quitDialog.hide();
        tokenSelection.hide();
        inputActivation.hide();
        fileInput.hide();
        stateNameInput.hide();
        inputNameInput.hide();
        inputMaintenance.hide();
        patienceBox.hide();
	} /* end stop */

    /*
        override the layout method with the one that does nothing. we do not
        need layout for the applet panel since all the positioning being done
        manually.
    */

    public synchronized void layout ()
    {

    } /* end layout */


    /*
        this method is called when repaint of the panel is requested
    */

    public void paint (Graphics g)
    {
        Rectangle size = bounds ();

        /* if cleanup was requested - clear the entire panel */

        if (clearAll)
        {
            clearAll = false;

            g.setColor (Color.lightGray);
            g.fillRect (0, 0, size.width, size.height);
        }

        /* repaint the components */

        buttons        .paint (g);
        machine        .paint (g, CONTROL_HEIGHT + 5);
        help           .paint (g, size.width, size.height);

    } /* end paint */

    public void updateInputActivation(String oldName, String newName)
    {
        inputActivation.paint (oldName, newName);
    }

    public void updateInputActivation(int action, String objekt)
    {
        inputActivation.paint (action, objekt);
    }


    /*
        this method handles all of the the events for the applet
    */

    public boolean handleEvent (Event evt)
    {
        switch (evt.id)
        {

            case Event.MOUSE_DOWN:

                switch (clickState)
                {
                    case CLICK_STATE_ADD:

                        /* tell machine to create a new state at the location
                           of the mouse click */

                        if (! machine.addState (evt.x, evt.y))
                            help.setHelp (help.MAX_STATES);
                        else
                            help.setHelp (help.EMPTY);

                        break;

                    case CLICK_STATE_REMOVE:

                        /* tell machine to remove a state at the location
                           of the mouse click */

                        if (! machine.removeState (evt.x, evt.y))
                            help.setHelp (help.NO_STATE);

                        break;

                    case CLICK_STATE_INITIAL:

                        /* tell machine to mark a state at the location
                           of the mouse click as initial */

                        if (! machine.makeStateInitial (evt.x, evt.y))
                            help.setHelp (help.INITIAL_STATE_PRESENT);
                        else
                            help.setHelp (help.EMPTY);

                        break;

                    case CLICK_STATE_ACCEPTING:

                        /* tell machine to mark a state at the location
                           of the mouse click as accepting */

                        if (! machine.makeStateAccepting (evt.x, evt.y))
                            help.setHelp (help.NO_STATE);
                        else
                            help.setHelp (help.EMPTY);

                        break;

                    case CLICK_STATE_NORMAL:

                        /* tell machine to mark a state at the location
                           of the mouse click as normal */

                        if (! machine.makeStateNormal (evt.x, evt.y))
                            help.setHelp (help.NO_STATE);
                        else
                            help.setHelp (help.EMPTY);

                        break;

                    case CLICK_TRANSITION_FIRST_STATE:

                        /* tell machine to mark a state at the location
                           of the mouse click as first state for the upcoming
                           transition */

                        ersterZustand = machine.selectFirstState (evt.x, evt.y);

                        if (ersterZustand == (State) null)
                        {
                            help.setHelp (help.NO_STATE);
                            break;
                        }

                        help.setHelp (help.LAST_STATE);

                        /* go on to selecting last state */

                        clickState = CLICK_TRANSITION_LAST_STATE;

                        repaint ();

                        return true;

                    case CLICK_TRANSITION_LAST_STATE:

                        /* tell machine to mark a state at the location
                           of the mouse click as last state for the upcoming
                           transition */

                        if (! machine.selectLastState (evt.x, evt.y))
                        {
                            help.setHelp (help.NO_STATE);
                            ersterZustand.highlightOff();
                        }
                        else
                        {
	                        help.setHelp (help.EMPTY);

    	                    /* popup token selection dialog */

        	                //T tokenSelection.dispose();
        	                //T tokenSelection = new TokenSelection(this, inputSignal);
        	                //T tokenSelection.hide();
        	                //T tokenSelection.setTitle("Token selection");
        	                //T tokenSelection.setMachine (machine, help, patienceBox);
        	                //T Rectangle bounds = bounds();
   	                		//T tokenSelection.reshape(bounds.x, bounds.y + CONTROL_HEIGHT, 150, 250);
        	                //T add(tokenSelection);
                  			tokenSelection.show();
        	            }
						ersterZustand = (State) null;

                        break;

                    case CLICK_TRANSITION_REMOVE_FIRST:

                        /* tell machine to mark a state at the location
                           of the mouse click as first state for the upcoming
                           transition removal */

						ersterZustand = machine.selectFirstState (evt.x, evt.y);

                        if (ersterZustand == (State) null)
                        {
                            help.setHelp (help.NO_STATE);
                            break;
                        }

                        help.setHelp (help.LAST_STATE);

                        /* go on to selecting last state */

                        clickState = CLICK_TRANSITION_REMOVE_LAST;

                        repaint ();

                        return true;

                    case CLICK_TRANSITION_REMOVE_LAST:

                        /* tell machine to mark a state at the location
                           of the mouse click as first state for the upcoming
                           transition removal */

                        if (! machine.selectLastState (evt.x, evt.y))
                        {
                            help.setHelp (help.NO_STATE);
                            ersterZustand.highlightOff();
                        }
                        else
                        {
	                        help.setHelp (help.EMPTY);

    	                    /* remove the transition from first state to last state */

        	                machine.removeTransition ();
						}

						ersterZustand = (State) null;

                        break;

                    case CLICK_RUN_SIMULATION:

                        /* invoke runSimulation method on machine until it returns
                           false */

                        switch (machine.runSimulation ())
                        {
                            case Machine.STATUS_ACCEPTED:
                                help.setHelp (help.STRING_ACCEPTED);
                                repaint();
                                return true;
                            	// or else simulation will end
                            	// CLK

                            case Machine.STATUS_NOT_ACCEPTED:
                                help.setHelp (help.STRING_NOT_ACCEPTED);
                                break;

                            case Machine.STATUS_BAD_TOKEN:
                                help.setHelp (help.BAD_TOKEN);
                                break;

                            case Machine.STATUS_BAD_GRAMMAR:
                                help.setHelp (help.BAD_MACHINE);
                                break;

                            case Machine.STATUS_MACHINE_WAITING:
                            	help.setHelp (help.MACHINE_WAITING);
                            	repaint();
                            	return true;
                            	// or else simulation will end
                            	// CLK

                            case Machine.STATUS_NORMAL:
                            default:
                                help.setHelp (help.RUN_INSTRUCTIONS);
                                repaint ();
                                return true;
                        }

                        break;

                    case CLICK_RENAME_STATE:
                    	ersterZustand = machine.selectFirstState(evt.x, evt.y);
                        if (ersterZustand == (State) null)
                        {
                        	help.setHelp (help.NO_STATE);
                        	break;
                        } else {
                        	stateNameInput.setErsterZustand(ersterZustand);
                        	stateNameInput.show();
                        	repaint();
                        }
                        ersterZustand = (State) null;

                        break;

                    case CLICK_NORMAL:
                    default:

                        /* see if there is a state to drag */

                        dragging = machine.selectDragState (evt.x, evt.y);

                        break;
                }

                clickState = CLICK_NORMAL;

                break;

            case Event.MOUSE_UP:

                /* if we are dragging something - stop it */

                if (dragging)
                {
                    machine.deselectDragState (evt.x, evt.y);
                    dragging = false;
                }

                break;

            case Event.MOUSE_DRAG:

                /* if we are dragging something - keep dragging */

                if (dragging)
                    machine.dragState (evt.x, evt.y);

                break;

            /* handle action events (button presses) */

            case Event.ACTION_EVENT:

                /* only respond to button presses */

                if (evt.target instanceof Button)
                {
                    /* find out the name of the button pressed */

                    String label = ((Button)(evt.target)).getLabel ();

                    /* pressing a button destroys previous click state */

                    clickState = CLICK_NORMAL;

                    if (label.equals ("Quit"))
                    {
                        /* popup quit confirmation dialog */

                        quitDialog.show ();
                    }
                    else if (label.equals ("About"))
                    {
                        /* popup about window and play a sound */

                        aboutBox.show();

                        //play (getCodeBase (), "welcome.au");
                    }
                    else if (label.equals ("New"))
                    {
                        clearAll = true;

                        /* create new machine instance - the old one will get
                           garbage collected */

                        machine = new Machine (inputSignal);

                        /* link dialogs with the new machine */

                        tokenSelection.setMachine (machine, help, patienceBox);
                        fileInput     .setMachine (machine, help);
                        stateNameInput.setMachine (machine, help, inputSignal, inputMaintenance);
				        inputNameInput.setMachine (machine, help, inputSignal, inputMaintenance);
				        inputMaintenance.setMachine (machine, help, inputSignal, inputNameInput);


                        help.setHelp (help.INITIAL);
                    }
                    else if (label.equals ("Load"))
                    {
                        /* 'load' does implicit 'new' */

                        clearAll = true;

                        /* create new machine instance - the old one will get
                           garbage collected */

                        machine = new Machine (inputSignal);

                        /* link dialogs with the new machine */

                        tokenSelection.setMachine (machine, help, patienceBox);
                        fileInput     .setMachine (machine, help);
                        stateNameInput.setMachine (machine, help, inputSignal, inputMaintenance);
                        inputNameInput.setMachine (machine, help, inputSignal, inputMaintenance);
				        inputMaintenance.setMachine (machine, help, inputSignal, inputNameInput);


                        help.setHelp (help.INITIAL);

                        /* tell file dialog to perform load and pop it up */

                        fileInput.setMode (FileInput.LOAD);
                        fileInput.show();
                    }
                    else if (label.equals ("Save"))
                    {
                        /* tell file dialog to perform save and pop it up */

                        fileInput.setMode (FileInput.SAVE);
                        fileInput.show();
                    }
                    else if (label.equals ("Define Inputs"))
                    {
                        /* popup input string dialog */

                        inputMaintenance.show();
                    }
                    else if (label.equals ("Set Inputs"))
                    {
                        /* popup input activation dialog */

                        inputActivation.show();
                    }
                    else if (label.equals ("VHDL-Output"))
                    {
                    	/* Create hardware description using
                    	   VHDL. */

                        machine.createVHDL();
                    }
                    else if (label.equals ("Run"))
                    {
                        /* tell machine to start simulation */

                        switch (machine.startSimulation ())
                        {
                            case Machine.STATUS_NORMAL:
                                help.setHelp (help.RUN_INSTRUCTIONS);
                                clickState = CLICK_RUN_SIMULATION;
                                break;

                            case Machine.STATUS_NO_INPUT:
                                help.setHelp (help.NO_INPUT_STRING);
                                break;

                            case Machine.STATUS_NO_INITIAL:
                                help.setHelp (help.NO_INITIAL_STATE);
                                break;

                            case Machine.STATUS_NO_ACCEPTING:
                                help.setHelp (help.NO_ACCEPTING_STATE);
                                break;

                            default:
                                break;
                        }

                    }
                    else if (label.equals ("Stop"))
                    {
                        /* tell machine to stop simulation */

                        machine.stopSimulation ();
                        help.setHelp (help.EMPTY);
                        clickState = CLICK_NORMAL;
                    }

                    /* for the following buttons just set the appropriate
                       click state and hint line text. all the work will be
                       done after the mouse is clicked */

                    else if (label.equals ("Add State"))
                    {
                        clickState = CLICK_STATE_ADD;
                        help.setHelp (help.SELECT_LOCATION);
                    }
                    else if (label.equals ("Del State"))
                    {
                        clickState = CLICK_STATE_REMOVE;
                        help.setHelp (help.TARGET_STATE);
                    }
                    else if (label.equals ("Mark Initial"))
                    {
                        clickState = CLICK_STATE_INITIAL;
                        help.setHelp (help.TARGET_STATE);
                    }
                    else if (label.equals ("Mark Accepting"))
                    {
                        clickState = CLICK_STATE_ACCEPTING;
                        help.setHelp (help.TARGET_STATE);
                    }
                    else if (label.equals ("Mark Normal"))
                    {
                        clickState = CLICK_STATE_NORMAL;
                        help.setHelp (help.TARGET_STATE);
                    }
                    else if (label.equals ("Add Transition"))
                    {
                        clickState = CLICK_TRANSITION_FIRST_STATE;
                        help.setHelp (help.FIRST_STATE);
                    }
                    else if (label.equals ("Del Transition"))
                    {
                        clickState = CLICK_TRANSITION_REMOVE_FIRST;
                        help.setHelp (help.FIRST_STATE);
                    }
                    else if (label.equals ("Rename State"))
                    {
                        clickState = CLICK_RENAME_STATE;
                        help.setHelp (help.RENAME_STATE);
                    }

                }

                break;

            /* for all other events run the default Applet class handler */

            default:


                return super.handleEvent (evt);
        }

        /* force applet repaint as something could have changed */

        repaint ();

        /* return true - means we have handled the event and there is
           no need to pass it along to other components */

        return true;

    } /* end handleEvent */


} /* end Simulator */

