/*---------------------------------------------------------------------------
File:         			Transition.java
Package:                        JFLAP Version 3.0
Author:				Magda & Octavian Procopiuc V1.0 07/15/96
                                Eric Gramond V3.0 08/14/98 
 
Description of Contents:	Contains class Transition.
				
--------------------------------------------------------------------------*/

/* 
 * Susan H. Rodger, Magda Procopiuc, Octavian Procopiuc, Eric Gramond
 * Computer Science Department
 * Duke University
 * June 1998
 * Supported by National Science Foundation DUE-9596002 and DUE-9555084.
 *
 * Copyright (c) 1998
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the author.  The name of the author may not be used to
 * endorse or promote products derived from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

package flap;

import java.awt.*;
import  java.awt.event.*;
import	java.util.*;

/**
 * This class implements the transition function of a machine.
 * It is designed to be used with the current machine types:
 * FSA, PDA, TM1, TM2.
 *
 * @author	Magda & Octavian Procopiuc
 * @version	1.0 15 July 1996
 */
public class Transition {
  public State		from;
  public State		to;
  public String		label;
  public String[]       sublabel;
  public double		distance;	// the ratio label->from / from->to
  public int		st;	// NORMAL or FOCUSED or HIGHLIGHTED
  public int            fieldCounter;
  public String[]       separator;
  public int            selectedSublabel;

  public  static final int	off= 14;
  public  static final int	offr = 4;
  private static final int	voff1 = 4;
  private static final int	voff2 = 2;
  private static final int	hoff1 = 2;
  private static final int	hoff2 = 3; 
  private static final int	arrow_length = 12;
  private static final int	arrow_width = 10;

  final static int NORMAL = 0;
  final static int SELECTED = 1;
  final static int FOCUSED = 2;
  final static int HIGHLIGHTED = 3;


  /**
   * Creates a transition with the given
   * from and to states and with the given label.
   */
  public Transition (State from, State to, String symbol, int fields, String[] seps) {
    this.from = from;
    this.to = to;
    label = "";
    fieldCounter = fields;
    separator = seps;
    distance = .5;
    selectedSublabel = 0;
    sublabel = new String[6];
    for (int i=0; i<fieldCounter; i++) {
        label += " " + separator[i];
        sublabel[i] = " ";
      }
    if(symbol.length() > 0)
      this.label = symbol;
  }

  /**
   * Creates a transition with the given
   * from and to states.
   */
  public Transition (State from, State to, int fields, String[] seps) {
    this(from, to, "", fields, seps);
  }

  /**
   * Returns the slope of the line between the from and to states.
   */
  public double slope() {
    if (to.p.x != from.p.x)
      return (double) (((double) from.p.y - to.p.y) / (from.p.x - to.p.x));
    else  if (to.p.y < from.p.y) return (double) 20000;
        else return (double) (-20000);
  }


  /**
   * Paints the label of a transition.
   */   
  public void paintLabel(Graphics g, Transition selectedTransition, Point pl) {
    int		cx;
    FontMetrics fm = g.getFontMetrics();
    int promptLength = (this == selectedTransition) ? fm.stringWidth("_") : 0;
    Color tc = (( this == selectedTransition) ? Params._t_selectedcolor: 
                   ((st == Transition.NORMAL ||
		     st == Transition.HIGHLIGHTED) ? Params._t_normalcolor: Params._t_focusedcolor));
    int h = fm.getAscent();

    int[] xPoints = new int[3];
    int[] yPoints = new int[3];
    computeArrow(pl, xPoints, yPoints);
 
    Color interiorc = ((st == Transition.HIGHLIGHTED) ? Params._t_selectedcolor : Params._t_interiorcolor);
    g.setColor(interiorc);
    g.fillRect(pl.x, pl.y, fm.stringWidth(label) + off + offr + promptLength, Params._t_height);
    g.setColor(tc);
    g.drawRect(pl.x, pl.y, fm.stringWidth(label) + off + offr + promptLength, Params._t_height);
    if (from == to) 
      g.fillOval(pl.x +hoff1, pl.y +voff1, arrow_width, arrow_width);
    else
      g.fillPolygon(xPoints, yPoints, 3);

    int yStrBase = (int) (pl.y - 2 + (Params._t_height + h)/2);
    if (this != selectedTransition)
      g.drawString(label, pl.x + off, yStrBase); 
    else {
        cx = pl.x + off;	// current writing position.
        promptLength = 0;                       
        g.setColor(Params._t_normalcolor);
        for (int i=0; i<fieldCounter; i++) {
          if (selectedSublabel == i) {
            promptLength = fm.stringWidth("_"); 
            g.setColor(Params._t_selectedcolor);
            g.drawString(sublabel[i]+"_", cx, yStrBase);
            cx += fm.stringWidth(sublabel[i] + "_");
            g.setColor(Params._t_normalcolor);
          } else {
            g.drawString(sublabel[i], cx, yStrBase);
            cx += fm.stringWidth(sublabel[i]);
          }
          g.drawString(separator[i], cx, yStrBase);
          cx += fm.stringWidth(separator[i]);
        }
    }  // end else.
  }

