/* ---------------------------------------------------------------
 File:                           Transition.java
 Package:                        FLAP
 Author:                         Dr. Ratnesh Kumar and Greg Stamp
 V2.0 10/28/2006
 Initial Developers: Yana Ong, Bharath Oruganti and Magda & Octavian Procopiuc
 ---------------------------------------------------------------*/
 
/*
 * Dr. Ratnesh Kumar, Greg Stamp.
 * Department of Electrical and Computer Engineering
 * Iowa State University
 * October, 2006.
 *
 * Copyright (c) 2006
 * All rights reserved.
 */

package flap;


import java.awt.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import mainAnalyzer.*;


/**
 * This is the main window associated with each machine that the user
 * opens. It contains the Desktop in the middle -- where the machine 
 * is displayed and 
 * edited --, a status bar at the bottom, an input string bar  
 * above the Desktop, and a menu bar at the top. 
 * It also contains the actual machine.
 */

public class Environment extends Frame {

	static final long serialVersionUID = 1L;
    /**
     * The type of the machine.
     */
    //public int		type;

    /**
     * The name of the file containing the current machine.
     */
    public String		fileName = NO_FILE_NAME;

    /**
     * The desktop.
     */
    public Desktop	d;

    /**
     * The machine.
     */
    public Machine	a;

    /**
     * The bottom status line.
     */
    public Label		statusLine;

    /**
     * The acceptance choice for PDA.
     */
    public Choice		acceptanceChoice;

    /**
     * The Container which opened this Environment.
     * Can be either MainMenu or FLAPApplet.
     */
    protected Container	mainmenu;

    /**
     * The current directory for opening/saving.
     */
    public String		currentDir = null;

    /**
     * When isApplet is set, some items in the menu bar are disabled.
     */
    public boolean	isApplet = false;
    boolean 	done = false;

    public final static String	CLOSE_STR = "Close";
    public final static String	QUIT_STR = "Quit Designer";
    public final static String	HLP_STATE_STR = "States";
    public final static String	HLP_TRANSITION_STR = "Transitions";
    public final static String	HLP_OVERVIEW_STR = "Overview";
    public final static String	HLP_TRACEWINDOW_STR = "Trace Window";
    public final static String	NO_FILE_NAME = "(no name)";

    final static String	SAVE_STRING = "Save";
    final static String	NOTSAVE_STRING = "Don't Save";
    final static String	CANCEL_STRING = "Cancel";
    final static String[]	SAVE_BUTTONS = {
        SAVE_STRING, NOTSAVE_STRING,
        CANCEL_STRING};

    final static int	NEW = 0;
    final static int	OPEN = 1;
    final static int	FASTRUN = 2;
    final static int	PRINT = 3;
    final static int	CLOSE = 4;
    final static int	SHOWNONDETS = 5;
    final static int  OPENGRAPHIC = 6;
    final static int  SAVE = 7;
    final static int  SAVEAS = 8;
    final static int  SHOWASTEXT = 9;
    final static int  CONTMASKTABLE = 10;
    final static int  QUIT = 11;
    final static int  NONE = 99;

    static String MI_NEW = "New",
            MI_OPEN = "Open As Text...",
            MI_OPEN_GRAPHIC = "Open As Graphic...",
            MI_SAVE = "Save",
            MI_SAVEAS = "Save As...",
            MI_SHOWASTEXT = "Show Text",
            CONT_MASK_TABLE = "Controllability/Masking Table";
  
    private Menu		filemenu;

    /* constructor modified to include Desktop - this is used by FileReader to pass desktop it already created - Yana. */
    public Environment(String aFileName, Container mainmenu, boolean isApplet, Desktop desktop) {
        super();
        setLocation(mainmenu.getX() + 40, 20);
        this.isApplet = isApplet;
        this.mainmenu = mainmenu;

        setBackground(Params._e_backcolor);
        if (desktop == null) {
            d = new Desktop();
        } else {
            d = desktop;
        }
        add("Center", d);
        a = new FSA(d);

        statusLine = new Label("", Label.LEFT);
        add("South", statusLine);
        Panel inputLine = new Panel();

        add("North", inputLine);
        putMenuBar();
        if (!aFileName.equals(NO_FILE_NAME)) {	
            String str = aFileName;
	
            fileName = null;
            fileName = str;
            try {
                open(aFileName);
                updateStatus("Mail any bugs or comments to: rkumar@iastate.edu");
            } catch (Exception e) {
                updateStatus("Could not open file " + str + ". New file.");
            }
        }
        setIconImage(Params._img_icon);
    }

