/*---------------------------------------------------------------------------
File:         			MinimizeDesktop.java
Package:                        JFLAP Version 3.1
Author:				Magda & Octavian Procopiuc V1.0 07/15/96
                                Eric Gramond V2.0 07/22/97
				Ted Hung V3.1 07/25/99
 
Description of Contents:	Contains class MinimizeDesktop.
				
--------------------------------------------------------------------------*/

/* 
 * Susan H. Rodger, Magda Procopiuc, Octavian Procopiuc, Eric Gramond, Ted Hung
 * Computer Science Department
 * Duke University
 * August 1999
 * Supported by National Science Foundation DUE-9596002, DUE-9555084,
 *  and DUE-9752583
 *
 * Copyright (c) 1999
 * 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 is a special version of the Desktop designed to handle the tree displaying
  * of the minimizing algorithm.
  *
  * @see		flap.State
  * @see		flap.Transition
  * @author	Magda & Octavian Procopiuc
  * @version	1.0, 15 July 1996
  */ 


public class MinimizeDesktop extends Desktop {
  
  State finals;
  State nonFinals;
  MinimizeTreeState expanding;
  State selectedSplitting;

  public MinimizeDesktop(int type) {
    super(type);
  }  
  

  protected void processKeyEvent(KeyEvent e) {
    
   if (e.getID() == KeyEvent.KEY_PRESSED){
      int key;
      if (!e.isActionKey()) // if is a character key
	key = e.getKeyChar();
      else key = e.getKeyCode();
      requestFocus();

      if(selectedState != null)
	selectedState.processDfaKey(key, e);
      else if(selectedSplitting != null)
        ((MinimizeTreeState) selectedSplitting).processSplitKey(key, e);
      else if(selectedTransition != null)
	selectedTransition.processKey(key, e);
      modified = false;
      repaint();	 
      e.consume(); // must consume so that a tab will not take the Desktop out of focus
   }
  }

  public void mousePressed(MouseEvent e) {
    State	s;
    Transition	t;
    boolean found = false;
    
    int x = e.getX();
    int y = e.getY();
    
    requestFocus();
    int mods = e.getModifiers();
    if((mods & Event.META_MASK) != 0 ||
       (mods & Event.CTRL_MASK) != 0 ||
       (mods & InputEvent.BUTTON2_MASK) != 0)
    {
        // right button down.
        menuOn = true;
        menu.move(x, y);
        menu.invalidateAll();
        if ((s = isInState(x, y)) != null) 
          menu.setState(s);
    }
    else if(mods == 0 || (mods & InputEvent.BUTTON1_MASK) != 0)
    {
       // left button down.
      if (((s = isInStateDfaLabel(x,y)) != null) && (expanding != null)) {
	for(Enumeration en = expanding.transitions.elements(); en.hasMoreElements(); ) {
	  t = (Transition) en.nextElement();
	  if(t.to == s)
	  {
	    found = true;
	    break;
	  }
	} 
	if(found == true) { 
	  draggingWhat = Desktop.DG_NOTHING;
	  firstState = s;
	  selectedTransition = null;
	  selectedSplitting = null;
	  selectedState = s; 
	} 
      } else if (((s = isInSplittingState(x,y)) != null) && ( s == expanding)){ 
	draggingWhat = Desktop.DG_NOTHING;
	firstState = s;
	selectedTransition = null;
	selectedState = null;
	selectedSplitting = s;
      } else {
	draggingWhat = Desktop.DG_NOTHING;
      }
    }
    
    repaint();
  }	// end of method MouseDown.
  
  public void mouseDragged(MouseEvent e)   {
    int x = e.getX();
    int y = e.getY();
    int mods = e.getModifiers();
    if((mods & Event.META_MASK) != 0 ||
       (mods & Event.CTRL_MASK) != 0 ||
       (mods & InputEvent.BUTTON2_MASK) != 0)
    {
      if (x >= menu.p.x && x <= menu.p.x + menu.width) {
	int idx = (int) Math.floor((y - menu.p.y) / menu.height);
	if (y >= menu.p.y && idx < menu.itemsCounter)
	  menu.hasFocus = idx;
	else
            menu.hasFocus = -1;
      } else
	menu.hasFocus = -1;
    }
    
    repaint();
  }	// end of method MouseDrag.
  

