import java.lang.*;
import java.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
//import com.sun.java.swing.*;
import javax.swing.*;
import java.net.URL;
import graph.*;


public class Client extends JApplet implements Runnable {
    boolean DEBUG = false; //Setting a debug parameter to true in html enables this
    int model; //Will be 1, 2 or 3. Read in from the html
    final static int MAXFIELDS = 100;
    final static int MAXPANES  = 10;
    //States for the parser
    final static int DEFAULT           = 0;
    final static int GRAPH_INFO       = 1;
    final static int READING_IMAGEMAP = 2;
    
    //The button that appears on the html page
    JButton begin;
    //These are initialized in each thread
    //Need to put here for the sake of the buttonclick handler
    int[] pane_number; 
    float[] initial_value;
    String[] comment;
    JTextField[] field_array;
    JFrame imagemap_pane;
    JFrame[] panes_array;
    Float_Rectangle[] clickable_array;
    int fields; //How many fields we've read
    PrintWriter out = null; 
    BufferedReader in = null;
    JButton return_button;
    Image imagemap;
    MyActionListener action_man; //Our handler for mouse clicks
    JTextArea t;
    ReadData rd1;

    //Quiet the annoying message
    //This is stupid since constructors aren't guaranteed to be called on applets
    //But thats what walrath@sun says works.
    public Client() {
        getRootPane().putClientProperty ("defeatSystemEventQueueCheck", Boolean.TRUE);
    }
        
    public void init() {
        try {
            imagemap = getImage(new URL(getParameter("image_url")));
        } catch (Exception e) {}
        
        //Set up our action listener
        action_man = new MyActionListener();
        action_man.setContext(this);

        begin = new JButton("Begin");
        begin.setDefaultCapable(true);
        begin.setFont(new Font("SansSerif",Font.PLAIN,14));
        begin.addActionListener(action_man);
        begin.setActionCommand("begin");
        getContentPane().add(begin);
        //getContentPane().setLayout(new BoxLayout(this.getContentPane(),BoxLayout.Y_AXIS));

        //Get which model we're running from the html document
        String modelstring = getParameter("model");
        if (modelstring != null) {
            try {
                model = Integer.parseInt(modelstring);
            } catch (NumberFormatException e) {
                System.out.println("HTML Error: Bad model number");
            }
        }
        try {
        String debugmode = getParameter("debug");
        if (debugmode.equalsIgnoreCase("true") || debugmode.equalsIgnoreCase("1"))
            DEBUG = true;
        } catch (Exception e) {} //Ignore it if no parameter exists
    }

