/*

Non-existent classes referenced:
  Interpreter
  RunTimeException

Methods to be completed:
  getFilename(String) = String
  check(String) = Boolean
  run(String, Boolean) = int

Completed methods:
  Agriplot() = JApplet
  init()
  main()

  makeContentPane() = Container
  actionPerformed(ActionEvent)
  codeWindowBlank()
  codeWindowReset()
  codeWindowAppend(String)
  outputWindowBlank()
  outputWindowAppend(String)

  save(String)
  load(String)
  run(String) = int

*/

import java.applet.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import java.io.StringBufferInputStream;
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.IOException;

public class Agriplot extends JApplet implements ActionListener {

  // ActionCommands for buttons
  protected static final String NEW   = "new";
  protected static final String LOAD  = "load";
  protected static final String SAVE  = "save";
  protected static final String RUN   = "run";
  protected static final String ABORT = "abort";
  protected static final String CHECK = "check";
  protected static final String CLEAR = "clear";

  // UI elements
  protected JTextArea   textCode, textOutput;
  protected JButton     butnNew, butnLoad, butnSave, butnRun, butnCheck, butnClear;
//  protected Interpreter interpreter = new Interpreter();

  // Constructor
  public Agriplot() {
    super();
    getRootPane().putClientProperty("defeatSystemEventQueueCheck",Boolean.TRUE);
  }

  // Initialisation
  public void init() {
    super.init();
    setContentPane(makeContentPane());
  }

