import java.awt.*;

import java.awt.event.*;

import java.awt.image.*;

import java.applet.Applet;

import java.io.PrintWriter;

import java.io.BufferedReader;

import java.io.InputStreamReader;

import java.net.Socket;

import java.net.URL;


public class tbClient extends Applet implements WindowListener,
                    ActionListener, MouseListener, MouseMotionListener {


    static boolean PrintLog=false;

    public static void log(String line) {
        if (PrintLog) System.out.println(line);
    }

    public static void main(String[] args) {
        Frame F=new Frame("WebCam Monitor");
        tbClient C=new tbClient();
        F.setSize(750, 500);
        F.addWindowListener(C);
        F.add(C);
        C.init();
        C.PrintLog=true;
        C.start();
        F.setVisible(true);
    }

    // Client-server parameters

    static final String  imgLocDefault   = "http://webbot.bradley.edu/cgi-bin/bot1cam.cgi";
//  static final String  imgLocDeafault  = "http://localhost/cgi-bin/bot1cam.cgi";

                 String  imgLoc          = null;
    static final int     timSlMsDefault  = 1000;
                 int     timSlMs         = 1000;
    static final String  srvLocDefault   = "webbot.bradley.edu";
//  static final String  srvLocDefault   = "localhost"; // enable only for testing

                         String  srvLoc          = null;
    static final int     srvPortDefault  = 6811;
                 int     srvPort         = 6811;
    //  Robot control parameters

    static final int     timToutDefault  = 10;
                 int     timTout         = 10;
    static final int     senMoveDefault  = 25;
                 int     senMove         = 25;

    // GUI

    tbImage WC;
    tbTextArea LOG;
    Button bF0, bF1, bF2, bF3, bF4, bB1, bB2, bB3, bB4;
    Button bR15, bR30, bR45, bL15, bL30, bL45;
    Button stat;
    Label     lSens;
    Scrollbar sSens;

    // Connection

    Socket          SOCK=null;
    PrintWriter     SEND=null;
    BufferedReader  RECV=null;
    boolean         conn=false;

    private void Connect() {
        tbClient.log("START: connecting to WebBot server...");
        try {
            SOCK=new Socket(srvLoc, srvPort);
            SEND=new PrintWriter(SOCK.getOutputStream());
            RECV=new BufferedReader(new InputStreamReader(SOCK.getInputStream()));
            conn=true;
            tbClient.log("START: connected to WebBot server");
            LOG.start();
        } catch (Exception e) {
            SOCK=null;
            SEND=null;
            RECV=null;
            conn=false;
            tbClient.log("ERROR: connecting to WebBot server failed - "+e.getMessage());
        }
    }


    public void ReportConnectionLost() {
        tbClient.log("LOG: connection loss reproted");
        SOCK=null;
        SEND=null;
        RECV=null;
        conn=false;
        setEnabledControlPanel(false);
    }

    public void setEnabledControlPanel(boolean status) {
    	bF0.setEnabled(status);
    	bF4.setEnabled(status);
        bF3.setEnabled(status);
        bF2.setEnabled(status);
        bF1.setEnabled(status);
        bB4.setEnabled(status);
        bB3.setEnabled(status);
        bB2.setEnabled(status);
        bB1.setEnabled(status);
        bL15.setEnabled(status);
        bL30.setEnabled(status);
        bL45.setEnabled(status);
        bR15.setEnabled(status);
        bR30.setEnabled(status);
        bR45.setEnabled(status);
        if (status) {
            tbClient.log("LOG: control panel activated");
            stat.setLabel("status report");
        } else {
            tbClient.log("LOG: control panel deactivated");
            stat.setLabel("regain control");
        }
}

    private Panel CreateControlPanel() {
        Panel CP=new Panel();
        CP.setLayout(new GridLayout(3,1, 10, 10));

        // assuming that LOG is already created by start();

        CP.add(LOG);

        // create the direction buttons

        // and direction button subpanel

        bF0	 =new Button("STOP");
        bF4	 =new Button("Forward 4");
		bF3	 =new Button("Forward 3");
		bF2	 =new Button("Forward 2");
		bF1	 =new Button("Forward 1");
	    bB4	 =new Button("Back 4");
		bB3	 =new Button("Back 3");
		bB2	 =new Button("Back 2");
		bB1	 =new Button("Back 1");
        bL45 =new Button("L 45");
		bL30 =new Button("L 30");
		bL15 =new Button("L 15");
        bR45 =new Button("R 45");
		bR30 =new Button("R 30");
		bR15 =new Button("R 15");
        bF0.addActionListener(this);
        bF4.addActionListener(this);
		bF3.addActionListener(this);
		bF2.addActionListener(this);
		bF1.addActionListener(this);
		bB4.addActionListener(this);
		bB3.addActionListener(this);
		bB2.addActionListener(this);
		bB1.addActionListener(this);
		bL45.addActionListener(this);
		bL30.addActionListener(this);
		bL15.addActionListener(this);
		bR45.addActionListener(this);
		bR30.addActionListener(this);
		bR15.addActionListener(this);

        {
            Panel PSEN=new Panel();
            {
                PSEN.setLayout(new GridLayout(2,1, 0,0));
            }
            Panel PBUT=new Panel();
            PBUT.setLayout(new GridLayout(4,1, 5,5));

            Panel PBR1=new Panel();
            PBR1.setLayout(new GridLayout(1,3, 5,5));

            Panel PBR2=new Panel();
            PBR2.setLayout(new GridLayout(1,3, 5,5));

            Panel PBR3=new Panel();
            PBR3.setLayout(new GridLayout(1,3, 5,5));
	//Row One

            PBR1.add(bF1);
            PBR1.add(bF2);
            PBR1.add(bF3);
	    PBR1.add(bF4);
	//Row Two

            PBR2.add(bL45);
            PBR2.add(bL30);
            PBR2.add(bL15);
            PBR2.add(bR15);
            PBR2.add(bR30);
            PBR2.add(bR45);
	//Row Three

            PBR3.add(bB1);
            PBR3.add(bB2);
            PBR3.add(bB3);
            PBR3.add(bB4);
	// ???

            PBUT.add(PBR1);
            PBUT.add(PBR2);
            PBUT.add(PBR3);
            PBUT.add(PSEN);
            CP.add(PBUT);
        }

        {
            Panel SWPAN=new Panel();
            SWPAN.setLayout(new GridLayout(5,1, 5,5));
            stat=new Button("report status");
            stat.addActionListener(this);
            SWPAN.add(stat);
            SWPAN.add(new Label("WebBot v2.5 - BU ECE Department", Label.CENTER));
            CP.add(SWPAN);
        }

        return(CP);
    }


    private String getParamOrDefault(String name, String def) {
        String p=def;
        try {
            p=getParameter(name);
        } catch (Exception e) {
            p=def;
        }
        tbClient.log("PARAM: "+name+"="+p);
        return(p);
    }

    private int getParamOrDefault(String name, int def) {
        int p=def;
        try {
            p=Integer.parseInt(getParameter(name), 10);
        } catch (Exception e) {
            p=def;
        }
        tbClient.log("PARAM: "+name+"="+p);
        return(p);
    }

    public void init() {
        tbClient.log("INIT: getting applet parameters...");
        PrintLog=(getParamOrDefault("client-debug","false").equals("true"));
        imgLoc =getParamOrDefault("image-location",  imgLocDefault);
        timSlMs=getParamOrDefault("image-refresh",   timSlMsDefault);
        srvLoc =getParamOrDefault("server-location", srvLocDefault);
        srvPort=getParamOrDefault("server-port",     srvPortDefault);
        timTout=getParamOrDefault("control-tout",    timToutDefault);
        senMove=getParamOrDefault("control-sens",    senMoveDefault);

        tbClient.log("INIT: starting all services...");

        // Client command log text area

        LOG=new tbTextArea(this);

        // Transmitted image canvas

        WC=new tbImage(imgLoc, timSlMs);
        WC.addMouseListener(this);
        // WC.addMouseMotionListener(this); not used for now


        setLayout(new BorderLayout(20,20));

        Label title=new Label("Telerobotics Project", Label.CENTER);
        title.setFont(new Font("Helvetica", Font.BOLD, 24));
        title.setForeground(new Color(0,0,128));
        add("North",  title);
        add("East",  new Label(""));
        Panel CP=new Panel();
        CP.setLayout(new BorderLayout(20,20));
        CP.add("Center", WC);
        CP.add("East", CreateControlPanel());
        add("Center", CP);
        add("South", new Label(""));
        add("West",  new Label(""));
        setVisible(true);

        tbClient.log("START: new GUI created");
    }


    public void start() {
        tbClient.log("START: starting all services...");
        setEnabledControlPanel(true);
        WC.start();

        Connect(); // also starts LOG


        // enable controls only if connected!

        setEnabledControlPanel(conn);

        tbClient.log("START: all services started...");
        LOG.append("# services started");
    }

    public void stop() {
        tbClient.log("STOP: stopping all services...");
        setEnabledControlPanel(false);

        tbClient.log("STOP: stopping image acquisition...");
        WC.stop();

        tbClient.log("STOP: stopping command log...");
        LOG.stop();

        tbClient.log("STOP: disconnecting from WebBot server...");
        conn=false;
        try { SEND.close(); } catch (Exception e) { } SEND=null;
        try { SOCK.close(); } catch (Exception e) { } SOCK=null;
        try { RECV.close(); } catch (Exception e) { } RECV=null;

        LOG.append("# services commenced");
        try {
            Thread.currentThread().sleep(100);
        } catch (Exception e) { }
        tbClient.log("STOP: applet should have been stopped by now");
    }

    private void SendCommand(String command) {
        if (SOCK==null||SEND==null) return;
        try {
            SEND.println(command);
            SEND.flush();
            LOG.append("->"+command);
            tbClient.log("COMMAND: "+command+" sent");
        } catch (Exception e) {
            tbClient.log("ERROR: "+command+" "+e.getMessage());
            LOG.append("# connection to server lost");
            ReportConnectionLost();
        }
    }


// INTERFACE ActionListener -------

    public void actionPerformed(ActionEvent e) {
        if (e.getSource()==bF0) {
            SendCommand("botmove f 0");
        }
        else if (e.getSource()==bF4) {
            SendCommand("botmove f 4");
        }
        else if (e.getSource()==bF3) {
            SendCommand("botmove f 3");
        }
        else if (e.getSource()==bF2) {
            SendCommand("botmove f 2");
        }
        else if (e.getSource()==bF1) {
            SendCommand("botmove f 1");
        }
        else if (e.getSource()==bB4) {
            SendCommand("botmove b 4");
        }
        else if (e.getSource()==bB3) {
            SendCommand("botmove b 3");
        }
        else if (e.getSource()==bB2) {
            SendCommand("botmove b 2");
        }
        else if (e.getSource()==bB1) {
            SendCommand("botmove b 1");
        }
        else if (e.getSource()==bR45) {
            SendCommand("botmove r 3");
        }
        else if (e.getSource()==bR30) {
            SendCommand("botmove r 2");
        }
        else if (e.getSource()==bR15) {
            SendCommand("botmove r 1");
        }
        else if (e.getSource()==bL45) {
            SendCommand("botmove l 3");
        }
        else if (e.getSource()==bL30) {
            SendCommand("botmove l 2");
        }
        else if (e.getSource()==bL15) {
            SendCommand("botmove l 1");
        }
        else if (e.getSource()==stat) {
            if (!conn) {
                Connect();
                // enable controls only if connected!

                if (conn) setEnabledControlPanel(true);
            }
            SendCommand("botstat 0");
        }
        else {
            tbClient.log("COMMAND: unknown!");
            LOG.append("# unrecognized event");
        }
    }

// INTERFACE MouseListener --------

    public void mouseClicked(MouseEvent e)        {
        final Rectangle r=WC.getBounds();
        final int cx=e.getX();
        final int cy=e.getY();

                              // senMove is within 0..100 range

        final int sensitivity=20*senMove;
        final int px= sensitivity*(cx-r.width/2 )/r.width;
        final int py=-sensitivity*(cy-r.height/2)/r.height;

        SendCommand("botmove "+py+" "+px);
    }
    public void mousePressed(MouseEvent e)        {}
    public void mouseReleased(MouseEvent e)       {}
    public void mouseEntered(MouseEvent e)        {}
    public void mouseExited(MouseEvent e)         {}
// INTERFACE MouseMotionListener --

    public void mouseMoved(MouseEvent e)          {}
    public void mouseDragged(MouseEvent e)        {}

// INTERFACE AdjustmentListener -------

    public void adjustmentValueChanged(AdjustmentEvent e) {
        if (e.getSource()!=sSens) return;
        senMove=e.getValue();
        lSens.setText(senMove+"%");
    }

// INTERFACE WindowListener -------

    public void windowClosing(WindowEvent e)      { stop(); System.exit(0); }
    public void windowClosed(WindowEvent e)       {  }
    public void windowOpened(WindowEvent e)       {  }
    public void windowIconified(WindowEvent e)    { WC.pause();  }
    public void windowDeiconified(WindowEvent e)  { WC.resume(); }
    public void windowActivated(WindowEvent e)    {  }
    public void windowDeactivated(WindowEvent e)  {  }
}