    public void run() {
        //For dealing with client-server communication
        Socket s = null;

        //These are for parsing the config file
        //Field1 = pane_number, Field2 = initial value, Field3 = comment
        pane_number = new int[MAXFIELDS]; 
        initial_value = new float[MAXFIELDS];
        comment = new String[MAXFIELDS];
        //Read when in IMAGEMAP state
        clickable_array = new Float_Rectangle[MAXPANES];
                
        //Four arrays to hold & display the list of fields (parameters)
        field_array = new JTextField[MAXFIELDS]; //All the text fields
        panes_array = new JFrame[MAXPANES]; //Window to hold text fields
        JPanel field_pane[] = new JPanel[MAXPANES]; //Pane that holds text fields
        JScrollPane scroll_pane[] = new JScrollPane[MAXPANES]; //Scroll that pane
        
        if (DEBUG) t = new JTextArea();
        int clickables_read = 0; //How many imagemap boxes have been read
        int done = 0;
        String line; 
        int state = DEFAULT;
        JImageComponent image_area;
        JScrollPane text_scroll_pane;
        ImagemapMouseListener i_listener;


        //Begin building GUI
        begin.setEnabled(false); //Disable the button to prevent double clicks
        return_button = new JButton("Submit");    
        return_button.setEnabled(true);
        fields = 0; //Set it here since this is multithreaded.

        //We must create all the stuff for each popup window
        //panes_array[0] is the model settings pane (graphing info etc)
        //all the others are the model parameters
        //imagemap_pane is the pane holding the map and other stuff
        for (int i = 0;i < MAXPANES; i++) {

            //Make new window
            if (i == 0) {
                imagemap_pane  = new JFrame("Bionome Client: Connecting...");
                imagemap_pane.getContentPane().setLayout(new BoxLayout(imagemap_pane.getContentPane(),BoxLayout.Y_AXIS));
                
                //Give it a    menu
                //JPopupMenu.setDefaultLightWeightPopupEnabled(false);
                JMenuBar menuBar = new JMenuBar();
                JMenu menu = new JMenu("File");
                menu.setMnemonic('f');
                JMenuItem menuItem = new JMenuItem("Reload",KeyEvent.VK_C);
                menuItem.setMnemonic('r');
                menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.CTRL_MASK));
                menuItem.addActionListener(action_man);
                menuItem.setEnabled(false);    //Reload doesn't work for now
                menu.add(menuItem);
                menu.addSeparator();
                menuItem = new JMenuItem("Quit",KeyEvent.VK_A);
                menuItem.setMnemonic('q');
                menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK));
                menuItem.addActionListener(action_man);
                menu.add(menuItem);
                menuBar.add(menu);
                imagemap_pane.setJMenuBar(menuBar);
            }
            panes_array[i] = new JFrame("Set options");

            //Create Layout Manager
            GridBagLayout gridbag = new GridBagLayout();
            panes_array[i].getContentPane().setLayout(gridbag);
            GridBagConstraints c = new GridBagConstraints();
            c.fill = GridBagConstraints.BOTH;

            //On first loop set up the imagemap pane
            //new array for adding Jfields and whatever
            if (i == 0) {
                int width  = Integer.parseInt(getParameter("imagewidth"));
                int height = Integer.parseInt(getParameter("imageheight"));
                image_area = new JImageComponent (imagemap); 
                image_area.setMinimumSize(new Dimension(width,height));
                image_area.setPreferredSize(new Dimension(width,height));
                c.gridy = 1;
                c.weightx = 1;
                gridbag.setConstraints(image_area,c);
                imagemap_pane.getContentPane().add(image_area);

                //Add Status Text Area
                t = new JTextArea(10,80);
                t.setFont(new Font("MonoSpaced", Font.PLAIN, 10));
                text_scroll_pane = new JScrollPane(t);
                c.gridy = 2;
                c.weightx = 1;
                c.weighty = 1;
                gridbag.setConstraints(text_scroll_pane,c);
                if (DEBUG) imagemap_pane.getContentPane().add(text_scroll_pane);

                //Make mouse listener for imagemap
                i_listener = new ImagemapMouseListener(imagemap,clickable_array,panes_array,t,this);
                image_area.addMouseListener(i_listener);

                //Create button for sending configuration back to the server
                return_button.setFont(new Font("SansSerif",Font.PLAIN,14));
                return_button.addActionListener(action_man);
                return_button.setActionCommand("return_button");
                return_button.setEnabled(false);

                c.weightx = 0;
                c.weighty = 0;
                c.gridy = 3;
                gridbag.setConstraints(return_button,c);
                imagemap_pane.getContentPane().add(return_button);
            }

            //Create Pane that will hold fields and comments
            field_pane[i] = new JPanel();
            field_pane[i].setLayout(new GridLayout(0,2));
            field_pane[i].setMinimumSize(new Dimension(100,500));

            //Make said Pane scrollable
            scroll_pane[i] = new JScrollPane(field_pane[i]);
            scroll_pane[i].setPreferredSize(new Dimension(540,300));
            //scroll_pane.setPreferredSize(new Dimension(250,200));
            c.weightx = 1;
            c.weighty = 10;
            c.gridy   = 4;
            c.gridwidth = GridBagConstraints.REMAINDER;
            c.gridheight = GridBagConstraints.REMAINDER;
            gridbag.setConstraints(scroll_pane[i],c);
            panes_array[i].getContentPane().add(scroll_pane[i]);


        }

        imagemap_pane.pack();
        imagemap_pane.show();

        try { 
            //Open a connection to the server
            String hostname = this.getCodeBase().getHost();
            s = new Socket(hostname,1998);
            //s = new Socket("be-research.ucsd.edu",1998);
            JLabel field_label = new JLabel("default",JLabel.LEFT);
            imagemap_pane.setTitle("Bionome Connected: be-research");

            //Set up our streams
            out = new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
            in  = new BufferedReader(new InputStreamReader(s.getInputStream()));

            //-->Send model #
            out.println(model);
            out.flush();

            //Now we read and parse the client configuration file from the server

            for (int i = 0; done == 0; i++) {
                line = in.readLine();

                try {
                    StringTokenizer st = new StringTokenizer(line);
                    for (int word_num = 0; st.hasMoreTokens(); word_num++) {
                        String word = st.nextToken();
                        //TODO: This will only find comments with #'s at the start of a token
                        //Make it search through the token for a # and discard everything after
                        if (word.substring(0,1).equals("#")) break;

                        //Now we do a switch, basically on the word
                        //STATE CHANGES
                        if (word.equals("GRAPH_INFO")) {
                            state = GRAPH_INFO;
                        }
                        else if (word.equals("IMAGEMAP")) {
                            state = READING_IMAGEMAP;
                        }
                        else if (word.equals("END")) {
                            if (state == GRAPH_INFO || state == READING_IMAGEMAP)
                                state = DEFAULT;
                            else 
                                done = 1;
                        }
                        else if (state == DEFAULT) {
                            //Process line - most will be adding fields to various panes
                            //Expect 0 1.3 "Cell permiability"
                            if (word_num == 0) {
                                //Expect int
                                fields++;
                                pane_number[fields - 1] = Integer.parseInt(word);
                            }
                            else if (word_num == 1) {
                                //Expect float
                                Float ftemp = new Float(word);
                                initial_value[fields-1] = ftemp.floatValue();

                                //Add the new field with its default value to its
                                //chosen pane.
                                field_array[fields - 1] = new JTextField(word);
                                //f.getContentPane().add(field_array[fields - 1]);

                                field_pane[pane_number[fields-1]].add(field_array[fields-1]);
                            }
                            else if (word_num >= 2) {
                                try {
                                //Expect string

                                //If we have already read a word for this comment, remove
                                //the label we have created and make a new one
                                if (comment[fields-1] != null)
                                    field_pane[pane_number[fields-1]].remove(field_label);
                                if (comment[fields-1] != null)
                                    comment[fields-1] = comment[fields-1] + " " + word;
                                else
                                    comment[fields-1] = word;
                                field_label = new JLabel(comment[fields-1],JLabel.LEFT);
                                field_pane[pane_number[fields-1]].add(field_label);
                                } catch (Exception e) {
                                    throw new Exception("Imagemap parse error: " + e.toString());
                                }
                            }
                        }
                        //TODO: Come up with more stuff to put in here
                        else if (state == GRAPH_INFO) {
                            ;
                        }
                        else if (state == READING_IMAGEMAP) {
                            Float first, second;
                            //Expect (.1,0) (.4,.5)
                            first = Float.valueOf(word.substring(word.indexOf("(")+1,word.indexOf(",")));
                            second = Float.valueOf(word.substring(word.indexOf(",")+1,word.indexOf(")")));
                            
                            if (clickables_read % 2 == 0) {
                                clickable_array[clickables_read / 2] = new Float_Rectangle(first.floatValue(),0,0,0);
                                clickable_array[clickables_read / 2].b = second.floatValue();
                            }
                            else {
                                clickable_array[clickables_read / 2].c = first.floatValue();
                                clickable_array[clickables_read / 2].d = second.floatValue();
                            }
                            clickables_read++; //Note this will go up twice per rectangle!
                        }

                    if (DEBUG) {
                            t.append(word);
                            t.append(" ");
                    }
                    }
                }
                catch (NoSuchElementException e) { ; }
                catch (NumberFormatException e)  { ; }

                if (DEBUG) { t.append("\n"); }
            }

            //Now we resize each of the panes so it looks nice
            for (int i=0; i < MAXPANES; i++) {
                int size = field_pane[i].getComponentCount();
                if (size == 0) continue;
                field_pane [i].setMinimumSize  (new Dimension(600,5*size));
                field_pane [i].setPreferredSize(new Dimension(600,10*size));
                scroll_pane[i].setMinimumSize  (new Dimension(600,5*size));
                scroll_pane[i].setPreferredSize(new Dimension(600,10*size));
            }
            //out.flush();

            //Now that we've finished building interface, allow them to send
            //values back to server
            return_button.setEnabled(true);
            //And to relaunch the client when they want to
            begin.setEnabled(true);

            //Allow person to set options. When they click done, send options
            //to the server
            ;            

            //xxxx
            //Now read output from the server and feed it to graph
            //It is a seperate thread to handle adding to the graph
            rd1 = new ReadData(in,this);
            new Thread(rd1).start();
            rd1.barrier(); //Wait until graphing is finished
            if (DEBUG) t.append("Done!\n");

            //TODO make it reenter here
        }
        catch (Exception e) {
            if (DEBUG) t.append(e.toString());
            imagemap_pane.setTitle("Bionome Error: " + e.toString());
            System.out.println("Bionome Error: " + e.toString());
        }
        finally { 
            try { 
                in.close(); //TODO - don't close sockets so we can reenter?
                out.close(); 
                s.close(); 
                //And reenable starting button if we haven't yet...
                begin.setEnabled(true);
            }
            catch(Exception e){} 
        }
    }
}

