/*---------------------------------------------------------------------------
File:         			UpperWindow.java
Package:                        JFLAP Version 1.0
Author:				Magda & Octavian Procopiuc V1.0 07/15/96
                                
Description of Contents:	Contains classes UpperWindow & 
				ConfigurationCanvas				
--------------------------------------------------------------------------*/

/* 
 * 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.image.*;
import java.awt.event.*;
import java.util.*;

/**
 * This is the Step Run Window, which appears when the user selects 
 * Step Run from the Run menu. It has a panel with a number of buttons
 * at the bottom and a ConfigurationCanvas in the center.
 *
 * @author	Magda & Octavian Procopiuc
 * @version	1.0 15 July 1996
 */

public class UpperWindow extends Frame implements ActionListener, WindowListener{

  Environment		parent;
  Machine		a;
  ConfigurationCanvas 	cc;
  int			maxCount; // maximum # of configs which can
  				  // be displayed on the canvas
  
/**  
 * Creates a window for the step run. The current configurations
 * will be displayed on a configuration canvas.
 */
  
  public UpperWindow(String title, Environment parent) {
    super(title);
    setLocation(40 + parent.getLocation().x, parent.getSize().height + parent.getLocation().y - 150);
    setBackground(Params._uw_backcolor);
    this.parent = parent;
    this.addWindowListener(this);
    BorderLayout bl = new BorderLayout();
    setLayout(bl);
    a = parent.a;
    cc = new ConfigurationCanvas(a, parent);
    add("Center", cc);

    Panel p = new Panel();
    p.setLayout(new GridLayout(1, 8));
    add("South", p);
    Button btn;
    p.add(btn = new Button("Kill"));
    btn.addActionListener(this);
    p.add(btn = new Button("Freeze"));
    btn.addActionListener(this);
    p.add(btn = new Button("Thaw"));
    btn.addActionListener(this);
    p.add(btn = new Button("Trace"));
    btn.addActionListener(this);
    p.add(btn = new Button("Step"));
    btn.addActionListener(this);
    p.add(btn = new Button("Help"));
    btn.addActionListener(this);
    p.add(btn = new Button("Restart"));
    btn.addActionListener(this);
    p.add(btn = new Button("Quit"));
    btn.addActionListener(this);
       
  }

  public Dimension getMinimumSize() {
    return new Dimension(Params._uw_width, Params._uw_height);
  }

  public Dimension getPreferredSize() {
    return getMinimumSize();
  }

  public void windowClosing(WindowEvent e) {
    quit();
  }
  public void windowOpened(WindowEvent e) 
  {}
  public void windowIconified(WindowEvent e)  
  {}
  public void windowDeiconified(WindowEvent e) 
  {}
  public void windowClosed(WindowEvent e)  
  {}
  public void windowActivated(WindowEvent e)
  {}
  public void windowDeactivated(WindowEvent e)
  {}