class tbImage extends Canvas implements Runnable {

    // parameters

    private String imgLoc;
    private int    tmSlMs;

    // variables

    private MediaTracker MT     = null;
    private Image    webcam     = null;
    private Thread   process    = null;
    private boolean  pause      = false;

    tbImage(String loc, int wait) {
        super();
        imgLoc=loc;
        tmSlMs=wait;
        MT=new MediaTracker(this);
    }

    public void start() {
        if (process!=null) return;
        process=new Thread(this,"Image Update");
        process.start();
        tbClient.log("IMAGE: started...");
    }

    public void pause() {
        tbClient.log("IMAGE: update paused...");
        pause=true;
    }

    public void resume() {
        tbClient.log("IMAGE: update resumed...");
        pause=false;
    }

    public void stop() {
        tbClient.log("IMAGE: thread stop requested");
        if (process==null) return;
        Thread tokill=process;
        process=null;
        tokill.interrupt();
        try {
            tokill.join(100);
        } catch (Exception e) {
            tbClient.log("IMAGE: thread cannot stop in 100ms");
        }
    }

    public void run() {
        while(Thread.currentThread()==process) {
            if (pause) {
                try {
                    Thread.currentThread().sleep(100);
                } catch (Exception e) { }
            } else {
                try {
                    tbClient.log("IMAGE: downloading...");
                    // remove the previous image from cache

                    if (webcam!=null) webcam.flush();
                    // get a new image by location

                    webcam = getToolkit().getImage(new URL(imgLoc));
                    // wait for downloading the image

                    // -- extra time added to thread.sleep()

                    MT.addImage(webcam,0);
                    MT.waitForAll();
                    MT.removeImage(webcam,0);
                    tbClient.log("IMAGE: refreshing...");
                } catch(Exception e) {
                    webcam=null;
                    tbClient.log("IMAGE: "+e.getMessage()+"!");
                }
                repaint();
                try {
                    tbClient.log("IMAGE: sleeping...");
                    Thread.currentThread().sleep(tmSlMs);
                } catch (Exception e) { }
            }
        }
        tbClient.log("IMAGE: thread commenced");
    }

