import java.applet.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
import java.io.*;
import java.net.*;


/*
    State class - implements all the logic for the state. It knows how to draw
    itself on the screen, how to read and write itself to/from a file. Also
    maintains all the transitions initiating from it.
*/


class State
{
    /*
        CLASS CONSTANTS
    */

    /*
        current state of the State
    */

    final static int    NORMAL      = 0;
    final static int    MOVING      = 1;
    final static int    ACTIVE      = 2;
    final static int    HIGHLIGHTED = 3;

    final static int	SEVERAL_ACCEPTED	= 1;
    final static int	NO_ONE_ACCEPTED		= 2;
    final static int	NO_ERRORS			= 3;

    /* dimensions on the screen in pixels */

    final static int    SIZE = 50;

    /* delimiters used to distinguish records in files */

    final static int    BOT = 0x70;
    final static int    EOS = 0x71;


    /*
        INSTANCE VARIABLES
    */

    /* state origin coordinates on the screen (top left corner of the bounding
       rectangle). */

    private int         originX;
    private int         originY;

    /* origin positions used to redraw state during dragging and moving */

    private int         oldX;
    private int         oldY;
    private int         dragX;
    private int         dragY;

    /* colors corresponding to different states */

    private Color       colorNormal;
    private Color       colorSelected;
    private Color       colorActive;
    private Color       colorHighlight;

    /* a growing array of transitions */

    private Vector      transitions;

    /* initial, accepting, valid flags */

    private boolean     initial;
    private boolean     accepting;
    private boolean     valid;

    /* current transition curresponding to the token */

    private Transition  currTransition;

    /* current and old states */

    private int         state;
    private int         oldState;

    /* transition being removed */
    // private Transition  removing;
    // Replaced by transition.setRemovingStatus() and
    // transition.getRemovingStatus().
    // CLK

    /* state id */

    private int         id;

    /* state id label */

    private String      label;

    private InputSignal	inputSignal;


    /*
        PUBLIC METHODS
    */


    /*
        constructor method for adding state from user click
    */

    public State (int i, int x, int y, InputSignal inp)
    {
        /* initialize instance variable */

        inputSignal = inp;

        id    = i;
        label = new Integer (id).toString ();

        oldX = originX = x;
        oldY = originY = y;

        colorNormal    = new Color (0, 0, 200);
        colorSelected  = new Color (80, 80, 80);
        colorActive    = new Color (255, 100, 0);
        colorHighlight = new Color (0, 100, 255);

        state = NORMAL;

        currTransition = (Transition) null;

        initial = accepting = false;
        valid = true;

        transitions = new Vector (5, 1);

    } /* end State */


    /*
        constructor method for adding state from file
    */

    public State (int i, FileInputStream file)
    {
        /* read data catching I/O error exception */

        try
        {
            /* initialize instance variable */

            id    = i;
            label = new Integer (id).toString ();

	        colorNormal    = new Color (0, 0, 200);
    	    colorSelected  = new Color (80, 80, 80);
        	colorActive    = new Color (255, 100, 0);
	        colorHighlight = new Color (0, 100, 255);

            state = NORMAL;

            currTransition = (Transition) null;

            initial = accepting = false;
            valid = true;

            transitions = new Vector (5, 1);

            /* read in origin coordinates and initial and accepting states */

            oldX = originX = file.read () | (file.read () << 8);
            oldY = originY = file.read () | (file.read () << 8);
            initial        = file.read () == 1;
            accepting      = file.read () == 1;

            int val = 0;

            /* while delimiter is not End Of State information */

            while (val != EOS)
            {
                /* read next delimiter */

                val = file.read ();

                /* if delimiter is Beginning Of Transition information */

                if (val == BOT)
                {
                    /* create a new transition, letting its constructor
                       initialize itself from the file */

                    Transition transition = new Transition (this, file);

                    transitions.addElement ((Object) transition);
                }

                /* any delimiter value other than EOS is error */

                else if (val != EOS)
                    return;
            }
        }
        catch (IOException e)
        {
        }

    } /* end State */



	/*
	   Change the state's label
	   CLK
	*/

	public void setLabel (String newName)
	{
		label = newName;
	} /* end setLabel */

	// CLK
	public String getLabel()
	{
		return label;
	}


    /*
        called when user starts dragging the state. sample the coordinates
        and set state to MOVING.
    */

    public void dragStart (int x, int y)
    {
        dragX    = x;
        dragY    = y;
        oldState = state;
        state    = MOVING;

    } /* end dragStart */


    /*
        called when user stops dragging the state. update the position by the
        same amount that user moved the mouse. restore previous state.
    */

    public void dragStop (int x, int y)
    {
        move (originX + (x - dragX), originY + (y - dragY));
        dragX = x;
        dragY = y;
        state = oldState;

    } /* end dragStop */


    /*
        called while user stops drags the state. update the position by the
        same amount that user moved the mouse.
    */