    /* this is the old constructor - modified to just call above constructor with desktop=null - Yana. */
    public Environment(String aFileName, Container mainmenu, boolean isApplet) {
        this(aFileName, mainmenu, isApplet, null);
    }

    public Dimension getgetPreferredSize() {
        return new Dimension(Params._e_width, Params._e_height);
    }

    /**
     * Constructs the menu bar.
     */
    private void putMenuBar() {
        MenuBar 	mb = new MenuBar();
        Menu	m;
        MenuItem	item;

        mb.setFont(Params._m_font);
        m = new Menu("File");
        filemenu = m;
        m.setFont(Params._m_font);
        m.add(new MenuItem(MI_NEW));
        m.add(item = new MenuItem(MI_OPEN));
        if (isApplet) {
            item.disable();
        }
        m.add(item = new MenuItem(MI_OPEN_GRAPHIC));
        if (isApplet) {
            item.disable();
        }
        m.add(item = new MenuItem(MI_SAVE));
        if (isApplet) {
            item.disable();
        }
        m.add(item = new MenuItem(MI_SAVEAS));
        if (isApplet) {
            item.disable();
        }
        m.addSeparator();
        m.add(item = new MenuItem(CLOSE_STR));
        if (isApplet) {
            item.disable();
        }
        mb.add(m);

        m = new Menu("View");
        m.add(item = new MenuItem(MI_SHOWASTEXT));
        if (isApplet) {
            item.disable();
        }
        mb.add(m);
	
        m = new Menu("Table");
        m.add(item = new MenuItem(CONT_MASK_TABLE));
        if (isApplet) {
            item.disable();
        }
        mb.add(m);

        m = new Menu("Help");
        m.setFont(Params._m_font);
        m.add(HLP_OVERVIEW_STR);
        m.add(HLP_STATE_STR);
        m.add(HLP_TRANSITION_STR);
        mb.add(m);	
        mb.setHelpMenu(m);

        setMenuBar(mb);
    }	// end of method putMenuBar.

    public boolean handleEvent(Event e) {
        Thread actionThread;

        if (e.id == Event.WINDOW_DESTROY) {
            actionThread = new DialogThread(this, CLOSE);
            actionThread.start();  
        } else if (e.id == Event.MOUSE_UP || e.id == Event.KEY_RELEASE) {
            updateStatus();
        }
        return super.handleEvent(e);
    }