/* Draws an image, handles the imagemap stuff, etc */
class JImageComponent extends JComponent {
    protected Image image;
    Float_Rectangle areas[];

  JImageComponent () {
        super();
        areas = new Float_Rectangle[Client.MAXPANES];
    }
    JImageComponent (Image new_image) {
        super();
        areas = new Float_Rectangle[Client.MAXPANES];
        image = new_image;
    }

    public void paint(Graphics g) {
            try {
                g.drawImage(image, 0, 0, this); //85x62 image
            } catch (Exception e) {
                System.err.println("Image Draw Error: " + e.toString());
            }

            //Now draw the highlighted rectangle
            //g.setXORMode(Color.red);
    }
}

/* Handles mouseclicks on the imagemap */
class ImagemapMouseListener extends MouseAdapter {
    Float_Rectangle areas[];
    JFrame frames[];
    JTextArea t;
    Image imagemap;
    Client client;
    Float_Rectangle down_click;

    ImagemapMouseListener(Image new_imagemap, Float_Rectangle new_areas[], JFrame new_frames[], JTextArea new_t, Client new_client) {
        imagemap = new_imagemap;
        areas = new_areas;
        frames = new_frames;
        t = new_t;
        client = new_client;
        down_click = new Float_Rectangle(-1,-1,-1,-1);
    }

