// NFA.java
// Implement a JFrame in which to draw a NFA

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import java.util.*;		// for iterator

/** Nondeterministic Finite Automaton, with graphic paint method
 *  implemented as a JFrame window.
 *  Permits user to build an NFA, with states and transitions, using menu.
 *
 *  @author <a href="mailto:bergmann@rowan.edu">Seth Bergmann and Greg Safko </a>
 * 
 */
public class NFA extends JFrame
	implements ActionListener, MouseListener
{

/** This NFA knows which state, if any, is selected
 */
public State selectedState = null;
/** User is in the process of adding a transition; a state has already
 *  been selected, and the user will then click on the target state.
 */
public boolean addingTrans;		// currently adding a transition

State fromState;				// remember origin of a transition
 
char transChar;					// a transition's input symbol
TransPanel transPanel;			// Panel to get a transition

/** The starting state of this NFA
 */
public State start;

/** An array of the states contained in this NFA.
 */
public State [] states;

/** The number of states contained in this NFA.
 */
public int nStates = 0;
private static int maxStates = 100;

/** Create an NFA, titled "NFA", with null layout manager
 */
public NFA ()
{  super ("NFA");
   setSize (600,400);
   getContentPane().setLayout (null);			// turn off layout mgr.
   setDefaultCloseOperation (EXIT_ON_CLOSE);
   setUpMenu();
   transPanel = new TransPanel (this);
   transPanel.setVisible (false);
   states = new State [maxStates];
   WindowUtilities.openInJFrame (transPanel, 200, 175);
}

String [] buildActions = 		// actions for the build menu
    {	
    "Add State ...",
	"Delete State ...",
	"Modify State ...",
	"Add Transition ...",
	"Delete Transition ...",
	"Show Regular Expression..."
    };

/** Create the menus and add them to this NFA
 *  Open, Save not yet implemented.
 *  Delete State, Delete Transition not yet implemented
 */
public void setUpMenu()
{  	JMenuItem menuItem;
	JMenuBar menuBar = new JMenuBar();
	JMenu fileMenu = new JMenu ("File");
	JMenu buildMenu = new JMenu ("Build NFA");
	fileMenu.add ("Open ...");
	fileMenu.add ("Save");
	fileMenu.add ("Save As ...");
	for (int i=0; i<buildActions.length; i++)
	{	menuItem = new JMenuItem (buildActions[i]);
		buildMenu.add (menuItem);
		menuItem.addActionListener (this);
	}
	menuBar.add (fileMenu);
	menuBar.add (buildMenu);
	setJMenuBar (menuBar);
	fileMenu.addActionListener (this);
	buildMenu.addActionListener (this);
}

/** Implement the menu actions
 *  Open, Save not yet implemented.
 *  Deletion not yet implemented.
 *  @param evt The menu event which invoked this method
 */
public void actionPerformed (ActionEvent evt)
{  if (evt.getActionCommand() == "Open ...") openFile();
   if (evt.getActionCommand() == "Save")     saveFile();
   if (evt.getActionCommand() == "Save As ...")     saveFileAs();
   for (int i=0; i<buildActions.length; i++)
	if (evt.getActionCommand().equals (buildActions[i]))
	  switch (i)
	    {	
	    case 0: addState(); 	break;
		case 1: delState();		break;
		case 2: modifyState();	break;
		case 3: addTrans();		break;
		case 4: delTrans();		break;
		case 5: showRE();		break;
	    }
}

/** Add a state to this NFA
 */
void addState()
{
    if(nStates < maxStates)
    {
		State s;
		s = new State(100,100,this);
		getContentPane().add (s);
		states [nStates] = s;
		s.index = nStates;
		nStates++;
		repaint();
	}
   	else 
   		System.out.println ("Too many states.  Max is "+ maxStates);  
}

// Allow the user to set the attributes of the selected State.
//  Eventually, grey out modify if no state is selected.
void modifyState()
{
	if (selectedState!=null) selectedState.setAttributes();
	repaint();
}

/** Add a transition to a state of this NFA.  A state should be selected first, by
 * clicking on it with the mouse.
 */
void addTrans()
{
	transPanel.setVisible (true);
	fromState = selectedState;
	addingTrans = true;
}

/** Delete a transition from this NFA
 */
void delTrans()
{
}

/** Show the regular expression corresponding to this NFA on stdout
 */
public void showRE()
{  
   Union u = new Union (new Empty(), new Empty());
   Union newu = u;
   Iterator it = start.transitions.iterator();
   Transition trans;
   while (it.hasNext())
   {
   	trans = (Transition) it.next();
   	if(trans.toState.isFinal)
   	{
   	newu.setRight(new Union( r(start.index,trans.toState.index, nStates - 1), new Empty()));
   	newu = (Union) u.getRight();
   	}
   }
   System.out.println ("result unreduced is : " + u);
   System.out.println ("result reduce is    : " + u.reduce());
   // Show the Regular Expression
   String reString = new String();
   String actionString = new String();
   String filename = new String();
   filename = JOptionPane.showInputDialog("Enter a file name (for lex input)");
   actionString = JOptionPane.showInputDialog("Enter an action for this NFA");
   reString = u.toString();
   System.out.println(reString);	
   PrintWriter lexfile = null;
   try 
	{
		lexfile = new PrintWriter(new FileOutputStream(filename));
	}
   catch(FileNotFoundException ex)
	{
		System.out.println("Error opening the output file");
		System.exit(0);	
	}
	lexfile.println("%%");
	lexfile.println(reString + " " + actionString);
	lexfile.println("%%");
	lexfile.close();
	System.exit(0);
  
}

/** The recursive algorithm from Hopcroft & Ullman 
 * @param i Origin state
 * @param j Destination state
 * @param k Limiting state
 * @return A regular expression for all strings which will take
 * the machine from state i to state j, without going through
 * a state higher than state k.  State numbers begin with 0.
 */
public Expr r (int i, int j, int k)
   {	
	if (k==-1)			// base case
	   {
	   	Iterator it = states[i].transitions.iterator();
	   	Union u = new Union(new Empty(), new Empty());
	    Transition trans;
	    Union newu = u;
	    while (it.hasNext())
	    {	
	    	trans = (Transition) it.next();
			if (trans.toState.index == j)
			{
				newu.setRight(new Union(new Alpha( new Character (trans.inp)), new Empty()));
				newu = (Union) newu.getRight();	
			}  
		}
		if (i==j) 
			newu.setRight(new Epsilon());
		return u.reduce();
		}	// end k==-1
  
 
return new Union(new Concat((new Concat(r(i,k,k-1).reduce(), new Star(r(k,k,k-1).reduce())).reduce()), r(k,j,k-1).reduce()).reduce().reduce(), r(i,j,k-1)).reduce();

   }

/** Delete a State from this  NFA.  Not yet implemented.
 */
void delState()
{
}

/** Open a new NFA, not yet implemented.
 */
void openFile()
{
}

/** Save this NFA, not yet implemented.
 */
void saveFile()
{
}

/** Save this NFA, with a new name, not yet implemented.
 */
void saveFileAs()
{
}

/** Handle the Mouse clicked event if on a state
 */
public void mouseClicked (MouseEvent evt)
{
}

/** Handle the Mouse entered event if on a state
 */
public void mouseEntered (MouseEvent evt)
{
}

/** Handle the Mouse exited event if on a state
 */
public void mouseExited (MouseEvent evt)
{
}

/** Handle the Mouse pressed event if on a state
 */
public void mousePressed (MouseEvent evt)
{
}

/** Handle the Mouse released event if on a state
 */
public void mouseReleased (MouseEvent evt)
{
}

/** Display all the components of this NFA on Graphics g
 *  For each state, check to see if it is a start and/or final state.
 *  Also display all the transitions from the state.
 */
public void paint (Graphics g)
{   super.paint (g);
    Graphics2D graphics2D = (Graphics2D) g;

    JComponent comp;
    Color oldColor;
    Arrow startArrow;		// for start state
    State state;
    int i;
    for (i=0; i<getContentPane().getComponentCount(); i++)
      {	comp = (JComponent) getContentPane().getComponent(i);
	oldColor = graphics2D.getColor();
	if (comp instanceof JLabel)
		graphics2D.setColor (Color.magenta);		// magenta?
	if (comp instanceof State)				// kludge for start states
	  { state = (State) comp;
	    if (state.isStart)
	     {	startArrow = new Arrow (state.locX, state.locY, 
				state.locX+state.r/1.414, state.locY+state.r/1.414,
				Color.blue);	
		startArrow.paint (graphics2D);
	     }
	   state.moveLabels(); 			// adjust the positions of the labels
						// on the transitions
          }
	comp.paint(graphics2D);		// paint each component
	graphics2D.setColor (oldColor);
      }
}


}

