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

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


/**
 * this is the window where the user inputs a grammar and then can transform 
 * it, load it, or save it.
 */

class GrammarInputWindow extends SourceWindow implements ActionListener {

  protected int myNumLines;
  protected Vector myLeftFields;
  protected Vector myRightFields;
    
  protected Button myCFGtoPDAButton;
  protected Button myCFGtoPDAButtonLR;
  protected Button myGNFtoPDAButton;
  protected Button myregtoFSAButton;

  protected Panel  myScrollPanel;
  protected GridLayout myFieldGrid;
  protected Panel myFieldPanel;
  protected GridLayout myNumberGrid;
  protected Panel myNumberPanel;
  
  protected Menu myFileMenu;
  protected Menu myOptionMenu;
  
  protected Vector envTarget;
  
    /**
     * default constructor
     */

  public GrammarInputWindow(String filename, MainMenu mainmenu, boolean isApplet) {
    super(mainmenu, isApplet);
    
    myNumLines = 10;
    myLeftFields = new Vector();
    myRightFields = new Vector();
    envTarget = new Vector();
    
    ScrollPane topScroll = new ScrollPane();
    topScroll.setSize(300, 300);
    Panel buttonPanel = new Panel(new GridLayout(2,2));	
    Separator sep = new Separator();
    add(topScroll, "North");
    add(buttonPanel, "South");
    
    Panel pan = new Panel(new FlowLayout());
    pan.add(sep);
    sep.setSize(300,2);
    pan.getInsets().top = 20;
    pan.getInsets().bottom = 20;
    add(pan, "Center");
    
    setTitle("Grammar Input Window");
    
    myScrollPanel = new Panel(new FlowLayout());
    pan = new Panel(new BorderLayout());
    myScrollPanel.add(pan);
    myFieldPanel = new Panel(myFieldGrid = new GridLayout(10,1));
    myNumberPanel = new Panel(myNumberGrid = new GridLayout(10,1));
    pan.add(myFieldPanel, "Center");
    pan.add(myNumberPanel, "West");
    topScroll.add(myScrollPanel);
    
    Panel p;
    TextField t1, t2;
    for(int i=0; i< myNumLines; i++)
    {
	p = new Panel(new FlowLayout());
	t1 = new OneLetterTextField();
	t2 = new TextField(10);
	myNumberPanel.add(new Label( (i+1) + ": "));
	p.add(t1);
	p.add(new ArrowCanvas(20, 10));
	p.add(t2);
	myLeftFields.addElement(t1);
	myRightFields.addElement(t2);
	myFieldPanel.add(p);
    }
    
    myCFGtoPDAButton = new Button("CFG to PDA (LL)");
    buttonPanel.add(myCFGtoPDAButton);
    myCFGtoPDAButton.addActionListener(this);
    
    myCFGtoPDAButtonLR = new Button("CFG to PDA (LR)");
    buttonPanel.add(myCFGtoPDAButtonLR);
    myCFGtoPDAButtonLR.addActionListener(this);
    
    myGNFtoPDAButton = new Button("GNF to PDA");
    buttonPanel.add(myGNFtoPDAButton);
    myGNFtoPDAButton.addActionListener(this);

    myregtoFSAButton = new Button("regular to FSA");
    buttonPanel.add(myregtoFSAButton);
    myregtoFSAButton.addActionListener(this);
    
    addMenuBar();
    
    addWindowListener(
	new WindowAdapter()
	{
	    public void windowClosing(WindowEvent ev)
	    {
		startThread("quit");
	    }
	} 
    );
    
    this.setSize(300, 400);
    this.setResizable(false);	
    pack();
    setVisible(false);
  }
  