    boolean inside(float x, float y, Float_Rectangle rect) {
        if (x >= rect.a && x <= rect.c && y >= rect.b && y <= rect.d)
            return true;
        return false;
}

    //We store the downclick and trigger if we get an upclick near it
    //This helps prevent cosmetic flaws, like it catching an upclick 
    //without a downclick to go with it, such as when you close a frame
    //above the imagemap
    public void mousePressed(MouseEvent e) {
        down_click = new Float_Rectangle(e.getX()-10,e.getY()-10,e.getX()+10,e.getY()+10);
    }

    //If the mouse was clicked within the image, check to see if they
    //clicked within one of the clickable areas we have set up.
    public void mouseReleased(MouseEvent e) {
        int width  = Integer.parseInt(client.getParameter("imagewidth"));
        int height = Integer.parseInt(client.getParameter("imageheight"));
        int x = e.getX();
        int y = e.getY();
        if (x > width || x < 0 || y > height || y < 0)
            return;
        if (width == 0 || height == 0) //Check for divide by zeros
            return;
        if (!inside(x,y,down_click)) //Check to see if the up and down-clicks match
            return;
        down_click = new Float_Rectangle(-1,-1,-1,-1); //Forget about the downclick
        float fract_x = x; //Will be between 0.0 (left) and 1.0 (right)
        float fract_y = y; //Will be between 0.0 (top) and 1.0 (bottom)
        fract_x /= width;
        fract_y /= height;
        for (int i = 0; i < client.clickable_array.length; i++) {
            if (client.clickable_array[i] == null) continue;
            if (inside(fract_x,fract_y,client.clickable_array[i])) {
                client.panes_array[i].pack(); //pane 0 is the settings pane
                client.panes_array[i].show();
                break;
            }
        }
    }
}