    public boolean action(Event e, Object arg) {
        Thread actionThread;

         if (e.target instanceof CheckboxMenuItem) {
            if (arg.equals("Auto re-label states")) {
                d.autoRelabel = ((CheckboxMenuItem) e.target).getState();
                d.relabel();
            }
        } else if (e.target instanceof MenuItem) {
            if (arg.equals(MI_NEW)) {
                actionThread = new DialogThread(this, NEW);
                actionThread.start();  
            } else if (MI_OPEN.equals(arg)) {
                actionThread = new DialogThread(this, OPEN);
                actionThread.start();  
            } else if (MI_OPEN_GRAPHIC.equals(arg)) {
                actionThread = new DialogThread(this, OPENGRAPHIC);
                actionThread.start();
            } else if (MI_SAVEAS.equals(arg)) {
                actionThread = new DialogThread(this, SAVEAS);
                actionThread.start();
            } else if (MI_SAVE.equals(arg)) {
                actionThread = new DialogThread(this, SAVE);
                actionThread.start();
            } else if (QUIT_STR.equals(arg)) {
                actionThread = new DialogThread(this, QUIT);
                actionThread.start();
            } else if (CLOSE_STR.equals(arg)) {
                actionThread = new DialogThread(this, CLOSE);
                actionThread.start();  
            } else if (MI_SHOWASTEXT.equals(arg)) {
                actionThread = new DialogThread(this, SHOWASTEXT);
                actionThread.start();
            } else if (CONT_MASK_TABLE.equals(arg)) {
                if ((fileName != null)
                        && (fileName.toString().trim().equals("") == false)
                        && (fileName.toString().equals(NO_FILE_NAME) == false)) {
                    actionThread = new DialogThread(this, CONTMASKTABLE);
                    actionThread.start();
                } else {
                    JOptionPane.showMessageDialog(this,
                            "A FSM file has to be opened before running this.",
                            "No FSM file", JOptionPane.ERROR_MESSAGE);
                }
            } else if ("Re-label states".equals(arg)) {
                relabel();
            } else if (HLP_STATE_STR.equals(arg)) {
                help(HelpDialog.STATE );
            } else if (HLP_TRANSITION_STR.equals(arg)) {
                help(HelpDialog.TRANSITION);
            }  else if (HLP_OVERVIEW_STR.equals(arg)) {
                help(HelpDialog.OVERVIEW);
            } else {
                OKDialog dg = new OKDialog("This feature is not implemented",
                        this, "Sorry...");

                dg.setVisible(true);
            }
        }
        repaint();
        return super.action(e, arg);

    }	// end of method action.

    /**
     * Relabels the states by calling relabel from the Desktop.
     */
    public void relabel() {
        a.d.relabel();
        a.d.repaint();
        updateStatus("Relabel completed.");
    }

    /**
     * Removes the current desktop and opens a brand new one.
     */
    void newone() {
        if (!testSavedAndProceed()) {
            return;
        }
        remove(d);
        d = new Desktop();
        a = new FSA(d);
        
    }

    /**
     * Shows the help dialog.
     */
    public void help(int helpOnWhat) {
        HelpDialog hd = new HelpDialog(helpOnWhat, this);

        hd.pack();
        hd.setVisible(true);
    }


    /**
     * Shows a FileDialog and tries to open the selected file as text.
     */
    public boolean openText(String filename) {
        boolean answer = true;
        InputFrame ip;

        // MainMenu flapMainMenu = null;
    
        if (!testSavedAndProceed()) {
            return false;
        }
      
        if (filename == null) // if no filename is specified, ask user for one
        {
            FileDialog fd = new FileDialog(this, "Open " + "FA",
                    FileDialog.LOAD);

            fd.setBackground(Params._dg_backcolor);

            /* preferably start from data directory - Yana */
            if (!MainMenu.workingDirectory.endsWith(MainMenu.fileSeparator)) {
                fd.setDirectory(
                        MainMenu.workingDirectory + MainMenu.fileSeparator
                        + "../data/");
            } else {
                fd.setDirectory(MainMenu.workingDirectory + "../data/");
            }

            fd.setVisible(true);
            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;
            filename = strDir + strFile;
        }
    
        if (filename.equals(NO_FILE_NAME)) {
            JOptionPane.showMessageDialog(this, "No file is opened.", "Error",
                    JOptionPane.ERROR_MESSAGE);
        } else {
            ip = new InputFrame(new Input(Analyzer.flapMainMenu), filename,
                    filename);
            ip.setLocation(d.getX() + 60, d.getY() + 40);
        }
        return answer;
    }

    /* Open a file as graphic - Yana
     * @param filename filename of text file to be opened as graphic
     */
    public boolean OpenGraphic() {

        if (!testSavedAndProceed()) {
            return false;
        }
	
        FileDialog fd = new FileDialog(this, "Open " + "FA",
                FileDialog.LOAD);

        fd.setBackground(Params._dg_backcolor);

        /* preferably start from data directory - Yana */
        if (!MainMenu.workingDirectory.endsWith(MainMenu.fileSeparator)) {
            fd.setDirectory(
                    MainMenu.workingDirectory + MainMenu.fileSeparator
                    + "../data/");
        } else {
            fd.setDirectory(MainMenu.workingDirectory + "../data/");
        }

        fd.setVisible(true);
        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;
        }
        String aName = strDir + strFile;
        MyFileReader fileReader = new MyFileReader();