  public void actionPerformed(ActionEvent e) {
    String arg = ((Button) e.getSource()).getLabel();
    if ("Quit".equals(arg)) {
      quit();
    } else if ("Step".equals(arg)) {
      startThread("step");
    } else if ("Kill".equals(arg)) {
      kill();
      a.d.repaint();
      cc.repaint();
    } else if ("Freeze".equals(arg)) {
      freeze();
      a.d.repaint();
      cc.repaint();
    } else if ("Thaw".equals(arg)) {
      thaw();
      a.d.repaint();
      cc.repaint();
    } else if ("Restart".equals(arg)) {
      restart();
      a.d.repaint();
      cc.repaint();
    } else if ("Trace".equals(arg)) {
      trace();
    } 
    else if ("Help".equals(arg))
    {
	if(parent.mainmenu.myHelpWindow == null)
        {
           try
           {
              parent.mainmenu.myHelpWindow = new HelpWindowHTML("JFLAP", Params._e_backcolor, parent.mainmenu.isApplet);
           }
           catch(Exception exc)
           {
              OKDialog dg = new OKDialog(exc.getMessage(), this, "Error");
              dg.show();
              return;
           }
        }
	parent.mainmenu.myHelpWindow.setCurrentTopic("Step Run Window");
	parent.mainmenu.myHelpWindow.setVisible(true);
	parent.mainmenu.myHelpWindow.toFront();
    }
  } 

// Calls the appropriate machine stepRun(int) and displays the message
// corresponding to the code returned by stepRun(int)
  public void step() {
    setEnabled(false);
    maxCount = cc.hCount * cc.vCount;
    switch (a.stepRun(maxCount)) {
      case Machine.NO_MESSAGE: 
        a.d.repaint();
        cc.repaint();
        break;
      case Machine.NO_MORE_CONFIGS_MSG:
        OKDialog dg1 = new OKDialog("No more expandable configurations", this, " ");
        dg1.findResult();
        break;
      case Machine.MAXCOUNT_MSG:
        OKDialog dg2 = new OKDialog("More than "+maxCount+" configurations\n would be obtained. Kill and/or freeze\nsome of the current configurations\nbefore proceeding.", this, "Warning");
        dg2.findResult();
        break;
      case Machine.MAXNCONFIGS_MSG: 
        a.d.repaint();
        cc.repaint();
        String[] str = {"Yes", "No"};
        if (a.maxNConfigs < a.capacity) {
          TextDialog dg3 = new TextDialog("More than "+a.maxNConfigs+" configurations\nhave been generated.\nContinue?", str, "Warning"); 

	  if (dg3.findResult().equals("Yes"))
	      a.maxNConfigs *= 2;
	  else 
	      quit();
        } else {
          OKDialog dg4 = new OKDialog("More than "+a.capacity+" configurations\n have been generated.\nUnable to continue processing.", this, " ");
          dg4.findResult();
          quit(); 
        }
        break;
      }  // end switch
    setEnabled(true);
  }


  private void quit() {
    parent.setEnabled(true);
    parent.d.changeAspect(false);
    setVisible(false);
  }

 
  private void kill() {
    int			i = 0;
    Configuration	c;
    State		s;

    while (i < a.currentConfigs.size()) {
      c = (Configuration) a.currentConfigs.elementAt(i);
      if (c.isSelected)
        a.currentConfigs.removeElement(c);
      else
        i++;
    }

    for (Enumeration e = parent.d.theStates.elements(); e.hasMoreElements(); ) {
      s = (State) e.nextElement();
      s.ss = State.NORMAL;
    }

   for (Enumeration e = a.currentConfigs.elements(); e.hasMoreElements(); ) {
      c = (Configuration) e.nextElement();
      if (c.isValid)
        c.theState.ss = State.FOCUSED;
    }
  
  }	// end of method kill.

  private void freeze() {
    Configuration c;
  
    for (Enumeration e = a.currentConfigs.elements(); e.hasMoreElements(); ) {
      c = (Configuration) e.nextElement();
      if (c.isSelected && c.isValid) {
        c.isFrozen = true;
      }
      c.isSelected = false;
    }
  }

  private void thaw() {
    Configuration c;
  
    for (Enumeration e = a.currentConfigs.elements(); e.hasMoreElements(); ) {
      c = (Configuration) e.nextElement();
      if (c.isSelected) {
        c.isFrozen = false;
      }
      c.isSelected = false;
    }
  }
   
  private void trace() {
    int			countSelected = 0;
    Configuration	c;
    Configuration	selected = null;

    for (Enumeration e = a.currentConfigs.elements(); e.hasMoreElements(); ) {
      c = (Configuration) e.nextElement();
      if (c.isSelected) {
        countSelected++; 
        selected = c;
      }
    }
    if (countSelected != 1) {
      OKDialog dg = new OKDialog("Exactly one configuration must be\n selected for trace", this, "Error");
      dg.show();
    } else {
      TraceWindow tw = new TraceWindow(a, selected, this, "Trace");
      tw.show();
    }
  }   