   public void mouseReleased(MouseEvent e){
    State	s;
    Transition	t;

    int x = e.getX();
    int y = e.getY();

    // left button up.
    if((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0)
    {
      drag = null;
      firstState = null;
      draggingWhat = Desktop.DG_NOTHING;
    }

    if (menuOn) {	
      menuOn = false;
      menu.takeAction();
    }
    repaint();
  }	// end of method MouseUp.

  public void paint(Graphics g) {
  
    Transition aTransition;
    MinimizeTreeState aState;  
    Dimension	d = getSize();
    g.setColor(backcolor);
    g.fillRect(0, 0, d.width, d.height);
    
    g.setColor(Params._d_forecolor);
    g.draw3DRect(0, 0, d.width-1, d.height-1, true);
    
    for (Enumeration e = theTransitions.elements(); e.hasMoreElements();) {
      ((Transition) e.nextElement()).paintLine(g, selectedTransition);
    }
    
    for (Enumeration e = theStates.elements(); e.hasMoreElements(); ) {
      aState = (MinimizeTreeState) e.nextElement();
      aState.paint(g, true);
      aState.paintSplittingLetter(g, selectedSplitting);
    }
    
    drawFinalTag(g);
    
    g.setFont(Params._t_font);
    if(showDfaLabels) {
      for (Enumeration e = theStates.elements(); e.hasMoreElements();) {
	((State) e.nextElement()).paintDfaLabel(g, selectedState);
      }
    }
    
    if (menuOn)
      menu.paint(g);
  }	
  
  
  /**
    * Creates a new state and adds it to the list of states.
    */
  public State addState(int x, int y) {
    State s = new MinimizeTreeState(currentId++, x, y);
    theStates.addElement(s);
    return s;
  }

  /**
    * places all the states nicely in the shape of a tree spaced out depending on weight
    */
  public void placeStates() {
    recurPlace(finals,  (int) (getSize().width/4), 0, 50);
    recurPlace(nonFinals, 3* (int) ( getSize().width/4), (int) getSize().width/2, 50);
  }
  
  private void recurPlace(State aState, int x, int xstart, int y) {
    Transition aTransition;
    State target;
    int xpos = xstart;
    int xposStart, add, weightSum;
    Enumeration e, e2;

    int weight = aState.stateList.size();
    aState.p = new Point(x, y);

    for (e = aState.transitions.elements(); e.hasMoreElements(); ) {
      aTransition = (Transition) e.nextElement();
      target = aTransition.to;
      xposStart = xpos;
      if( (aState.transitions.size() == 2) && 
	  ((Transition)(aState.transitions.elementAt(0))).to.stateList.size() == 0 &&
	  ((Transition)(aState.transitions.elementAt(1))).to.stateList.size() == 0)
	add = (int) ((x-xstart)/2);
      else if (target.stateList.size() == 0) {
	weightSum = 0;
	for(e2 = aState.transitions.elements(); e2.hasMoreElements(); ) 
	  weightSum += ((Transition) e2.nextElement()).to.stateList.size();
	add = (int) (((x-xstart) * (weight-weightSum))/weight);
      } else 
	add = (int) (((x-xstart) * target.stateList.size())/weight);
      

      xpos += add;
      recurPlace(target,  xpos, xposStart, y+50);
      xpos += add;

    }
  }

  /**
    * draws the "final" and "nonfinal" tags on top of the trees
    */
  private void drawFinalTag(Graphics g) {
    
    int emptyWidth = Params.emptyDfaLabelWidth;
    int y = 15;
    int x = finals.p.x;
    int x2 = nonFinals.p.x;
    FontMetrics fm = g.getFontMetrics();
    Color tc = Params._t_selectedcolor;
    x-= emptyWidth + (int) (fm.stringWidth("Final")/2);
    x2-= emptyWidth + (int) (fm.stringWidth("Nonfinal")/2);
    Point start = new Point(x,y);
    Point start2 = new Point(x2,y);
    Point end = new Point(fm.stringWidth("Final") + 2*emptyWidth, Params._t_height);
    Point end2 = new Point(fm.stringWidth("nonFinal")+ 2*emptyWidth,Params._t_height);
    
    g.setColor(Params._t_interiorcolor);
    g.fillRect(start.x, start.y, end.x, end.y);
    g.fillRect(start2.x, start2.y, end2.x, end2.y);
    g.setColor(tc);
    g.drawRect(start.x, start.y, end.x, end.y);
    g.drawRect(start2.x, start2.y, end2.x, end2.y);

    int h = fm.getAscent();
    g.drawString("Final", x+emptyWidth, y - 2+(Params._t_height+h)/2); 
    g.drawString("Nonfinal", x2+emptyWidth, y - 2+(Params._t_height+h)/2); 

  }

  /** 
    * checks if the given coordinates are within one of the letter splitting labels
    * @return the state corresponding to the label or null if not in a label
    */
  public State isInSplittingState(int x, int y) {
    MinimizeTreeState aState = null;
    for(Enumeration e = theStates.elements(); e.hasMoreElements(); ) {
      aState = (MinimizeTreeState) e.nextElement();

      if(aState.splitLabelStart == null || aState.splitLabelEnd == null)
	continue;

      if((x >= aState.splitLabelStart.x) && (x <= aState.splitLabelEnd.x) &&
	 (y >= aState.splitLabelStart.y) && (y <= aState.splitLabelEnd.y))
	return aState;
    }
    return null;
  }

}
