import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
import java.lang.*;

class UserInterface extends JApplet implements ActionListener, KeyListener {
  // Interface components
  JButton execButt, timeRangeButton, coalButt, xmlButt;
  Box executionBox; 
  Box navigationBox;
  Box settingsBox;
  Box configureBox;

  JButton clrButt, backButt, forwardButt;
  JButton settingsButton;
  
  JTextArea qryArea;
  JLabel qryAreaLab, qryLab;

  JTextArea msgArea;
  JLabel msgAreaLab, msgLab;

  ButtonGroup isTemporal;
  JTextField snapshotTime;
  JRadioButton allTimes;
  JRadioButton snapshot;
  // Control Settings
  QryBuffer qryBuff = new QryBuffer();
  public static Settings coal = new Settings();
  boolean xmlFormat = false;
  public static TimeDefinition time=new TimeDefinition();
  public static boolean timeWindow=false;
  public static boolean settingsWindow=false;

  UserInterface() {
    int marginWidth=10;
    int rowGap=5;

    // Panel to execute a query, at all times or snapshot time
    executionBox=Box.createHorizontalBox();
    execButt=new JButton ("Execute");
    allTimes=new JRadioButton("All times",false);
    snapshot=new JRadioButton("Snapshot",true);
    snapshotTime=new JTextField(10);
    snapshotTime.setText("0");
    snapshotTime.setMaximumSize(snapshotTime.getPreferredSize());
    isTemporal=new ButtonGroup();
    isTemporal.add(allTimes);
    isTemporal.add(snapshot);
    executionBox.add(execButt);
    executionBox.add(Box.createHorizontalStrut(marginWidth));
    executionBox.add(allTimes);
    executionBox.add(snapshot);
    executionBox.add(snapshotTime);
    executionBox.add(Box.createGlue());

    timeRangeButton = new JButton ("Time");
    coalButt = new JButton ("Coalesce");
    xmlButt = new JButton ("Save as XML");

    // buffer navigation buttons
    navigationBox=Box.createHorizontalBox();
    qryLab=new JLabel(qryBuff.getQueryTitle());
    backButt = new JButton("<");
    forwardButt = new JButton (">");
    clrButt = new JButton ("Clear");
    navigationBox.add(qryLab);
    navigationBox.add(Box.createGlue());
    navigationBox.add(backButt);
    navigationBox.add(Box.createHorizontalStrut(marginWidth));
    navigationBox.add(forwardButt);
    navigationBox.add(Box.createHorizontalStrut(marginWidth));
    navigationBox.add(clrButt);

    configureBox=Box.createHorizontalBox();
    configureBox.add(coalButt);
    configureBox.add(timeRangeButton);
    configureBox.add(xmlButt);
	     
    // implement the User input and place in panel
    qryArea=new JTextArea(3,50);
    qryArea.setLineWrap(true); 
    qryArea.addKeyListener(this);

    // implement the Message display panel
    settingsBox=Box.createHorizontalBox();
    msgLab=new JLabel("System Messages");
    settingsButton=new JButton("Settings");
    settingsBox.add(msgLab);
    settingsBox.add(Box.createGlue());
    settingsBox.add(settingsButton);
    
    msgArea = new JTextArea (15, 50);
    msgArea.setLineWrap(true); 
    System.setOut(new PrintStream(new JTextAreaStream(msgArea)));

    // GUI Frame layout
    Box main=Box.createVerticalBox();
    main.add(navigationBox);
    main.add(Box.createVerticalStrut(marginWidth));
    //getContentPane().add(configureBox);
    main.add(qryArea);
    main.add(Box.createVerticalStrut(rowGap));
    main.add(executionBox);
    main.add(Box.createVerticalStrut(2*rowGap));
    main.add(settingsBox);
    main.add(Box.createVerticalStrut(rowGap));
    main.add(msgArea);

    // Create the scroll pane for the msgArea  
    JScrollPane scrollPane=new JScrollPane(msgArea);
    main.add(scrollPane, BorderLayout.CENTER);
    getContentPane().add(main);

    // initialise the background colour of buttons
    colorCoalButt();
    if (xmlFormat==false)
      xmlButt.setBackground( Color.red );
    else
      xmlButt.setBackground( Color.lightGray);
		
    execButt.addActionListener(this);
    clrButt.addActionListener(this);
    coalButt.addActionListener(this);
    timeRangeButton.addActionListener(this);
    xmlButt.addActionListener(this);
    backButt.addActionListener(this);
    forwardButt.addActionListener(this);
    settingsButton.addActionListener(this);
  }

  public void actionPerformed (ActionEvent evt)
  {
    Object source = evt.getSource();

    if (source==execButt)
      {
	runQry();			
      }
		
    if (source==backButt)
      {
	navigateBuffer("back");
      }

    if (source==forwardButt)
      {
	navigateBuffer("forward");
      }

    if (source==clrButt)
      {
	qryArea.setText("");
	// msgArea.setText(empty);
      }

    if (source==coalButt)
      {
	setCoal();
	colorCoalButt();
	if (getCoal())
	  System.out.println("Coalesce Property is turned ON");
	else
	  System.out.println("Coalesce Property is turned OFF");
      }

    if (source==xmlButt)
      {
	if (xmlFormat==false)
	  {
	    xmlFormat=true;
	    xmlButt.setBackground( Color.lightGray);
	    System.out.println("Saving all results in XML format is turned ON");
	  }
	else
	  {
	    xmlFormat=false;
	    xmlButt.setBackground( Color.red );
	    System.out.println("Saving all results in XML format is turned OFF");
	  }
      }

    if (source==settingsButton)
      new ControlSettings();

    if (source==timeRangeButton)
      {
	if (!timeWindow)
	  {
	    TimeSets.execute();
	    timeWindow=true;
	  }
	else
	  System.out.println("A Time Settings Window has already been opened");
			
      }
		
  }