  protected void addMenuBar() {
    MenuBar  mb = new MenuBar();
    Menu	 m;
    MenuItem item;
    
    mb.setFont(Params._m_font);
    
    myFileMenu = new Menu("File");

    myFileMenu.add(item = new MenuItem("Open...", new MenuShortcut('o')));
    if (isApplet) item.setEnabled(false);
    item.addActionListener(this);
    myFileMenu.add(item = new MenuItem("Save", new MenuShortcut('s')));
    if (isApplet) item.setEnabled(false);
    item.addActionListener(this);
    myFileMenu.add(item = new MenuItem("Save as...", new MenuShortcut('S',true)));
    if (isApplet) item.setEnabled(false);
    item.addActionListener(this);
    myFileMenu.addSeparator();

    myFileMenu.add(item = new MenuItem(SourceWindow.STR_CLOSE,new MenuShortcut('c')));
    item.addActionListener(this);
    myFileMenu.add(item = new MenuItem(SourceWindow.STR_QUIT, new MenuShortcut('q')));
    if (isApplet) item.setEnabled(false);
    item.addActionListener(mainmenu);
    mb.add(myFileMenu);
    
    myOptionMenu = new Menu("Options");
    myOptionMenu.add(item = new MenuItem("Clear", new MenuShortcut('l')));
    item.addActionListener(this);
    myOptionMenu.add(item = new MenuItem("Add Inputs", new MenuShortcut('a')));
    item.addActionListener(this);
    mb.add(myOptionMenu);
    
    m = new Menu("Help");
    m.add(item = new MenuItem("About", new MenuShortcut('A')));
    item.addActionListener(this);
    m.add(item = new MenuItem("Help HTML", new MenuShortcut('h')));
    item.addActionListener(this);
    m.add(item = new MenuItem("Help Text", new MenuShortcut('t')));
    item.addActionListener(this);
    mb.setHelpMenu(m);
    
    setMenuBar(mb);	
  }
  
  
  public void actionPerformed(ActionEvent h) {
    if(h.getActionCommand().equals(SourceWindow.STR_CLOSE)) 
      startThread("quit");
    else if (h.getActionCommand().equals("Open..."))
      startThread("load");
      //load();
    else if (h.getActionCommand().equals("Save"))
      save();
    else if (h.getActionCommand().equals("Save as..."))
      saveAs();
    else if(h.getActionCommand().equals("Clear"))
      clear();
    else if(h.getActionCommand().equals("Add Inputs"))
      addInputs();
    else if(h.getActionCommand().equals("Help HTML"))
    {
      helpHTML();
      mainmenu.myHelpWindow.setCurrentTopic("Grammar Input Window");
    }
    else if(h.getActionCommand().equals("Help Text"))
    {
      helpText();
      mainmenu.myHelpWindow.setCurrentTopic("Grammar Input Window");
    }
    else if(h.getActionCommand().equals("About"))
      about();
    else if(h.getActionCommand().equals("CFG to PDA (LL)"))
      startThread("doCFGtoPDA");
    else if(h.getActionCommand().equals("CFG to PDA (LR)"))
      startThread("doCFGtoPDALR");
    else if(h.getActionCommand().equals("GNF to PDA")) 
      startThread("doGNFtoPDA");
    else if(h.getActionCommand().equals("regular to FSA"))
      startThread("doregtoFSA");
  }


/**
 * shutdown the window
 */

    public boolean quit() {
	
	// check all windows generated
	Environment env;

	while(envTarget.size() > 0) {
	    env = (Environment) envTarget.elementAt(0);
	    if(!env.quit())
		return false;
	}
	
	//no need to test saved. yet.
	return super.quit();
    }



  public void setEnabled(boolean b) {
      super.setEnabled(b);
      myFileMenu.setEnabled(b);
      myOptionMenu.setEnabled(b);
  }


    /**
     * remove the given environment from the child window list.
     */
  public void removeFromTargets(Environment e) {
    envTarget.removeElement(e);
  }


    /**
     * clear all inptu fields
     */

  protected void clear() {
    TextField tf;
    for(Enumeration e = myLeftFields.elements(); e.hasMoreElements(); ) {
      tf = (TextField) e.nextElement();
      tf.setText("");
    }
    for(Enumeration e = myRightFields.elements(); e.hasMoreElements(); ) {
      tf = (TextField) e.nextElement();
      tf.setText("");
    }
  }
    