        try {
            fileReader.read(aName); 
 
            if (fileReader.a != null) { // just in case...
                fileReader.d.setSize(this.getSize());
                a = fileReader.a;
                remove(d);
                d = a.d;
                add("Center", d);
                fileName = null;
                fileName = aName;
            }	  
            
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return false;
        } 
        return true;
    }// end of OpenGraphic

    /* Ranga Code */
    public void writefiletoTextArea(String aName) throws Exception {}

    /**
     * Tries to open the given file in graphic format.
     */
    public void open(String aName) throws Exception {
    
        MyFileReader fr = new MyFileReader();

        fr.read(aName);
        if (fr.a != null) { // just in case...
            a = fr.a;
            remove(d);
            d = a.d;
            add("Center", d);
        }
    }

    /**
     * Tries to save the current machine.
     */
    public boolean save() {
        String error = a.checkMachine();

        if (error != null) {
            OKDialog dg = new OKDialog(error + "\nOperation canceled.", this, 
            		"FA" + " Save Error");

            dg.setVisible(true);
            return false;
        }
        if (NO_FILE_NAME.equals(fileName)) {
            return saveAs();
        } else {
            return save(fileName);
        }
    }

    /**
     * Opens a FileDialog and tries to save the current machine
     * in the specified file.
     */
    public boolean saveAs() {
        FileDialog fd = new FileDialog(this, "Save " + "FA",
                FileDialog.SAVE);

        /* hardcoded to start from "data" directory - Yana */
        if (!MainMenu.workingDirectory.endsWith(MainMenu.fileSeparator)) {
            fd.setDirectory(
                    MainMenu.workingDirectory + MainMenu.fileSeparator
                    + "../data/");
        } else {
            fd.setDirectory(MainMenu.workingDirectory + "../data/");
        }

        fd.setBackground(Params._dg_backcolor);
        fd.setVisible(true);
        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);
    }

    /**
     * Tries to save the current machine in the file given
     * by the name.
     */
    public boolean save(String aName) {
        boolean answer = false;
        FileWriter fw = new FileWriter(a);

        try {
            if (aName.trim().endsWith(MainMenu.fileSeparator)) {
                throw (new Exception("Not a valid file name.\n"));
            }

            fw.write(aName);
            fileName = null;
            fileName = aName;
            a.d.modified = false;
            updateStatus("File saved");
            answer = true;
        } catch (IOException e) {
            OKDialog dg = new OKDialog(
                    "I/O Error.\nFile not saved.\n" + e.getMessage(), this,
                    "FA" + " Save Error");

            dg.setVisible(true);
        } catch (NoSuchElementException e) {
            OKDialog dg = new OKDialog(
                    "Tokenizer Error.\nFile not saved.\n" + e.getMessage(), this,
                    "FA" + " Save Error");

            dg.setVisible(true);
        } catch (Exception e) {
            OKDialog dg = new OKDialog(
                    "File Error.\nFile not saved.\n" + e.getMessage(), this,
                    "FA" + " Save Error");

            dg.setVisible(true);
        }
        return answer;
    }	// end of method save(String).

    public void setEnabled(boolean val) {
        super.setEnabled(val);
        filemenu.setEnabled(val);
    }

    public void quit() {
        mainmenu.postEvent(new Event(this, Event.ACTION_EVENT, QUIT_STR));
    }

    public void close() {
        if (!testSavedAndProceed()) {
            return;
        }
        mainmenu.postEvent(new Event(this, Event.ACTION_EVENT, CLOSE_STR));
    }

    /**
     * Updates the title and the status line.
     */
    public void updateStatus() {
        String typeInTitle = "Finite State Automaton";
        String strModified = (a.d.modified) ? "(modified)" : "";

        setTitle(typeInTitle + ": " + fileName + " " + strModified);
        statusLine.setText("");
    }

    /**
     * Updates the title and the status line.
     * @param message	the new status line.
     */
    public void updateStatus(String message) {
        updateStatus();
        statusLine.setText(message);
    }

    /**
     * Tests if the current machine is modified,
     * asks to save it if it is  modified.
     * @return	false if the user pushed the cancel button, true otherwise.
     */
    public boolean testSavedAndProceed() {
        boolean answer = true;

        if (isApplet) {
            answer = true;
        } else if (a.d.modified) {
            TextInDialog dg = new TextInDialog("Current machine is not saved",
                    SAVE_BUTTONS, this, "FA" + " Warning");
            Thread dt = Thread.currentThread();

            dg.setDialogThread(dt);
            dg.setLocation(mainmenu.getX() + 65, 100); // added	
            dg.setVisible(true);

            if (dg.getPushedButton().equals(ThreadDialog.NOT_YET)) { 
                dt.suspend();
            }
            String pushed = dg.getPushedButton();

            if (pushed.equals(SAVE_STRING)) {
                if (!save()) {
                    answer = false;
                }
            } else if (pushed.equals(CANCEL_STRING)) {
                answer = false;
            }
 
        }
        return answer;
    }	// end of method testSavedAndProceed.

    /**
     * Should be removed when the bug regarding non-modal
     * dialogs is fixed.
     */
    public void threadAction(int id) {
    
        switch (id) {
        case OPEN:
            openText(null); // open file in text format
            break; 

        case CLOSE:
            close();
            break;

        case NEW:
            newone();
            break;

        case OPENGRAPHIC:
            OpenGraphic();
            break;
	
        case SAVE:
            save();
            break;

        case SAVEAS:
            saveAs();
            break;
	    
        case QUIT:
            quit();
            break;
	    
        case SHOWASTEXT:
            openText(fileName); // open the shown graphic file in its text format
            break;
		
        case CONTMASKTABLE:
            if (testSavedAndProceed()) {
                InputFileFrame iff = new InputFileFrame(this, fileName, a);
            }
            break;

        default: 
            System.out.println("threadAction is called with id = " + id);
            break;
        }
    }  // end of method threadAction

}	// end of class Environment.