    public void drag (int x, int y)
    {
        move (originX + (x - dragX), originY + (y - dragY));
        dragX = x;
        dragY = y;

    } /* end drag */


    /*
        draw itself on the specified graphics context.
    */

    public void paint (Graphics g)
    {
        /* remove previous image */

        remove (g);

        /* do not paint the image if state is not valid */

        if (! valid)
            return;

        /* set color depending on the current state */

        Color   color;

        switch (state)
        {
            case MOVING:
                color = colorSelected;
                break;

            case ACTIVE:
                color = colorActive;
                break;

            case HIGHLIGHTED:
                color = colorHighlight;
                break;

            case NORMAL:
            default:
                color = colorNormal;
                break;
        }

        /* set color and draw the circle */

        g.setColor (color);
        g.drawOval (originX, originY, SIZE, SIZE);


        /* draw bounding rectangle to denote initial state */

        if (initial)
            g.drawRect (originX, originY, SIZE - 1, SIZE - 1);

        /* draw smaller internal circle to denote accepting state */

        if (accepting)
            g.drawOval (originX + 5, originY + 5, SIZE - 10, SIZE - 10);

        /* draw state label */

        int labelHeight = g.getFontMetrics().getHeight();
        int labelWidth = g.getFontMetrics().stringWidth(label);

        g.drawString (label, originX + (SIZE - labelWidth) / 2, originY + SIZE / 2);

        /* draw all the transitions that belong to us */

        Transition tmp;

        for (int i = 0; i < transitions.size (); i ++)
        {
            tmp = (Transition) (transitions.elementAt (i));

            /* depending on the state pick the color (could be different
                than the state color) */

            switch (state)
            {
                case MOVING:
                    color = colorSelected;
                    break;

                case ACTIVE:
                    /* only draw the transition having the token as active */

                    if (currTransition == tmp)
                    {
                        color = colorActive;
                        break;
                    }

                case HIGHLIGHTED:
                case NORMAL:
                default:
                    color = colorNormal;
                    break;
            }

            g.setColor (color);

            /* paint transition */

            tmp.paint (g);
        }

    } /* end paint */


    /*
        remove out drawing from the specified graphics context.
    */

    public void remove (Graphics g)
    {
        /* fill the area occupied by the state with background color */

        g.setColor (Color.lightGray);
        g.fillRect (oldX, oldY, SIZE, SIZE);

        /* remove transition drawings */

        for (int i = 0; i < transitions.size (); i ++)
        {
            Transition tmp = (Transition) (transitions.elementAt (i));

            /* clear the transition */

            tmp.remove (g);

            /* if this transition is being removed or the state on the other
               side of the transition is removed - delete it */

            if (tmp.getRemovingStatus() || ! tmp.stateValid ())
            {
                transitions.removeElementAt (i);
                i --;
            }
        }

        /* update previous coordinates */

        oldX = originX;
        oldY = originY;

    } /* end remove */


    /*
        move state's origin to specified location.
    */

    public void move (int x, int y)
    {
        originX = x;
        originY = y;

    } /* end move */


    /*
        return the bounding rectangle of the state image.
    */

    public Rectangle bounds ()
    {
        return new Rectangle (originX, originY, SIZE, SIZE);

    } /* end bounds */


    /*
        check if the specified coordinate is within the state image.
    */

    public boolean inside (int x, int y)
    {
        return (x >= originX && x <= originX + SIZE &&
                y >= originY && y <= originY + SIZE);

    } /* end inside */


    /*
        mark state as initial.
    */

    public void makeInitial ()
    {
        initial = true;

    } /* end makeInitial */


    /*
        mark state as accepting.
    */

    public void makeAccepting ()
    {
        accepting = true;

    } /* end makeAccepting */


    /*
        mark state as normal (clear the initial and accepting flags)
    */

    public void makeNormal ()
    {
        initial = accepting = false;

    } /* end makeNormal */


    /*
        mark state as invalid
    */

    public void makeInvalid ()
    {
        valid = false;

    } /* end makeInvalid */


    /*
        return the initial flag.
    */

    public boolean isInitial ()
    {
        return initial;

    } /* end isInitial */


    /*
        return the accepting flag.
    */

    public boolean isAccepting ()
    {
        return accepting;

    } /* end isAccepting */


    /*
        return the valid flag.
    */

    public boolean valid ()
    {
        return valid;

    } /* end valid */


    /*
        return state id.
    */

    public int stateId ()
    {
        return id;

    } /* end stateId */


    /*
        set the state to highlighted.
    */

    public void highlightOn ()
    {
        state = HIGHLIGHTED;

    } /* end highlighOn */

    public void activeOn ()
    {
        state = ACTIVE;

    } /* end highlighOn */


    /*
        clear the highlighted state (set the state to normal).
    */

    public void highlightOff ()
    {
        state = NORMAL;

    } /* end highlighOn */


    /*
        add new transition from this state to specified state.
    */