    /**
     * add 4 rules to the list 
     */
  protected void addInputs() {
    
    Panel p;
    TextField t1, t2;
    
    for(int i = 0; i < 4; i++)
    {
      p = new Panel(new FlowLayout());
      t1 = new OneLetterTextField();
      t2 = new TextField(10);
      myNumberPanel.add(new Label( (myNumLines+i+1) + ": "));
      p.add(t1);
      p.add(new ArrowCanvas(20, 10));
      p.add(t2);
      myLeftFields.addElement(t1);
      myRightFields.addElement(t2);
      myFieldPanel.add(p);
    }
    myNumLines +=4;
    
    myFieldGrid.setRows(myNumLines);
    myNumberGrid.setRows(myNumLines);
    pack();
    
  }


    /**
     * save the grammar in a file
     */

  public boolean save() {
    if (NO_FILE_NAME.equals(fileName))
      return saveAs();
    else
      return save(fileName);
  }


    /**
     * input a file name. save grammar in it
     */
  public boolean saveAs() {

    FileDialog fd = new FileDialog(this, "Save Grammar", FileDialog.SAVE);
    if (currentDir != null)
      fd.setDirectory(currentDir);
    fd.setBackground(Params._dg_backcolor);
    fd.show();
    String strFile = fd.getFile();
    String strDir = fd.getDirectory();
    if (strDir == null || strFile == null)
      return false;
    if (strFile.endsWith(".*.*"))  // ugly bug in JDK 1.0.2 for win32.
      strFile = strFile.substring(0, strFile.length() - 4);
    if (!strDir.endsWith(MainMenu.fileSeparator))
      strDir += MainMenu.fileSeparator;
    currentDir = strDir;
    String aName = strDir + strFile;
    return save(aName);

  }



    /**
     * save grammar in the given file name
     */
  public boolean save(String name) {
    boolean answer = false;
    
    try {
      if (name.trim().endsWith(MainMenu.fileSeparator))
        throw (new Exception("Not a valid file name.\n"));
      if (!name.endsWith(".GRM")) 
        name += ".GRM";
      Grammar g = makeGrammar();
      String text = g.toSaveString();
      PrintWriter ps = new PrintWriter((new FileWriter(name)), true);
      ps.println("# Grammar created by JFLAP 3.1");
      ps.println(text);
      fileName = name;
      answer = true;
    }
    catch (IOException e) {
      OKDialog dg = new OKDialog("I/O Error.\nFile not saved.\n"
		+ e.getMessage(), this, "Grammar Save Error");
      dg.show();
    }
    catch (NoSuchElementException e) {
      OKDialog dg = new OKDialog("Tokenizer Error.\nFile not saved.\n"
		+ e.getMessage(), this, "Grammar Save Error");
      dg.show();
    }
    catch (BadInputException e) {
      OKDialog dg = new OKDialog("Grammar incorrect.\nFile not saved.\n" 
		+ e.getMessage(), this, "Grammar Save Error");
      dg.show();
    }
    catch (Exception e) {
      OKDialog dg = new OKDialog("File Error.\nFile not saved.\n"
		+ e.getMessage(), this,"Grammar Save Error");
      dg.show();
    }
    return answer;
  }


/**
 * load a grammar
 */
  public void load() {
    
    FileDialog fd = new FileDialog(this, "Open Grammar", FileDialog.LOAD);
    fd.setBackground(Params._dg_backcolor);
    if (currentDir!= null)
      fd.setDirectory(currentDir);
    fd.show();
    String strFile = fd.getFile();
    String strDir = fd.getDirectory();
    if (strDir == null || strFile == null) {
	setEnabled(true);
	return;
    }
    if (strFile.endsWith(".*.*"))  // ugly bug in JDK 1.0.2 for win32.
      strFile = strFile.substring(0, strFile.length() - 4);
    if (!strDir.endsWith(MainMenu.fileSeparator))
      strDir += MainMenu.fileSeparator;
    currentDir = strDir;
    String aName = strDir + strFile;
    if (!aName.endsWith(".GRM"))
      aName += ".GRM";
    try {
      boolean	answer = false;
      GrammarFileRead	fr = new GrammarFileRead();
      
      Grammar g = fr.read(aName);
      fileName = aName;
      displayGrammar(g);
    }
    catch (FileNotFoundException e) {
      OKDialog dg = new OKDialog("Could not find file\n"+"'" + aName + "'", 
		this, "Grammar Open Error");
      dg.findResult();
    }
    catch (EOFException e) {
      OKDialog dg = new OKDialog("Unexpected end of file.", this, 
	       "Grammar Open Error");
      dg.findResult();
    }
    catch (NumberFormatException e) {
      OKDialog dg = new OKDialog("Numbers not found where\nthey were needed...",
		this, "Grammar Open Error");
      dg.findResult();
    }
    catch (IOException e) {
      OKDialog dg = new OKDialog("An I/O error occured.", this, 
		"Grammar Open Error");
      dg.findResult();
    }
    catch (BadInputException e) {
      OKDialog dg = new OKDialog("File corrupted" + e.getMessage(),
		this, "Grammar Open Error");
      dg.findResult();
    }
    setEnabled(true);
  }