class MyActionListener implements ActionListener {
    Client client;
    
    public void setContext(Client new_client) {
        client = new_client;
    }
                                      
    public void actionPerformed(ActionEvent e) {
        if (client.DEBUG) System.out.println("actionPerformed: " + e.toString());
        if (e.getActionCommand() == null) { //Timer event
            System.err.println("ActionListener -- Unhandled Timer: " + e.toString());
        }
        else if (e.getActionCommand().equals("begin")) new Thread(client).start();
        else if (e.getActionCommand().equals("return_button")) {
            client.return_button.setEnabled(false);
            for (int i = 0; i < client.fields; i++) {
                client.out.println(client.field_array[i].getText());
                client.field_array[i].setText("Sent");
            }
            client.out.flush();
        }
        else if (e.getActionCommand().equals("textual")) { //Show text output of model
            client.rd1.text_area_frame.pack();
            client.rd1.text_area_frame.show();
        }
        else if (e.getActionCommand().equals("Select All")) { //Select all output
            client.rd1.text_area.selectAll();
        }
        else if (e.getActionCommand().equals("Copy")) { //Select all output
            //Causes security violation
            /* Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
            StringSelection data = new StringSelection ("Happy frog!");
            StringSelection data = new StringSelection (client.rd1.text_area.getText ());
            clipboard.setContents (data, data);
            */
        }
        else if (e.getActionCommand().equals("Quit")) { //Select all output
            //HACK: Start a timer to kill main menu in a few millisecs so we don't get a warning
            Timer cleanup = new Timer(1,new TimerActionListener("cleanup",this));
            cleanup.setInitialDelay(100);
            cleanup.setRepeats(false);
            cleanup.start();
            for (int i = 0; i < client.MAXPANES; i++)
                client.panes_array[i].dispose();
            if (client.rd1 != null) {
                if (client.rd1.gw != null) client.rd1.gw.dispose();
                if (client.rd1.text_area_frame != null) client.rd1.text_area_frame.dispose();
            }
        }
        else if (e.getActionCommand().equals("cleanup")) {
            //This causes a error message to be printed on the mouseup event
            // if we kill it in the Quit handler. :p
            client.imagemap_pane.dispose();
        }
        else {
            System.err.println("ActionListener -- Unknown Action: " + e.toString());
        }

    }
}

//Sort of like a nextthink function in QuakeC.
//It is a helper that allows Timer's to pass strings to MyActionListener
//since by default their getActionCommand() is null, which limits us to 
//one timer per actionlistener otherwise...
class TimerActionListener implements ActionListener {
    String triggername;
    ActionListener helper;
    TimerActionListener(String triggername, ActionListener helper) {
        this.triggername = triggername;
        this.helper = helper;
    }
    public void actionPerformed(ActionEvent e) {
        helper.actionPerformed(new ActionEvent(this,e.getID(),triggername));
    }
}
class Float_Rectangle extends Object {
    public float a,b,c,d;
    Float_Rectangle(float x, float y, float w, float z) {
        a = x; b = y; c = w; d = z;
    }
}

/*
** Here is the Thread class to calculate the data and append it to the existing
** data set.
** This class is easily modified to get the data from a server 
** or the local machine. 
*/