    public void addTransition (State last, InputSignal tokens)
    {
        /* see if we already have transition between these two states */

        for (int i = 0; i < transitions.size (); i ++)
        {
        	System.out.println("State.addTransition rechnet: " + i + " von " + transitions.size());
            Transition tmp = (Transition) (transitions.elementAt (i));

            /* if one exists - just upgrade its token set with the new ones.
               do not create a new transition. */

            if (tmp.destination () == last)
            {
                tmp.addTokens (tokens);
                return;
            }
        }

        /* create a new transition */

        Transition transition = new Transition (this, last, tokens);

        transitions.addElement ((Object) transition);

    } /* end addTransition */


    /*
        remove transition from this state to specified state.
    */

    public void removeTransition (State last)
    {
        /* find the transition with the specified destination state and mark
           it as being removed. it will be actually removed in the paint()
           call after its image had been removed from the screen. */

        for (int i = 0; i < transitions.size (); i ++)
        {
            Transition tmp = (Transition) (transitions.elementAt (i));

            if (tmp.destination () == last)
            {
                tmp.setRemovingStatus();
                return;
            }
        }

    } /* removeTransition */


    /*
        Take the specified input signals and see if any one can be passed
        along. If there are more than one transitions corresponding to any
        input signal report an error (SEVERAL_ACCEPTED).
        Another error is reported, if no input is accepted by any transition
        (NO_ONE_ACCEPTED).
        Otherwise set our state to active and mark the corresponding
        transition as current.
    */

    public int takeTokens (InputSignal inps)
    {
        Transition tmp;

        boolean skipRest = false;

        for (int j = 0; j < inps.size(); j++)
        {
	        /* find all transitions that take this input signal */

	        for (int i = 0; (i < transitions.size()) && !skipRest; i ++)
    	    {
        	    tmp = (Transition) (transitions.elementAt (i));

            	if (tmp.belongs ((String) inps.elementAt(j)))
	            {
    	            /* if more than one transition match - error */

        	        if (currTransition != (Transition) null)
            	    {
                	    currTransition = (Transition) null;
                    	return SEVERAL_ACCEPTED;
	                }

    	            currTransition = tmp;
	            }
	            if (currTransition != (Transition) null)
	            	skipRest = true;
    	    }
        }

        /* if no transitions match - error */

        if (currTransition == (Transition) null)
            return NO_ONE_ACCEPTED;

        /* mark state as active */

        // state = ACTIVE;

        return NO_ERRORS;

    } /* end takeTokens */


    /*
        logically pass the token and return the state which it was passed to
        (destination state of the current transition).
    */

    public State giveToken ()
    {
        /* When simulation starts */

        //if (state != ACTIVE)
        //    return (State) null;

        /* get the state at the end of the transition */

        State next;

        if (currTransition != (Transition) null)
        {
	        next = currTransition.destination ();
	        /* clear the active state */
	        currTransition = (Transition) null;
	    }
	    else
	    {
	    	next = (State) this;
	    }
	    state = NORMAL;
	    return next;


    } /* end giveToken */


    /*
        write state data into a file.
    */

    public boolean saveFile (FileOutputStream file)
    {
        /* write data catching I/O error exception */

        try
        {
            /* write origin coordinates */

            file.write (originX & 0xff);
            file.write (originX >>> 8);
            file.write (originY & 0xff);
            file.write (originY >>> 8);

            /* write initial and accepting flag values */

            if (initial)
                file.write (1);
            else
                file.write (0);

            if (accepting)
                file.write (1);
            else
                file.write (0);

            /* make all of out transitions write their own data */

            for (int i = 0; i < transitions.size (); i ++)
            {
                /* write Beginning Of Transition delimiter */

                file.write (BOT);

                /* make transitions write its data */

                if (! ((Transition) (transitions.elementAt (i))).saveFile (file))
                    return false;
            }

            /* write End Of State delimiter */

            file.write (EOS);
        }
        catch (IOException e)
        {
            return false;
        }

        return true;

    } /* end saveFile */


    /*
        make all transitions resolve state ids into state objects with the
        specified resolver object.
    */

    public void resolveId (StateIdResolver res)
    {
        Transition tmp;

        for (int i = 0; i < transitions.size (); i ++)
        {
            tmp = (Transition) (transitions.elementAt (i));

            tmp.resolveId (res);
        }

    } /* end resolveId */


	public void renameToken(String oldName, String newName)
	{
		for (int i = 0; i < transitions.size(); i++)
			((Transition) transitions.elementAt(i)).renameToken(oldName, newName);
	}

	public void removeToken(String toBeRemoved)
	{
		for (int i = 0; i < transitions.size(); i++)
		{
            Transition tmp = (Transition) (transitions.elementAt (i));

            if (tmp.removeToken(toBeRemoved) == true)
            	tmp.setRemovingStatus();
		}
	}

	public Vector getTransitions()
	{
		return transitions;
	}


} /* end State */