    public void paint(Graphics g) {
        final Rectangle r=getBounds();
        g.drawRect(1,1,r.width-2,r.height-2);
        if (webcam!=null) {
            int wi=webcam.getWidth(this);
            int hi=webcam.getHeight(this);
            int w1=(r.width -wi)/2;
            int h1=(r.height-hi)/2;

            int w2=w1+wi;
            int h2=h1+hi;
            g.clearRect(2, 2, r.width-3,h1-3);
            g.clearRect(2, h1,w1-3,hi);
            g.clearRect(w2,h1,w1-3,hi);
            g.clearRect(2,h2+1,r.width-3,h1-3);

            g.drawImage(webcam, w1, h1, this);
        }
    }

    public void update(Graphics g) {
        paint(g);
    }
}









class tbTextArea extends TextArea implements Runnable {

    private tbClient PARENT  = null;
    private Thread   process = null; // not running yet


    tbTextArea(tbClient parent) {
        super("", 4, 10, TextArea.SCROLLBARS_VERTICAL_ONLY);
        setEditable(false);
        setFont(new Font("Times", Font.BOLD, 10));
        PARENT=parent;
    }


    public void append(String line) {
        super.append(line+"\n");
    }


    public void start() {
        if (process!=null) return; // do not start if already running

        process=new Thread(this,"Command Log");
        process.start();
        tbClient.log("LOG: thread started");
    }

    public void stop() {
        tbClient.log("LOG: thread stop requested");
        process=null;
    }

    public void run() {
        try {
            while (Thread.currentThread()==process) {
                String line=(PARENT.RECV).readLine();
                append(line);
//tbClient.log("LOG: "+line);

            }
        } catch (Exception e) {
            append("* connection lost");
            tbClient.log("LOG: connection lost - disabling controls");
        }
        process=null;
        PARENT.ReportConnectionLost();
        tbClient.log("LOG: thread commenced");
    }

}

syntax highlighted by Code2HTML, v. 0.8.11