class ReadData extends Thread {
    //      local copy of the update period in milliseconds
    final static int MAXGRAPHS = 10;
    int p = 1000;
    DataSet[] d;
    Graph2D[] g;
    JScrollPane graph_scroll_pane;
    JPanel graph_panel;
    int current_graph = 0;
    JFrame gw; //Graphing window
    GridBagLayout gridbag; //Layout for gw
    GridBagConstraints c;
    BufferedReader in;
    //      local copy of the maximum number of points.
    int m = 25;
    int entered = 0;
    String st;
    String mfile;
    Client client;
    JButton textual;
    TextArea text_area; //Have to use AWT so we can copy from it
    JFrame text_area_frame;

    public ReadData(BufferedReader in,Client environment)
    {
        //Minor stuff
        this.in = in;
        client = environment;
        try {p = Integer.parseInt(client.getParameter("PERIOD"));}
        catch (Exception e) {}
        m = 25; //Number of points before we start scrolling
        st = client.getParameter("TITLE");
        mfile = client.getParameter("MARKERS");

        //Initialize array of graphs & data sets
        d = new DataSet[MAXGRAPHS];
        g = new Graph2D[MAXGRAPHS];


        //Set up text output window
        //TODO: Get Real TimeStamp here
        text_area = new TextArea("Timestamp will be here\n",640,480);
        text_area.setEditable(false);
        text_area.setFont(new Font("MonoSpaced", Font.PLAIN, 10));
        //Create a panel so that the new window shows up
        // in a nice 640x480 area. Otherwise, it fullscreens.
        graph_panel = new JPanel(); //Ugly, we use this var twice
        graph_panel.setMinimumSize(new Dimension(640,480));
        graph_panel.setPreferredSize(new Dimension(640,480));
        graph_panel.setLayout(new GridLayout(1,1));
        graph_panel.add(text_area);
        //Add button to select all text
        /* textual = new JButton("Select All"); //Ugly, we use this twice
        textual.setPreferredSize(new Dimension(128,48));
        textual.setFont(new Font("SansSerif",Font.PLAIN,14));
        textual.addActionListener(client.action_man);
        textual.setActionCommand("Select all");
        //move down text_area_frame.getContentPane().add(textual);
        */
        text_area_frame = new JFrame("Text Output");
        text_area_frame.getContentPane().setLayout(new BoxLayout(text_area_frame.getContentPane(),BoxLayout.Y_AXIS));
        Box box = Box.createVerticalBox();
        box.add(Box.createVerticalStrut(80));
        text_area_frame.getContentPane().add(box);
        text_area_frame.getContentPane().add(graph_panel);

        //Give it a menu
        JPopupMenu.setDefaultLightWeightPopupEnabled(false);
        JMenuBar menuBar = new JMenuBar();
        JMenu menu = new JMenu("Edit");
        menu.setMnemonic('e');
        JMenuItem menuItem = new JMenuItem("Copy",KeyEvent.VK_C);
        menuItem.setMnemonic('c');
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));
        menuItem.addActionListener(client.action_man);
        menuItem.setEnabled(false); //Copy doesn't work for now
        menu.add(menuItem);
        menu.addSeparator();
        menuItem = new JMenuItem("Select All",KeyEvent.VK_A);
        menuItem.setMnemonic('a');
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.CTRL_MASK));
        menuItem.addActionListener(client.action_man);
        menu.add(menuItem);
        menuBar.add(menu);
        text_area_frame.setJMenuBar(menuBar);

        text_area_frame.pack();



        //xxxx
        //Build initial window
        gridbag = new GridBagLayout();
        c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;

        gw = new JFrame("Output"); //Frame for the whole window
        gw.getContentPane().setLayout(new GridLayout(1,1));
        graph_panel = new JPanel(); //Pane that will have graphs added to it
        graph_panel.setLayout(gridbag);
        graph_panel.setMinimumSize(new Dimension(640,480));
        graph_scroll_pane = new JScrollPane(graph_panel); //Make pane scrollable
        graph_scroll_pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        //Add button that will show the text output of model
        c.gridy = 0;
        c.weightx = 0.1;
        c.weighty = 0.1;
        textual = new JButton("Show Text Output");
        textual.setFont(new Font("SansSerif",Font.PLAIN,14));
        textual.addActionListener(client.action_man);
        textual.setActionCommand("textual");
        gridbag.setConstraints(textual,c);
        graph_panel.add(textual);
        //gw.getContentPane().add(textual);

        //Title the graph
        Label title = new Label(st, Label.CENTER);
        title.setFont(new Font("TimesRoman",Font.PLAIN,25));
        c.gridy = 1;
        c.weightx = 0.1;
        c.weighty = 0.1;
        gridbag.setConstraints(title,c);
        graph_panel.add(title);

        //Create the graph
        g[0] = new Graph2D();
        try {
            URL markersURL = new URL(client.getDocumentBase(),mfile);
            g[0].setMarkers(new Markers(markersURL));
        } catch(Exception e) {
            client.t.append("Marker URL failed: " + e.toString());
        }

        //Setup initial data set
        d[0] = new DataSet();
        d[0].linecolor   = new Color(255,0,0);
        //d[0].marker      = 1;
        d[0].marker      = 0;
        d[0].markercolor = new Color(100,100,255);
        g[0].attachDataSet(d[0]);
        //Title the axises in the .html document, with 'param ylabel0 = foo'
        // 'param xlabel0 = bar', for the first graph, ylabel1 = foobar
        // xlabel1 = happy for the second graph, and so on.
        Axis yaxis;
            yaxis = g[0].createAxis(Axis.LEFT);
            yaxis.setTitleText(client.getParameter("ylabel" + current_graph));
            yaxis.setTitleColor(Color.blue);
            yaxis.setTitleFont(new Font("Helvetica",Font.PLAIN,16));
            yaxis.setManualRange(false);
            yaxis.attachDataSet(d[0]);
            yaxis.setLabelFont(new Font("Helvetica",Font.PLAIN,16));
            yaxis.setLabelColor(Color.green);

        Axis xaxis;
            xaxis = g[0].createAxis(Axis.BOTTOM);
            xaxis.setTitleText(client.getParameter("xlabel" + current_graph));
            xaxis.setTitleColor(Color.black);
            xaxis.setTitleFont(new Font("Helvetica",Font.PLAIN,16));
            xaxis.setManualRange(false);
            xaxis.attachDataSet(d[0]);
            xaxis.setLabelFont(new Font("Helvetica",Font.PLAIN,16));
            xaxis.setLabelColor(Color.orange);

        //Set initial values
        g[0].zerocolor = new Color(0,255,0);
        g[0].borderTop = 100;
        g[0].borderBottom = 50;

        c.gridy = 3;
        c.weightx = 2; //2
        c.weighty = 5; //5
        gridbag.setConstraints(g[0],c);
        graph_panel.add(g[0]);
        gw.getContentPane().add(graph_scroll_pane);
    }