  private void restart() {
    for (Enumeration e = a.d.theStates.elements(); e.hasMoreElements(); ) {
      ((State) e.nextElement()).ss = State.NORMAL;
    } 
    a.init();
  }
   
    /* 
     * starts a function thread with the given function inside
     */
    public void  startThread(String functionName) {
	FunctionThread ft;
	Class source;
	try {
	    source = this.getClass();
	    ft = new FunctionThread(source.getMethod(functionName, new Class[0]), this, new Object[0]);
	    ft.start();
	}
	catch(NoSuchMethodException h) {
	    System.out.println("no " + functionName + " method found.");
	}
    }


}	// end of class UpperWindow.


/**
 * This is the canvas in the Step Run Window in which the configurations
 * are displayed.
 *
 * @see		flap.UpperWindow
 * @see		flap.Configuration
 * @author	Cecilia & Octavian Procopiuc
 * @version	1.0 15 July 1996
 */
class ConfigurationCanvas extends Canvas implements MouseListener{
  Machine	a;
  int		hCount;
  int		vCount;
  Environment	parent;
  Image		offscr;
  Graphics	offg;

  public ConfigurationCanvas(Machine a, Environment parent) {
    this.a = a;
    this.parent = parent;
    this.addMouseListener(this);
    setFont(Params._c_font);
    Configuration.setParams(Params._c_width, Params._c_height, 11);
  }
    
  public void mousePressed(MouseEvent e) 
  {
    Configuration c;
    
    if ((c = isInConfiguration(e.getX(), e.getY())) != null) {
      c.isSelected = !c.isSelected;
      repaint();
    }
  }      
  public void mouseReleased(MouseEvent e) 
  {}
  public void mouseEntered(MouseEvent e) 
  {}
  public void mouseExited(MouseEvent e) 
  {} 
  public void mouseClicked(MouseEvent e) 
  {}

  public void paint(Graphics g) {
    update(g);
  } 

  public void update(Graphics g) {

    Dimension	d = getSize();
    Configuration c;

    if (offscr == null) {
      offscr = createImage(d.width, d.height);
      offg = offscr.getGraphics();
    }

    offg.setColor(Params._c_backcolor);
    offg.fillRect(0, 0, d.width, d.height);

    offg.setColor(Params._c_forecolor);
    offg.draw3DRect(0, 0, d.width-1, d.height-1, true);

    setLimits();

    for (int i = 0; i < a.currentConfigs.size(); i++) {
      Point p = locateConfiguration(i);
      c = (Configuration) a.currentConfigs.elementAt(i);
      c.paint(offg, p.x, p.y);
    }
    g.drawImage(offscr, 0, 0, null);

  }	// end of method update.

// Checks if point (x, y) is in a configuration.
// If so, returns that configuration.
  private Configuration isInConfiguration(int x, int y) {
    Configuration	c = null;
    boolean		done = false;
    int			i = 0;
    int			sz = a.currentConfigs.size();
    Point		p;

    while (i < sz && !done) {
      c = (Configuration) a.currentConfigs.elementAt(i);
      p = locateConfiguration(i++);
      if (x >= p.x && y >= p.y && x < p.x+Configuration.w && y < p.y+Configuration.h)
        done = true;
    }

    return done?c:null;
  }	// end of method isInConfiguration.

// Sets number of configurations on a row and column.
  private void setLimits() {
    Dimension d = getSize();

    hCount = (int) Math.floor(d.width / Configuration.w);
    vCount = (int) Math.floor(d.height / Configuration.h);
  }

// Locates the x,y-coordinates of configuration number i.
  private Point locateConfiguration(int i) { 
    int x = Configuration.w * (i % hCount);
    int y = Configuration.h * (int) Math.floor(i / hCount);
    return new Point(x, y);
  }

} // end of class ConfigurationCanvas