  /**
   * Paints the line of a transition.
   */
  public void paintLine(Graphics g, Transition selectedTransition) {
    int hw = 5;
    Color tc = ((this == selectedTransition) ? Params._t_selectedcolor: 
		((st == Transition.NORMAL) ? Params._t_normalcolor: Params._t_focusedcolor));
    g.setColor(tc);
    if (from == to) {
	g.drawOval(from.p.x - hw, from.p.y - from.loopSize, 
		   2*hw, from.loopSize);
	g.fillOval(from.p.x-4, from.p.y - from.loopSize - 4, 8, 8);
    }
    else
	g.drawLine(from.p.x, from.p.y, to.p.x, to.p.y);
  }


/**
  * Computes the points of the little triangle which shows
  * the direction of the transition.
  * @param pl	the upper left corner of the transition's label.
  * @param xpoints	the x coordinates of the points representing the arrow.
  * @param ypoints	the y coordinates of the points representing the arrow.
  */
  public void computeArrow(Point pl, int[] xPoints, int[] yPoints) {
    int x1 = from.p.x;
    int y1 = from.p.y;
    int x2 = to.p.x;
    int y2 = to.p.y;

    if (Math.abs(x2-x1) > Math.abs(y2-y1)) {
      //off = hoff1 + arrow_length + 2; 
      if (x2 > x1) {			//right-arrow
        xPoints[0] = xPoints[1] = pl.x + hoff1;
        xPoints[2] = pl.x + hoff1 + arrow_length;
        yPoints[0] = pl.y + voff1;
        yPoints[1] = pl.y + voff1 + arrow_width;
        yPoints[2] = pl.y + voff1 + (int) (arrow_width / 2);
      } else {				//left-arrow
        xPoints[0] = xPoints[1] = pl.x + hoff1 + arrow_length;
        xPoints[2] = pl.x + hoff1;
        yPoints[0] = pl.y + voff1;
        yPoints[1] = pl.y + voff1 + arrow_width;
        yPoints[2] = pl.y + voff1 + (int) (arrow_width / 2);
      } 
    } else {
      //      off = hoff2 + arrow_width + 2;
      if (y2 > y1) {			//down-arrow
        xPoints[0] = pl.x + hoff2;
        xPoints[1] = pl.x + hoff2 + arrow_width;
        xPoints[2] = pl.x + hoff2 + (int) (arrow_width / 2);
        yPoints[0] = yPoints[1] = pl.y + voff2;
        yPoints[2] = pl.y + voff2 + arrow_length;
      } else {				//up-arrow
        xPoints[0] = pl.x + hoff2;
        xPoints[1] = pl.x + hoff2 + arrow_width;
        xPoints[2] = pl.x + hoff2 + (int) (arrow_width / 2);
        yPoints[0] = yPoints[1] = pl.y + voff2 + arrow_length;
        yPoints[2] = pl.y + voff2;
      }
    }
  }

  /** processes a KeyEvent that occured in this transition for the Desktop. */  
  public boolean processKey(int key, KeyEvent e) {
    boolean modified = false;

     if (!e.isActionKey() && key >= 32 && key <= 126 && key != ((int) ' ') && 
	 key != ((int) ';') &&  (fieldCounter == 1 || (key != 32 && key != ((int) ','))) && 
	 key != ((int) '|'))
     {
       sublabel[selectedSublabel] += new Character((char) key);
       modified = true;
     } else if (key == 8 || key == 127||
                e.getKeyCode() == KeyEvent.VK_DELETE ||
		e.getKeyCode() == KeyEvent.VK_BACK_SPACE /*||
                key == 65288 || key == 65535*/) {	 //Backspace or Delete
       if (sublabel[selectedSublabel].length() > 1) {
	 sublabel[selectedSublabel] = sublabel[selectedSublabel].substring(0, sublabel[selectedSublabel].length()-1);
	 modified = true;
       }
     } else if (key == KeyEvent.VK_RIGHT || (key == '\t' && e.getModifiers() == 0)) {
       selectedSublabel = (++selectedSublabel) % fieldCounter;
     } else if (key == KeyEvent.VK_LEFT || 
		(key == '\t' && e.getModifiers() == InputEvent.SHIFT_MASK)) {
       selectedSublabel = (--selectedSublabel+fieldCounter) % fieldCounter;
     }
     label = "";
     for (int i=0; i<fieldCounter; i++) 
       label += sublabel[i]+separator[i];
       
     return modified;
   }

   /**
   * Tokenizes the given label.
   * The result is stored in the sublabel array.
   */
  public  void tokenizeLabel() {
    String	separators = "";

    for (int i=0; i<fieldCounter; i++)
      separators += separator[i];
    StringTokenizer stk = new StringTokenizer(label, separators, false);
    for (int i=0; i<fieldCounter; i++)
      sublabel[i] = stk.nextToken();
  }	
   

  /** returns a Vector of Strings corresponding to the list of ORed transitions represented
    * by this Transition 
    * works only for FSA's */
  public Vector tokenizeOrList() {
    Vector result = new Vector();
    String temp = "";
    
    if(fieldCounter > 1)
      return null;

    for(int i= 0; i<label.length(); i++) {
      if(label.charAt(i) == ',') {
	result.addElement(temp);
	temp = "";
      }
      else temp += label.charAt(i);
    }
    result.addElement(temp);
    return result;    
  }


    /**
     * sets the label to the given Vector of strings. Each element of the Vector becomes
     * a piece of the sublabel
     */
  public void setLabel(Vector aVector) {
    String aString;
    label = new String();
    for(Enumeration e = aVector.elements(); e.hasMoreElements(); ) {
      aString = (String) e.nextElement();
      label += aString.trim();
      if(e.hasMoreElements())
	label += ',';
    }
  }

}

