import java.applet.*;
import java.awt.*;
import java.lang.*;
import java.util.*;
import java.io.*;
import java.net.*;


/*
    Transition class - implements the transition login. It knows how to draw
    itself on the screen, how to read and write itself to/from a file. It knows
    which tokens it is suppose to fire for and what state is at the end of the
    transition.
*/


class Transition
{
    /*
        INSTANCE VARIABLES
    */

    /* Is transition scheduled to be removed? */

    private boolean			willBeRemoved;

    /* source and destination states of the transition */

    private State           srcState;
    private State           dstState;

    /* destination state id, when destination state object is not known */

    private int             dstId;

    /* contains names of input signals the transition reacts to */

    private InputSignal		tokens;

    /* transition label */

    private StringBuffer    label;

    /*
        coordinates for drawing transition image on the screen
    */

    /* start and end coordinates */

    private int             x1, x2, y1, y2;

    /* label coordinates */

    private int             xs, ys;

    /* arrow top, first and second lines coordinates */

    private int             xat, xa1, xa2, yat, ya1, ya2;


    /*
        PUBLIC METHODS
    */


    /*
        constructor method for adding transition from user click
    */

    public Transition (State first, State last, InputSignal tok)
    {
        /* initialize instance variables */

        willBeRemoved = false;

        srcState = first;
        dstState = last;
        tokens   = tok;

        if (last != (State) null)
            dstId    = last.stateId ();

        x1 = x2 = y1 = y2 = xs = ys = xat = xa1 = xa2 = yat = ya1 = ya2 = 0;

        /* construct new label for this transition */

        makeLabel ();

    } /* end Transition */


    /*
        constructor method for adding transition from file
    */

//! Wird so bestimmt noch nicht funktionieren...

    public Transition (State first, FileInputStream file)
    {
        /* read data catching I/O error exception */

        try
        {
            /* initialize instance variables */

            srcState = first;
            dstState = (State) null;

            x1 = x2 = y1 = y2 = xs = ys = xat = xa1 = xa2 = yat = ya1 = ya2 = 0;

            /* read destination state id */

            dstId = file.read ();

            /* create new token flags array and initialize it from file */

            tokens = new InputSignal(Simulator.ESTIMATED_INPUTS, 1);

            //I for (int i = 0; i <= InputString.TOKENS; i ++)
            //I     tokens [i] = file.read () == 1;

            /* construct new label for this transition */

            makeLabel ();
        }
        catch (IOException e)
        {
        }

    } /* end Transition */


    /*
        checks if the destination state is valid.
    */

    public boolean stateValid ()
    {
        return (dstState != (State) null && dstState.valid ());

    } /* end stateValid */


    /*
        draw this transition image on the specified graphics context.
        actually just calculate the coordinates and call draw() method to
        do the actual drawing.
    */

    public void paint (Graphics g)
    {
        /* get source state bounding rectangle */

        Rectangle start = srcState.bounds ();

        /* if the source and destination states are the same */

        if (dstState == srcState)
        {
            /* draw a 3/4 circle from the top to the right side of the state
               image */

            x1 = start.x + start.width / 2;
            y1 = start.y - start.height / 2;
            x2 = x1 + start.width;
            y2 = y1 + start.height;

            xs = x2 - 10;
            ys = y1 + 10;

            xat = x1 + (x2 - x1) / 2;
            yat = y2;
            xa1 = x1 + (x2 - x1) / 2 + 3;
            ya1 = y2 - 7;
            xa2 = x1 + (x2 - x1) / 2 + 7;
            ya2 = y2 + 3;

        }

        /* if destination and source states are different figure out the
           coordinates depending on their relative position */

        else
        {
            /* get destination state bounding rectangle */

            Rectangle end   = dstState.bounds ();

            /* figure out the distances between the two states */

            int dx = Math.abs (end.x - start.x);
            int dy = Math.abs (end.y - start.y);


            /* destination is between -45 and 45 degrees of arc, if source
               is considered the center of the coordinate system.

                  /
                S -> D
                  \
            */

            if (end.x >= start.x + start.width && dx >= dy)
            {
                x1 = start.x + start.width;
                y1 = start.y + start.height / 2;
                // Originally was ...start.height /4*3 --
                // here and in the following lines.
                // CLK

                x2 = end.x;
                y2 = end.y + end.height / 2;

                xa1 = x2 - 5;
                ya1 = y2 - 5;

                xa2 = x2 - 5;
                ya2 = y2 + 5;

                xs = x1 + (x2 - x1) / 4;
                ys = y1 + (y2 - y1) / 4;
            }

            /* destination is between 45 and 135 degrees of arc, if source
               is considered the center of the coordinate system.

                  D
                  ^
                 \|/
                  S
            */

            else if (start.y >= end.y + end.height && dy >= dx)
            {
                x1 = start.x + start.width / 2;
                y1 = start.y;

                x2 = end.x + end.width / 2;
                y2 = end.y + end.height;

                xa1 = x2 + 5;
                ya1 = y2 + 5;

                xa2 = x2 - 5;
                ya2 = y2 + 5;

                xs = x1 + (x2 - x1) / 2;
                ys = y1 + (y2 - y1) / 2;
            }

            /* destination is between 135 and 225 degrees of arc, if source
               is considered the center of the coordinate system.

                  \
               D <- S
                  /
            */

            else if (start.x > end.x + end.width && dx > dy)
            {
                x1 = start.x;
                y1 = start.y + start.height / 2;
                // Was ...start.height /4
                // CLK

                x2 = end.x + end.width;
                y2 = end.y + end.height / 2;

                xa1 = x2 + 5;
                ya1 = y2 + 5;

                xa2 = x2 + 5;
                ya2 = y2 - 5;

                xs = x1 + (x2 - x1) / 4 * 3;
                ys = y1 + (y2 - y1) / 4 * 3;
            }

            /* destination is between 225 and 315 degrees of arc, if source
               is considered the center of the coordinate system.

                  S
                 /|\
                  v
                  D
            */

            else if (end.y > start.y + start.height && dy > dx)
            {
                x1 = start.x + start.width / 2;
                y1 = start.y + start.height;

                x2 = end.x + end.width / 2;
                y2 = end.y;

                xa1 = x2 - 5;
                ya1 = y2 - 5;

                xa2 = x2 + 5;
                ya2 = y2 - 5;

                xs = x1 + (x2 - x1) / 2;
                ys = y1 + (y2 - y1) / 2;

            }

            /* they must be overlapping - do not draw the transition */

            else
                x1 = x2 = y1 = y2 = xa1 = xa2 = ya1 = ya2 = 0;

            /* arrow head top is by the destination coordinate */

            xat = x2;
            yat = y2;

        }

        /* now draw the transition */

        draw (g);

    } /* end paint */