class ShowOutputFile extends Frame // using inputframe now...
{
	static final long serialVersionUID = 1L;
	TextArea tf;
    String  str = new String();
    StringBuffer currFile = new StringBuffer();

    public ShowOutputFile(String FileName) {
        super("Textual Representation of State Machine:" + FileName);
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();

        setLayout(gridbag);
        c.gridx = 6;
        c.gridy = 8;
        tf = new TextArea(10, 30);
        gridbag.setConstraints(tf, c);
        add(tf);
        readFile(FileName);
        setSize(300, 300);
    }
   
    protected String parseFileName(String fullPathName) {
        int idx = fullPathName.lastIndexOf(mainAnalyzer.Analyzer.fileSeparator);

        return fullPathName.substring(idx + 1);
    }

    // Read a local file
    protected void readFile(String filename) {
        try {
            new ReadFile(filename, tf);
            currFile.setLength(0);
            currFile.append(filename);
        } catch (IOException e) {
            System.out.println("Exception in readFile: " + e.toString());
        } catch (Exception e1) {
            System.out.println("Exception in readFile: " + e1.toString());
        }
    }

    public boolean handleEvent(Event evt) {
        if (evt.id == Event.WINDOW_DESTROY) { 
            this.setVisible(false);
            this.dispose();
        }
        return true;
    }
}// end of class ShowOutputFile


class ReadFile {
    StringBuffer sb = new StringBuffer();

    ReadFile(String FileName, TextArea taText)
        throws IOException,
                FileNotFoundException {
        FileInputStream fis;

        fis = new FileInputStream(FileName);
        int oneChar;

        while ((oneChar = fis.read()) != -1) {
            sb.append((char) oneChar);
        }
        fis.close();
        taText.setText(sb.toString());
    }
}