    /**
     * display thr given grammar in the text fields. make sure the start symbol
     * is on the first rule
     */
  public void displayGrammar(Grammar g) {

    while(myNumLines < g.getRules().size())
      addInputs();
    
    Enumeration e, e2;
    Rule r;
    TextField t;
    String rightHandString;
    GrammarRightHandToken grt;
    int i =0;
    for(e = g.getRules().elements(); e.hasMoreElements(); ) {
      r = (Rule) e.nextElement();
      t = (TextField) myLeftFields.elementAt(i);
      t.setText(r.getLeftHand().getContents());
      
      t = (TextField) myRightFields.elementAt(i);
      rightHandString = "";
      for(e2 = r.getRightHand().elements(); e2.hasMoreElements(); ) {
        grt = (GrammarRightHandToken) e2.nextElement();
        if(grt instanceof Lambda)
          continue;
        rightHandString += grt.getContents();
        
      }
      t.setText(rightHandString);
      i++;
    }
    for( ; i < myNumLines; i++) {
      ((TextField) myLeftFields.elementAt(i)).setText("");
      ((TextField) myRightFields.elementAt(i)).setText("");
    }
    
  }



    /**
     * create a grammar from the input fields.
     */
  
  public Grammar makeGrammar() throws BadInputException {
    
    Enumeration el, er;
    TextField tfl, tfr;
    Grammar g = new Grammar();
    String gram = "";
    String left = "", right = "";
    Variable start = null;
    int i = 0;
    
    for (el = myLeftFields.elements(), er = myRightFields.elements(); 
         el.hasMoreElements() && er.hasMoreElements(); ) {
      
      i++;
      tfl = (TextField) el.nextElement();
      tfr = (TextField) er.nextElement();
      if(tfr.getText() != null)
        left = tfl.getText().trim();
      if(tfl.getText() != null)
        right = tfr.getText().trim();
      
      if(left == null || left.length() == 0)
		continue;
      
      if(start == null) {
        start = new Variable(left);
        g.setStart(start);
      }
      
      if(left.length() > 1)
        throw new BadInputException("\n\nOn rule " + i+ ":\nOnly one letter can be in the left field.");
      if(right == null)
        gram+=left+"\n";
	    else
              gram += left+right+"\n";
    }
    
    if(gram.length() == 0)
      throw new BadInputException("\n\nEmpty grammar.");
    g.readString(gram);
	return g;    
  }
  
    /**
     * coverts a cfg into a PDA in a new Enviroment.
     */
  public void doCFGtoPDA() {
    Grammar g;
    setEnabled(false);
    
    try {
      g = makeGrammar();
      CFGChecker.checkGrammar(g);
    }
    catch(BadInputException h) {
      String error = h.getMessage();
      OKDialog ok	= new OKDialog(error, this, "Error");
      ok.findResult();
      setEnabled(true);
      return;
    }
    setEnabled(true);
    Environment env = new Environment(Machine.PDA, Environment.NO_FILE_NAME, mainmenu, isApplet);
    env.pack();
    env.a.mode = Machine.CFGtoPDA;
    env.setVisible(true);
    
    Desktop temp = new Desktop(Machine.PDA);
    env.dfa = new PDA(temp);
    env.dfa.mode = Machine.CFGtoPDA;
    CFGChecker.CFGtoStartPDA(g, env.d);
    CFGChecker.CFGtoPDA(g, env.dfa.d);
    
    env.showAll.setEnabled(true);
    env.isDone.setEnabled(true);
    env.expandOne.setEnabled(false);
    env.showSolveMenu();

    envTarget.addElement(env);
    env.envSource = this;
  }
  