    /*
        draw the transition at its old coordinates, without recalculating.
        the color is preset in state's paint method.
    */

    public void remove (Graphics g)
    {
        draw (g);

    } /* end remove */


    /*
        draw the transition image with the current coordinates.
    */

    public void draw (Graphics g)
    {
        /* draw the main transition line */

        if (srcState == dstState)
            g.drawArc (x1, y1, x2 - x1, y2 - y1, -90, 270);
        else
            g.drawLine (x1, y1, x2, y2);

        /* draw the arrow head */

        g.drawLine (xat, yat, xa1, ya1);
        g.drawLine (xat, yat, xa2, ya2);

        /* draw the label */

		makeLabel();

        g.drawString (label.toString (), xs, ys);

    } /* draw */


    /*
        Check if the specified input belongs to this transition. Either the
        flag for the token or the all-encompassing flag will be set.
    */

    public boolean belongs (String s)
    {
    	if (s == (String) null)
    		return false;
    	else if (tokens.indexOf("All") != -1)
    		return true;
    	else if (tokens.indexOf(s) != -1)
    		return true;
    	else
    		return false;

    } /* end belongs */


    /*
        return the destination state for this transition.
    */

    public State destination ()
    {
        return dstState;

    } /* end destination */


    /*
        merge the old and new token flag arrays, thus updating the tokens for
        this transition.
    */

    public void addTokens (InputSignal newTokens)
    {
        /* merge */

        for (int i = 0; i < newTokens.size(); i ++)
        {
        	String s = (String) newTokens.elementAt(i);
        	if (! tokens.contains((Object) s))
        		tokens.addElement(s);
        }

        /* redo the label since the tokens have changed */

        makeLabel ();

    } /* end addTokens */

    public void renameToken(String oldName, String newName)
    {
    	if ((oldName != (String) null) && (newName != (String) null))
    	{
    		if (tokens.contains(oldName))
    			tokens.setElementAt(newName, tokens.indexOf(oldName));
    	}
    }

	/* Remove an input signal from transition and return true
	   if no signals remain. */

    public boolean removeToken(String toBeRemoved)
    {
    	if (tokens.contains(toBeRemoved))
    		tokens.removeElement((Object) toBeRemoved);

    	if (tokens.size() <= 0)
    		return true;
    	else
    		return false;
    }

    public InputSignal getTokens()
    {
    	return tokens;
    }

    /*
        write the transition data to file.
    */

    public void setRemovingStatus()
    {
    	willBeRemoved = true;
    }

    public boolean getRemovingStatus()
    {
    	return willBeRemoved;
    }

//! Wird so bestimmt noch nicht funktionieren...

    public boolean saveFile (FileOutputStream file)
    {
        /* write the data catching the I/O error exception */

        try
        {
            /* write destination state id */

            file.write (dstState.stateId ());

            /* write token flag array */

            //I for (int i = 0; i <= InputString.TOKENS; i ++)
            //I     if (tokens [i])
            //I         file.write (1);
            //I     else
            //I         file.write (0);
        }
        catch (IOException e)
        {
            return false;
        }

        return true;

    } /* end saveFile */


    /*
        resolve the destination state id into the actual object. this is done
        after reading transitions in from files. call the specified resolver
        object with the id.
    */

    public void resolveId (StateIdResolver res)
    {
        if (dstState == (State) null)
            dstState = res.resolveId (dstId);

    } /* end resolveId */


    /*
        PRIVATE METHODS
    */


    /*
        construct the label out of the token characters
    */

    private void makeLabel ()
    {
        /* if all-encompassing flag is set - write nothing */

        if (tokens.contains("All"))
            label = new StringBuffer ("");
        else
        {
            label = new StringBuffer (" ");

            int i;

            for (i = 0; i < tokens.size(); i ++)
            {
                label.append ((String) tokens.elementAt(i));
                label.append (", ");
            }
            if (label.length() >= 2)
            	label.setLength(label.length() - 2);
        }

    } /* end makeLabel */

} /* end Transition */