  public void keyPressed(KeyEvent e) 
  {
    int keyCode = e.getKeyCode();

    if (keyCode==10) /* ENTER KEY */
      {
	runQry();
      }

    if (keyCode==40) /* ENTER KEY */
      {
	navigateBuffer("back");
      }

    if (keyCode==38) /* ENTER KEY */
      {
	navigateBuffer("forward");
      }
  }

  public void keyTyped(KeyEvent e)
  {
    //int keyCode = e.getKeyCode();
    //if (keyCode==10) /* ENTER KEY */
    //{
    //	runQry();
    //}
  }

  public void keyReleased(KeyEvent e) 
  {
    int keyCode = e.getKeyCode();
    //if (keyCode==10) /* ENTER KEY */
    //{
    //	runQry();
    //}
    //qryArea.setText("");
    //qryArea.insert("", 0);
  }

  public void runQry() {
    String s = qryArea.getText().trim();
    if (!s.equals("")) {
      /* place string in buffer */
      qryBuff.setQry(s);

      // Create title to use for query result
      String qTitle=qryBuff.getQueryTitle();

      // Update query view to show clear box
      qryBuff.nextQry();
      qryArea.setText(qryBuff.getQry());
      qryLab.setText(qryBuff.getQueryTitle());

      // Now process query 
      String[] args1 = getSubStr(s);

      Tree.setConstants();
      TreeNode tree = Tree.buildTree(args1, false); // N.B. CHANGE TO buildTree
      System.out.print(qTitle+" generated plan: ");
      PrintTree.print(tree);
      System.out.println("");
			
      // check handling of brackets in query
      int bracks = Tree.getNoBracks();
      if (bracks==0) {
	Table output = ExecuteTree.generate(tree);

	int x = output.getNoRowSet();
	int y = output.getNoCols();
				
	// print table and set title to the query number */
	// PJM Make query appear in title
	String title=qTitle+": "+s;
	if (allTimes.isSelected())
	  new PrintRes(output, title);
	else
	  new PrintRes(output,title,
		       Integer.parseInt(snapshotTime.getText()));
	System.out.println(qTitle+" result size (for all times): No of Rows="+
			   x+" and No of Cols="+y);
				
	if (xmlFormat==true)
	  {
	    System.out.println("XML FILE CREATED: result of query saved in file 'results.xml' in the current directory");
	    File fo = new File("results.xml");
	    SaveXMLfile.save(output, fo);
	  }
	
	// input and msg text area updates
	msgArea.append("***********************************************\n");
	//msgArea.requestFocus();
      }
      else // brackets err
	CatchErr.brackErr(bracks);	
    }
  }


  // tokenise input 
  // PJM: a crude rewrite so that whitespace is not required between
  // all tokens
  private static String[] getSubStr(String s) {
    ArrayList tokens=new ArrayList();
    StringBuffer q=new StringBuffer(s);
    int type=1;
    int start=0;
    int i;

    for (i=0;i<q.length();i++) {
      switch (q.charAt(i)) {
	// The type=0 characters: always tokens by themselves, e.g "{"
      case '(' :
      case ')' :
      case '}' :
      case '{' :
	start=tokenAdd(tokens,q,type,start,i);
        type=0;
	break;
	// The type=1 characters: whitespace, and thus return no token
      case ' ' :
      case '\n' :
      case '\t' :
        if (type!=1)
	  start=tokenAdd(tokens,q,type,start,i);
	type=1;
	break;
	// The type=2 characters: sequence forms an operator, e.g. "<="
      case '=' :
      case '<' :
      case '>' :
        if (type!=2)
	  start=tokenAdd(tokens,q,type,start,i);
	type=2;
	break;
	// The type=3 characters which form strings, numbers etc
      default :
	if (type!=3)
	  start=tokenAdd(tokens,q,type,start,i);
	type=3;
	break;
      }
    }

    tokenAdd(tokens,q,type,start,i);

    return (String[])tokens.toArray(new String[0]);
  }

  private static int tokenAdd(ArrayList tokens,StringBuffer q,int type,
			      int start, int end) {
    if (type!=1)
      tokens.add(q.substring(start,end));

    return end;
  }

  /****************************************************/
  /*			INTERFACE CONTROL FEATURES    */
  /****************************************************/

  /* Navigating the Buffer back and forward */
  
  public void navigateBuffer(String move) {
    String s = qryArea.getText();
    String qry = "";
    
    if (move.equals("back"))
      qry = qryBuff.prevQry();
    else if (move.equals("forward"))
      qry = qryBuff.nextQry();
    else
      System.out.println("Unable to recognise input to navigate query buffer");
    
    qryArea.setText(qry); 
    qryLab.setText(qryBuff.getQueryTitle());
  }

  /* enable and disenable coalesce flag */ 
  public static boolean getCoal()
  {
    return coal.getFlag();
  }

  public void setCoal()
  {
    coal.setFlag();
  }

  /* colour the coalesce button accordingly */
  public void colorCoalButt()
  {
    if (getCoal()==true)
      coalButt.setBackground( Color.lightGray);
    else //the option is set to false
      coalButt.setBackground( Color.red );
  }


  public static int getMinTime()
  {
    return time.getMinTime();
  }

  public static int getMaxTime()
  {
    return time.getMaxTime();
  }

  public static void setMinTime(int t)
  {
    time.setMinTime(t);
  }

  public static void setMaxTime(int t)
  {
    time.setMaxTime(t);
  }

  public static void closeTimeWindow()
  {
    timeWindow=false;
  }


}