  // main method
  public static void main() {
      try {
        UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
      } catch(Exception e) {/*DONOTHING*/}
      JFrame frame = new JFrame("AGRIPLOT Interpreter");
      frame.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e){System.exit(0);}
      });
      Agriplot applet = new Agriplot();
      frame.setContentPane(applet.makeContentPane());
      frame.pack();
      frame.setVisible(true);
  }

  // UI creation
  public Container makeContentPane() {
    // setup JButton properties
    butnNew   = new JButton("New");
      butnNew.setMnemonic(KeyEvent.VK_N);
      butnNew.setActionCommand(NEW);
      butnNew.addActionListener(this);
      butnNew.setToolTipText("Clear code window.");
    butnLoad  = new JButton("Load");
      butnLoad.setMnemonic(KeyEvent.VK_L);
      butnLoad.setActionCommand(LOAD);
      butnLoad.addActionListener(this);
      butnLoad.setToolTipText("Load code from file.");
    butnSave  = new JButton("Save");
      butnSave.setMnemonic(KeyEvent.VK_S);
      butnSave.setActionCommand(SAVE);
      butnSave.addActionListener(this);
      butnSave.setToolTipText("Save code to file.");
    butnRun   = new JButton("Run");
      butnRun.setMnemonic(KeyEvent.VK_R);
      butnRun.setActionCommand(RUN);
      butnRun.addActionListener(this);
      butnRun.setToolTipText("Interpret the code.");
    butnCheck = new JButton("Check");
      butnCheck.setMnemonic(KeyEvent.VK_C);
      butnCheck.setActionCommand(CHECK);
      butnCheck.addActionListener(this);
      butnCheck.setToolTipText("Check the code for consistency.");
    butnCheck = new JButton("Clear");
      butnClear.setMnemonic(KeyEvent.VK_E);
      butnClear.setActionCommand(CLEAR);
      butnClear.addActionListener(this);
      butnClear.setToolTipText("Clear the output window.");

    // add JButtons into buttonPane
    JPanel buttonPane = new JPanel();
    buttonPane.setLayout(new FlowLayout());
    buttonPane.add(butnNew);
    buttonPane.add(butnLoad);
    buttonPane.add(butnSave);
    buttonPane.add(butnRun);
    buttonPane.add(butnCheck);
    buttonPane.add(butnClear);

    // setup JTextArea properties
    textCode  = new JTextArea(10,10);
      textCode.setFont(new Font("Courier", Font.PLAIN, 12));
      codeWindowReset();
    textOutput= new JTextArea(10,10);
      textOutput.setFont(new Font("Courier", Font.PLAIN, 12));

    // add JTextAreas into JScrollPanes
    JScrollPane codeScrollPane = new JScrollPane(textCode);
      codeScrollPane.setMinimumSize(new Dimension(100, 50));
    JScrollPane outputScrollPane = new JScrollPane(textOutput);
      outputScrollPane.setMinimumSize(new Dimension(100, 50));

    // add JScrollPanes into splitPane
    JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, codeScrollPane, outputScrollPane);
      splitPane.setOneTouchExpandable(true);
      splitPane.setDividerLocation(0.75);

    // add buttonPane and splitPane into the main JPanel
    JPanel pane = new JPanel();
    pane.setLayout(new BorderLayout());
    pane.add(buttonPane, BorderLayout.NORTH);
    pane.add(splitPane, BorderLayout.CENTER);

    // setup JPanel properties
    pane.setBackground(new Color(255,255,255));
    pane.setBorder(BorderFactory.createMatteBorder(1,1,1,1,Color.blue));

    return pane;
  }

  // 3 functions for operating on the code input JTextArea
  public void codeWindowReset() {
    textCode.setText("/*********************************\n * Type your code here, or click *\n * 'Load' to open a file.        *\n *********************************/\n");
  }
  public void codeWindowBlank() {
    textCode.setText("");
  }
  public void codeWindowAppend(String message) {
    textCode.setText(textCode.getText()+message);
  }

  // 2 functions for operating on the output JTextArea
  public void outputWindowBlank() {
    textOutput.setText("");
  }
  public void outputWindowAppend(String message) {
    textOutput.setText(textOutput.getText()+message);
  }

  // handle actions
  public void actionPerformed(ActionEvent e) {
    String action = e.getActionCommand();
    int errlvl;
    String filename;
    if(action.equals(NEW)) {
      outputWindowAppend("\n#new");
      codeWindowReset();
    } else if(action.equals(LOAD)) {
      outputWindowAppend("\n#load");
      filename = getFilename("Load file: ");
      if(! filename.equals(""))  load(filename);
    } else if(action.equals(SAVE)) {
      outputWindowAppend("\n#save");
      filename = getFilename("Save file: ");
      if(! filename.equals(""))  save(filename);
    } else if(action.equals(CHECK)) {
      outputWindowAppend("\n#check");
      check(textCode.getText());
    } else if(action.equals(RUN)) {
      outputWindowAppend("\n#check");
      if(check(textCode.getText())) {
        outputWindowAppend("\n#run");
        errlvl = run(textCode.getText());
        if(errlvl != 0)  outputWindowAppend("\nrun() halted with errorlevel " + errlvl);
      }
      butnNew.setEnabled(false);
      butnLoad.setEnabled(false);
      butnSave.setEnabled(false);
      butnCheck.setEnabled(false);

      butnRun.setText("Abort");
      butnRun.setActionCommand(ABORT);
      butnRun.setToolTipText("Abort interpretation.");
    } else if(action.equals(ABORT)) {
      outputWindowAppend("\n#abort");

      butnNew.setEnabled(true);
      butnLoad.setEnabled(true);
      butnSave.setEnabled(true);
      butnCheck.setEnabled(true);

      butnRun.setText("Run");
      butnRun.setActionCommand(RUN);
      butnRun.setToolTipText("Interpret the code.");
    } else if(action.equals(CLEAR)) {
      outputWindowBlank();
    }
  }

  // the most important method: run the interpreter
  public int run(String code, Boolean isForReal) {
    StringBufferInputStream streamCode = new StringBufferInputStream(code);
    try{
      //   what is the most appropriate way to do all this?
      // Parser parserCode = new Parser(streamCode);
      // if(isForReal)  interpreter.interpret(parserCode.parse());
      // else           interpreter.check(parserCode.parse());
    }catch(RunTimeException e){
      outputWindowAppend(e);
      outputWindowAppend("Line " + String.toString(e.lnum) + ": " + e.line);
      return -1;
    }catch(ParseException e){
      outputWindowAppend(e);
      outputWindowAppend("Line " + String.toString(e.lineNumber()) + ", Token: " + e.token());
      return -1;
    }
    return 0;
  }

  // wrapper to run(String, Boolean)
  public int run(String code) {
    return run(code, true);
  }

  // check whether code is consistent
  public boolean check(String code) {
    int errlvl = run(code, false);
    switch(errlvl) {
      case(0):
        outputWindowAppend("\n\nCode passed checks.");
        return true;
      break;
      default:
        outputWindowAppend("\n\nCode failed checks.");
        return false;
    }
  }

  // get filename from user
  public String getFilename(String prompt){
    //$TODO
    return "";
  }

  // load code from specified file
  public void load(String file){
    try{
      URL URLFile = new URL(getDocumentBase(), file);
      DataInputStream streamFile = new DataInputStream(URLFile.openStream());
      String curline;

      codeWindowBlank();
      while ((curline = streamFile.readLine()) != null)
        codeWindowAppend(curline + "\n");
      streamFile.close();

    }catch(Exception e){
      outputWindowAppend("\n\nError loading \"" + file + "\" : " + e);
    }
  }

  // save code to specified file
  public void save(String file){
    try{
      URL URLFile = new URL(getDocumentBase(), file);
      FileOutputStream streamFile = new FileOutputStream(URLFile);
      PrintStream streamPrint = new PrintStream(streamFile);
      InputStream streamCode = new InputStream(textCode.getText());

      while((curline = streamCode.readLine()) != null)
        streamPrint.println(curline);

      streamPrint.close();
      streamFile.close();

    }catch(Exception e){
      outputWindowAppend("\n\nError loading \"" + file + "\" : " + e);
    }
  }
}