synchronized void barrier() {
    if (entered == 0) {
        entered = 1;
        try {wait();}
        catch(Exception e) {
            System.out.println(e.toString());
        }
    }
    else {
        notify();
    }
}

//xxxx
public void run()
{
    double data[] = new double[2];
    double last = 0; //Holds last x value to detect new graph
    int count = 0;
    int init = 0;
    if(d == null) return;
    if(d[current_graph] == null) return;
    if(g == null) return;
    if(g[current_graph] == null) return;
    if(gw == null) return;
    if(graph_scroll_pane == null) return;
    if(in == null) return;

    setPriority(Thread.MAX_PRIORITY);

    String line = new String(""); //Create new String here to avoid error
    StringBuffer all_lines = new StringBuffer("");
    try {
    while ((line = in.readLine()) != null) {
        //System.out.println(line); 
        if (line.equals("END")) {
        //System.out.println("End received."); 
            break;
            }
            all_lines.append(line + "\n");
        /* For Fast Performance & no graphing
            if (!line.equals("END"))
            continue;
            */

        StringTokenizer stoke = new StringTokenizer(line);
        String word = stoke.nextToken();

        if (init == 0) {
            d[current_graph].yaxis.maximum = 200.0;
            d[current_graph].yaxis.minimum = -200.0;
            //TODO - Figure out how to size this thing right
            //Maybe add some boxlayout struts
            gw.getRootPane().setPreferredSize(new Dimension(640,480));
            gw.getRootPane().setMinimumSize(new Dimension(300,300));
            //gw.pack();
            //gw.show();

            //Initialize last
            last = Double.valueOf(word).doubleValue();
            init = 1;
        }

        count++;
        try { 
            data[0] = Double.valueOf(word).doubleValue();
            word = stoke.nextToken();
            data[1] = Double.valueOf(word).doubleValue();
        } catch (NumberFormatException e) {}
        //if(count >= m) d.delete(0,0);
        //data[0] = count;
        //data[1] = (2.0*random.nextDouble() - 1.0)*15.0;

        //If old x > new x, we moved back in time
        //So start a new graph
        if (last > data[0]) { 
            //System.out.println("New Graph!");
            g[current_graph].repaint();
            current_graph++;

            //Sync these settings with one's above
            //This is a pretty hackish way to do this
            //Should probably turn this into a function
            g[current_graph] = new Graph2D();
            d[current_graph] = new DataSet();
            d[current_graph].linecolor   = new Color(255,0,0);
            d[current_graph].marker      = 0; //1
            d[current_graph].markercolor = new Color(100,100,255);
            g[current_graph].attachDataSet(d[current_graph]);

            Axis yaxis;
                yaxis = g[current_graph].createAxis(Axis.LEFT);
                yaxis.setTitleText(client.getParameter("ylabel" + current_graph));
                yaxis.setTitleColor(Color.pink);
                yaxis.setTitleFont(new Font("Helvetica",Font.PLAIN,16));
                yaxis.setManualRange(false);
                yaxis.attachDataSet(d[current_graph]);
                yaxis.setLabelFont(new Font("Helvetica",Font.PLAIN,16));
                yaxis.setLabelColor(Color.yellow);

            Axis xaxis;
                xaxis = g[current_graph].createAxis(Axis.BOTTOM);
                xaxis.setTitleText(client.getParameter("xlabel" + current_graph));
                xaxis.setTitleColor(Color.red);
                xaxis.setTitleFont(new Font("Helvetica",Font.PLAIN,16));
                xaxis.setManualRange(false);
                xaxis.attachDataSet(d[current_graph]);
                xaxis.setLabelFont(new Font("Helvetica",Font.PLAIN,16));
                yaxis.setLabelColor(Color.magenta);

            c.gridy = 3 + 2 * current_graph;
            c.weightx = 2;
            c.weighty = 4;
            gridbag.setConstraints(g[current_graph],c);
            graph_panel.setMinimumSize(new Dimension(400,400*current_graph));
            graph_panel.setPreferredSize(new Dimension(400,400*current_graph));
            graph_panel.add(g[current_graph]);
            //gw.pack();
        }
        last = data[0];

        try {
            d[current_graph].append(data,1);
        }
        catch (Exception e) {System.out.println("Error appending Data!");}

        //TODO Alarm class
        /**** TODO: Put this back in if we allow drawing...
        We don't need this when we are hiding the drawing
        if (count % 100 > 0) { continue; }
        else g[current_graph].repaint();

        try {    sleep(500); }
        catch(Exception e) {
            System.out.println(e.toString() + "\n");
        }
        ******/
    }
    //System.out.println("Thread Terminates\n");
    //System.out.flush();
    }
    catch(Exception e) {
        System.out.println("Error in Readdata.run: " + e.toString()); 
    }
    finally {
        text_area.append(all_lines.toString()); //Copy all output to a text window
        gw.pack();
        gw.show();
        barrier(); //Allow main() to finish
        }
} //End run
} //End class


/* TODO: Add in screen centering
Dimension dimForScreen = Toolkit.getDefaultToolkit().getScreenSize();
       
MyDialog dl = new MyDialog(new Frame());
        
Dimension dimForLog = dl.getSize();

dl.setLocation((dimForScreen.width / 2) - (dimForLog.width / 2),
                (dimForScreen.height / 2) - (dimForLog.height / 2));
*/