    /**
     * converts the current CFG into a PDA in an environment window. LR mode.
     */
  
  public void doCFGtoPDALR() {
    Grammar g;
    setEnabled(false);
    
    try {
      g = makeGrammar();
      CFGtoPDALR.checkGrammar(g);
    }
    catch(BadInputException h) {
      String error = h.getMessage();
      OKDialog ok = new OKDialog(error, this, "Error");
      ok.findResult();
      setEnabled(true);
      return;
    }
    setEnabled(true);
    Environment env = new Environment(Machine.PDA, Environment.NO_FILE_NAME, mainmenu, isApplet);
    env.pack();
    env.a.mode = Machine.CFGtoPDALR;
    env.setVisible(true);
    
    Desktop temp = new Desktop(Machine.PDA);
    env.dfa = new PDA(temp);
    env.dfa.mode = Machine.CFGtoPDALR;
    CFGtoPDALR.CFGtoStartPDA(g, env.d);
    CFGtoPDALR.CFGtoPDA(g, env.dfa.d);
    
    env.showAll.setEnabled(true);
    env.isDone.setEnabled(true);
    env.expandOne.setEnabled(false);
    env.showSolveMenu();

    envTarget.addElement(env);
    env.envSource = this;
  }


  
    /**
     * converts a greibach normal form grammar into a pda in its environment 
     * wnidow.
     */
  
  public void doGNFtoPDA() {
    Grammar g;
    setEnabled(false);
    
    try {
      g = makeGrammar();
      GNFtoPDA.checkGrammar(g);
    }
    catch(BadInputException h) {
      String error = h.getMessage();
      OKDialog ok = new OKDialog(error, this, "Error");
      ok.findResult();
      setEnabled(true);
      return;
    }
    setEnabled(true);
    Environment env = new Environment(Machine.PDA, Environment.NO_FILE_NAME, mainmenu, isApplet);
    env.pack();
    env.a.mode = Machine.GNFtoPDA;
    env.setVisible(true);
    
    Desktop temp = new Desktop(Machine.PDA);
    env.dfa = new PDA(temp);
    env.dfa.mode = Machine.GNFtoPDA;
    GNFtoPDA.GNFtoStartPDA(g, env.d);
    GNFtoPDA.GNFtoPDA(g, env.dfa.d);
    
    env.showAll.setEnabled(true);
    env.isDone.setEnabled(true);
    env.expandOne.setEnabled(false);
    env.showSolveMenu();

    envTarget.addElement(env);
    env.envSource = this;
  }


    /**
     * converts a regular grammar into an FSA
     */
  public void doregtoFSA() {
    Grammar g;
    setEnabled(false);
    
    try {
      g = makeGrammar();
      RegtoFSA.checkGrammar(g);
    }
    catch(BadInputException h) {
      String error = h.getMessage();
      OKDialog ok = new OKDialog(error, this, "Error");
      ok.findResult();
      setEnabled(true);
      return;
    }
    setEnabled(true);
    Environment env = new Environment(Machine.FSA, Environment.NO_FILE_NAME, mainmenu, isApplet);
    env.pack();
    env.a.mode = Machine.regtoFSA;
    env.setVisible(true);
   
    
    Desktop temp = new Desktop(Machine.FSA);
    env.dfa = new FSA(temp);
    env.dfa.mode = Machine.regtoFSA;
    RegtoFSA.regtoFSA(g, env.dfa.d);
   
    env.showAll.setEnabled(true);
    env.isDone.setEnabled(true);
    env.expandOne.setEnabled(true);
    env.showSolveMenu();

    env.showNameLabels.setEnabled(true);
    env.showNameLabels.setState(true);
    envTarget.addElement(env);
    env.envSource = this;

    env.d.showStateLabels = true;
    env.d.showStringLabel = true;
    env.dfa.d.showStateLabels = true;
    env.dfa.d.showStringLabel = true;
  }

    /** 
     * starts a functionThread with the given function inside
     */
  public void  startThread(String functionName) {
	FunctionThread ft;
	Class source;

	setEnabled(false);
	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.");
	}
}


}
