// File Name:   campict.java
//
// Description: Download and display a list of images with or without
//              reflesh/reload. Mainly for use with Axis Neteye 200
//-----------------------------------------------------------------------------
// History
//
// Date   Name           Ver        Changes  option
// ----   ----           ---        -------
// 960513 Le Chi Kiet    1.xx       --
// 961022 Le Chi Kiet    2.xx       New layout
// 961130 Le Chi Kiet    3.1x       New design
// 970130 Le Chi Kiet    3.2x       Add byPassCamCache
// 970214 Le Chi Kiet    3.3x       Take away byPassCamCache
//                                  Add OpenDoor
// 970130 Le Chi Kiet    3.4x       Add noStatusText
//                                  Add manual garbage collector.
// ****************************************************************************
//  © Copyright 1996-1997 Axis communications AB, Lund, SWEDEN
//  All rights reserved.
//  Email: Kiet.LeChi@axis.com or neteye@axis.com

import java.awt.*;
import java.applet.Applet;
import java.net.*;
import java.util.*;
import java.io.*;

public class campict extends Applet implements Runnable
{
  //{{DECLARE_CONTROLS
  //}}
  long nError=0;

  String version    = "Version 3.44 ";
  String homeLink   = "http://www.axis.com/neteye/campict/campict.html ";
  String binaryFile = "campict ";

  String defaultImage      = "fullsize.jpg|halfsize.jpg";
  String defaultImageLabel = "fullsize.jpg  352×288 pixels.|halfsize.jpg  176×144 pixels.";

  URL codeBase;
  Vector imageList   = new Vector();
  Vector imageLabels = new Vector();
  boolean imgDefault = false;

  int current = 0, amountOfImages = 0;
  int interval = 0;
  int waitInt = 50;  //ms
  int maxLoadTime = 90000; //ms (here 90s)
  int msTimeLeft;
  int doorOpenDelay;

  Color bgColor = Color.white;
  Image currentImage = null;
  Vector imageBank   = new Vector();
  Thread engine;
  Image  axislogo1 = null;
  Image  axislogo2 = null;
  String axislogoLoc1 = "axislogo.gif";
  String axislogoLoc2 = "/pub/images/axislogo.gif";
  int numOfLoaded = 0;

  boolean initError = false;
  boolean loaded    = false;
  boolean standby   = false;
  boolean imgError  = false;
  boolean allLoaded = false;
  boolean showAbout = false;

  int frameWidth;
  int frameHeight;
  Image offScrImage;
  Graphics offScrGC;
  boolean noRefresh, noFrame, imgFitFrame, noOptions, noStatusText;

  Label m_status;
  Button m_option, m_about, m_openDoor;
  String statusText = " ";

  Panel p2;
  int p2Width  = 330;
  int p2Height = 240;
  int p2x, p2y;
  Label m_passLabel1, m_passLabel2, m_passLabel3;
  TextField m_userName, m_password;
  Button m_passOk, m_passCancel;
  String userName = "", password = "";

  Panel p3;
  int p3Width  = p2Width;
  int p3Height = p2Height;
  int p3x, p3y;
  Button m_goTo, m_optionOk, m_optionCancel, m_optionNext, m_optionPrev;
  Checkbox check[];
  int checkList[][],  checkListNumber=0, maxShowCheck=3;
  Label m_labelInterval, m_labelDoorDelay;
  Label m_optionLabel2, m_optionLabel3, m_optionLabel4;
  TextField m_interval, m_doorDelay;
  Checkbox m_imgFitFrame, m_noFrame;
  boolean showStatus[];
  boolean openDoorNotValid = false;

  String file;
  DataInputStream in;
  DataOutputStream out;
  boolean unauthorized = false;

  //--------------------------------------------------------
  // Init of the thread
  //--------------------------------------------------------
  public synchronized void init()
  {
    //{{INIT_CONTROLS
    //}}

    String string = null;
    int x ,y;

    System.out.println("Init applet...");

      // get color param from html-file
    bgColor = getParam("bgcolor", bgColor);
    super.init();

      // get "codeBase" from html-file
    codeBase  = getCodeBase();
    axislogo1 = getImage(codeBase, axislogoLoc1);
    prepareImage(axislogo1, this);
    axislogo2 = getImage(codeBase, axislogoLoc2);
    prepareImage(axislogo2, this);

      // get imageList and imageLabels
    string = getParameter("DataFile");
    if (string!=null)
    {
      parseImageDataFromFile(string);
    }
    else
    {
      parseImageDataFromParam();
    }

      // get int param from html-file
    interval      = getParam("Interval", 3) * 1000;
    doorOpenDelay = getParam("OpenDoor", 0) * 1000;

      // get boolean param from html-file
    noRefresh      = getParam("NoRefresh");
    imgFitFrame    = getParam("ImgFitFrame");
    noFrame        = getParam("NoFrame");
    noOptions      = getParam("NoOptions");
    noStatusText   = noOptions;

      // get the size from html-file
    frameWidth = size().width;
    frameHeight= noOptions ? size().height : size().height - 30;
    p2x = (frameWidth  - p2Width)  / 2;
    p2y = (frameHeight - p2Height) / 2;
    p3x = p2x;
    p3y = p2y;

      // define layout
    this.setLayout(null);
    y = frameHeight + 4;
    x = frameWidth - 130;
    m_about = new Button(" About ");
    m_about.reshape(x,y,60,23);
    m_option = new Button("Options");
    m_option.reshape(x+70,y,60,23);
    m_status = new Label(" ");
    m_status.reshape(5,y,x-20,23);
    m_status.setBackground(bgColor);
    initPasswordPanel();
    initOptionPanel();

    if (!noOptions)
    {
      this.add(m_about);
      this.add(m_option);
      this.add(m_status);
    }

      // create double buffer
    try
    {
      offScrImage = createImage(frameWidth, frameHeight+30);
      offScrGC = offScrImage.getGraphics();
      offScrGC.setColor(bgColor);
      offScrGC.fillRect(0, 0, frameWidth, frameHeight);
    } catch (Exception e)
      {
    	System.out.println("[init()] "+e);
        initError = true;
      }

    current = 0;
  }


  //------------------------------------------------------------------------
  //  get color param from html-file
  //------------------------------------------------------------------------
  public synchronized Color getParam(String param, Color defaultColor)
  {
    String string;

    string = getParameter("bgcolor");
    if (   string!=null
        && string.length()==7
        && string.charAt(0)=='#')
    {
      return (new Color(Integer.parseInt(string.substring(1,7),16)));
    }
    return defaultColor;
  }


  //------------------------------------------------------------------------
  //  get boolean param from html-file
  //------------------------------------------------------------------------
  public synchronized boolean getParam(String param)
  {
    String string;

    string = getParameter(param);
    if (string!=null)
    {
      return (Boolean.valueOf(string).booleanValue());
    }
    return false;
  }


  //------------------------------------------------------------------------
  //  get int param from html-file
  //------------------------------------------------------------------------
  public synchronized int getParam(String param, int defaultInt)
  {
    String string;

    string = getParameter(param);
    return ((string==null) ? defaultInt : Integer.parseInt(string));
  }


  //------------------------------------------------------------------------
  //  get imageList and imageLabels from file
  //------------------------------------------------------------------------
  public synchronized void parseImageDataFromFile(String dataFile)
  {
    URL   dataFileURL = null;
    InputStream istream = null;
    DataInputStream dStream = null;
    String string;
    StringTokenizer imageStr;

    try
    {
      dataFileURL = new URL(getDocumentBase(), dataFile);
      istream = dataFileURL.openStream();
    } catch(MalformedURLException e)
      {
        initError   = true;
        dataFileURL = null;
        return;
      }
      catch(IOException e)
      {
        initError = true;
        System.out.println("Unable to open file " + dataFile);
        return;
      }

    dStream = new DataInputStream(istream);
    current = 0;
    try
    {
      string = dStream.readLine();
      while (string!=null)
      {
        imageStr = new StringTokenizer(string, "|");
        string = imageStr.nextToken();
        imageList.addElement(string);
        if (imageStr.hasMoreTokens())
        {
          string = imageStr.nextToken();
        }
        imageLabels.addElement(string);
        string = dStream.readLine();
        current++;
      }
    amountOfImages = current;
    } catch(IOException e)
      {
        initError = true;
        System.out.println(e+"Parsing data source failed!");
      }
  }


  //------------------------------------------------------------------------
  //  get imageList and imageLabels from html-param
  //------------------------------------------------------------------------
  public synchronized void parseImageDataFromParam()
  {
    String string;
    StringTokenizer imageStr;

      // get Image's filename from html-file
    string = getParameter("Image");
    if (string==null)
    {
      imgDefault = true;
      string = defaultImage;
    }
    imageStr = new StringTokenizer(string, "|");
    amountOfImages = imageStr.countTokens();
    while (imageStr.hasMoreTokens())
    {
      string = imageStr.nextToken();
      imageList.addElement(string);
      imageLabels.addElement(string);
    }

     // get Image's Label from html-file
    string = getParameter("ImageLabel");
    if (string==null && imgDefault)
    {
      string = defaultImageLabel;
    }
    if (string!=null)
    {
      StringTokenizer imageLabelStr = new StringTokenizer(string, "|");
      current = 0;
      while (imageLabelStr.hasMoreTokens())
      {
        string = imageLabelStr.nextToken();
        if (!(string.equals(" ")))
        {
          imageLabels.insertElementAt(string, current);
        }
        current++;
      }
    }
  }


  //--------------------------------------------------------
  // init password panel
  //--------------------------------------------------------
  public synchronized void initPasswordPanel()
  {
    int x, y;

    p2 = new Panel();
    p2.setBackground(Color.lightGray);
    p2.setLayout(null);
    p2.reshape(p2x, p2y, p2Width, p2Height);

    m_passLabel1 = new Label("Please enter your authentication information.");
    m_passLabel1.reshape(x=10, y=35, p2Width-y, 17);
    p2.add(m_passLabel1);

    m_passLabel2 = new Label("User name:", Label.RIGHT);
    m_passLabel2.reshape(x+=20, y+=50, 80, 17);
    p2.add(m_passLabel2);
    m_userName = new TextField(userName);
    m_userName.reshape(x+90, y-3, 150, 22);
    p2.add(m_userName);

    m_passLabel3 = new Label("Password:", Label.RIGHT);
    m_passLabel3.reshape(x, y+=40, 80, 17);
    p2.add(m_passLabel3);
    m_password = new TextField(password);
    m_password.reshape(x+90, y-3, 150, 22);
    m_password.setEchoCharacter('*');
    p2.add(m_password);

    x = 105;
    y = 185;
    m_passOk = new Button("OK");
    m_passOk.reshape(x, y, 55, 23);
    p2.add(m_passOk);
    m_passCancel = new Button("Cancel");
    m_passCancel.reshape(x+=65, y, 55, 23);
    p2.add(m_passCancel);
  }


  //--------------------------------------------------------
  // init option panel
  //--------------------------------------------------------
  public synchronized void initOptionPanel()
  {
    int i, j;
    int x, y;

    p3 = new Panel();
    p3.setBackground(Color.lightGray);
    p3.setLayout(null);
    p3.reshape(p3x, p3y, p3Width, p3Height);

    x = 15;
    y = 15;

    m_openDoor = new Button("OpenDoor");
    m_openDoor.reshape(x-4, y, 75 ,25);
    p3.add(m_openDoor);
    if (doorOpenDelay==0)
    {
      m_openDoor.disable();
    }

    m_goTo = new Button("Visit homepage of campict.java");
    m_goTo.reshape(x+83, y, 225, 25);
    p3.add(m_goTo);

    m_labelDoorDelay = new Label("OpenDoor delays (s):", Label.RIGHT);
    m_labelDoorDelay.reshape(x-=25, y+=43, 138, 17);
    p3.add(m_labelDoorDelay);
    m_doorDelay = new TextField(Integer.toString((int)doorOpenDelay/1000));
    m_doorDelay.reshape(x+140, y-3, 30, 22);
    p3.add(m_doorDelay);
    if (doorOpenDelay==0)
    {
      m_doorDelay.disable();
    }

    m_labelInterval = new Label("Loading interval (s):", Label.RIGHT);
    m_labelInterval.reshape(x, y+=26, 138, 17);
    p3.add(m_labelInterval);
    m_interval = new TextField(Integer.toString((int)interval/1000));
    m_interval.reshape(x+=140, y-3, 30, 22);
    p3.add(m_interval);

    m_noFrame = new Checkbox("No frame");
    m_noFrame.reshape(x+=40, y-=23, 150, 17);
    p3.add(m_noFrame);
    m_noFrame.setState(noFrame);

    m_imgFitFrame = new Checkbox("Fit image to frame");
    m_imgFitFrame.reshape(x, y+=20, 150, 17);
    p3.add(m_imgFitFrame);
    m_imgFitFrame.setState(imgFitFrame);

    m_optionLabel4 = new Label("Choose Image(s) ["+amountOfImages+"]:");
    m_optionLabel4.reshape(x=5, y+=33, 191, 17);
    p3.add(m_optionLabel4);

    x = 40;
    y = 205;
    m_optionOk = new Button("OK");
    m_optionOk.reshape(x, y, 55, 23);
    p3.add(m_optionOk);
    m_optionCancel = new Button("Cancel");
    m_optionCancel.reshape(x+=65, y, 55, 23);
    p3.add(m_optionCancel);
    m_optionPrev = new Button("<");
    m_optionPrev.reshape(x+=65, y, 55, 23);
    p3.add(m_optionPrev);
    m_optionNext = new Button(">");
    m_optionNext.reshape(x+=65, y, 55, 23);
    p3.add(m_optionNext);

    check      = new Checkbox[amountOfImages];
    showStatus = new boolean[amountOfImages];
    int nGrp   = (int)Math.ceil(((double)amountOfImages)/((double)maxShowCheck));
    checkList  = new int[nGrp][4];
    current    = 0;

    x = 25;
    y = 137;
    for (i=0; i<nGrp; i++)
    {
      if (i==0)
      {
        checkList[0][0] = 0;  //first list's has no prev
      }
      else
      {
        checkList[i-1][3] = 1;       //prev list has next
        checkList[i][0]   = 1;       //this list has prev
        checkList[i][1]   = current; //first IMAGE in this list
      }
      for (j=0; j<maxShowCheck && current<amountOfImages; j++, current++)
      {
        check[current] = new Checkbox((String)imageLabels.elementAt(current));
        check[current].reshape(x, y+j*20, 225, 17);
        check[current].setState(true);
        showStatus[current] = check[current].getState();
      }
      checkList[i][2] = current-1;  //last image in this list
      checkList[i][3] = 0;       //this list has no next
    }
    if (imgDefault)
    {
        //uncheck halfsize.jpg an zoom.jpg
      for (i=1; i<amountOfImages; i++)
      {
        check[i].setState(false);  //uncheck halfsize.jpg
        showStatus[i] = check[i].getState();
      }
    }
    addCheckBoxes();
  }


  //--------------------------------------------------------
  // Start the thread
  //--------------------------------------------------------
  public synchronized void start()
  {
    if (initError)
    {
      m_status.setText("Fail in init!");
      return;
    }
      // start the thread
    if (engine==null)
    {
      engine = new Thread(this);
      engine.start();
      super.start();
      System.out.println("Start applet...");
      standby  = false;
      showAbout = false;
    }
  }

  //--------------------------------------------------------
  // Stop the thread
  //--------------------------------------------------------
  public synchronized void stop()
  {
    if (engine!=null)
    {
      updateStatus(" ");
      standby  = true;
      engine.stop();
      engine = null;
      super.stop();
      System.out.println("Stop applet...");
      System.out.println("--------------");
    }
  }


  //--------------------------------------------------------
  // Run the thread => ACTION!
  //--------------------------------------------------------
  public void run()
  {
    int t1, t2, loadTime;
    Event event;

    m_status.setText(" ");
    clickedAbout();
    System.out.println("Run applet...");
    Thread me = Thread.currentThread();
    me.setPriority(Thread.MIN_PRIORITY);

    while(engine==me && !standby)
    {
      t1 = (int)System.currentTimeMillis();
      if (!showAbout)
      {
        if (!nextImageLoc())
        {
          standby = true;
        }
      }
      getNewImage();
      numOfLoaded++;

      if (!(imgError || standby || showAbout))
      {
        paintOffScrGC(); //write to dubble buffer
        repaint();       // write to screen
      }

      t2 = (int)System.currentTimeMillis();
      loadTime = t2 - t1;
      msTimeLeft = interval - loadTime;
      msTimeLeft = (msTimeLeft<150) ? 150 : msTimeLeft;

      try
      {
        if (imgError)
        {
          nError++;
          m_status.setText("Fail in loading image " + (current+1) +" !");
        }
        else
        {
          while (msTimeLeft>100 && !standby)
          {
            updateStatus("  Time-out: "+ ((msTimeLeft+500)/1000) + " s");
            engine.sleep((msTimeLeft<1000) ? msTimeLeft : 800); // timeout with 1 s or remaind time
            msTimeLeft -= 1000;   // countdown
          }
        }
      } catch (InterruptedException e)
        {
          System.out.println("[run()]"+e);
        }
      updateStatus(" ");
      System.out.println("[ "+nError+" | "+numOfLoaded+" | "+(current+1)+" | "+loadTime+" ]");
      System.gc();
    }
  }


  //--------------------------------------------------------
  // find the next location
  //--------------------------------------------------------
  public synchronized boolean nextImageLoc()
  {
    int oldLoc = current;

      // check if this one want to show
    do
    {
      current++;  // next image

        // keep the counter on track!
      if (current+1>amountOfImages)
      {
        current = 0;
      }
    } while (!(showStatus[current] || current==oldLoc));

    if (!showStatus[current])
    {
      m_status.setText("No camera selected!");
      return false;
    }
    else
    {
      return true;
    }
  }


  //--------------------------------------------------------
  // get a new image
  //--------------------------------------------------------
  public synchronized void getNewImage()
  {
    if (noRefresh)
    {
      if (!allLoaded)
      {
        getNewImageFromNet();
        if (!imgError)
        {
          imageBank.addElement(currentImage);
        }
        else
        {
          imageBank.addElement(null);
        }

        if (current+1==amountOfImages)
        {
          allLoaded = true;
        }
      }
      else
      {
        getAnImageFromBank();
      }
    }
    else
    {
      getNewImageFromNet();
    }
  }


  //--------------------------------------------------------
  // get a new image from bank
  //--------------------------------------------------------
  public synchronized void getAnImageFromBank()
  {
    currentImage = (Image)imageBank.elementAt(current);
    showAbout = false;
  }


  //--------------------------------------------------------
  // get a new image from net
  //--------------------------------------------------------
  public synchronized void getNewImageFromNet()
  {
    int loadTime, t1;
    MediaTracker mTracker = new MediaTracker(this);

    try
    {
      updateStatus("["+nError+"|"+numOfLoaded+"]"+"  Loading image ...      ");
      currentImage = getImage(codeBase, (String)imageList.elementAt(current));
      currentImage.flush();
      mTracker.addImage(currentImage, numOfLoaded);     // use for control of loading
      mTracker.checkID(numOfLoaded, true);
      loadTime = 0;
      loaded = false;
      showAbout = false;

      t1 = (int)System.currentTimeMillis();
      while (loadTime<maxLoadTime && !loaded && !standby && !showAbout)  // if stop-button not activated
      {
        loadTime = (int)System.currentTimeMillis() - t1;
        loaded = mTracker.checkID(numOfLoaded, true);
      }
      System.out.print("["+loadTime+"] ");

      if (!(standby || showAbout))
      {
        imgError = (mTracker.statusID(numOfLoaded, true)==MediaTracker.COMPLETE) ? false : true;
      }

      if (standby || showAbout || imgError)
      {
        currentImage = null;
      }
    } catch(Exception e)
      {
    	System.out.println("[getNewImageFromNet()] "+e);
      }
    updateStatus(" ");
  }


  //--------------------------------------------------------
  // update image
  //--------------------------------------------------------
  public synchronized void update(Graphics g)
  {
    paint(g);
  }


  //--------------------------------------------------------
  // Update screen
  //--------------------------------------------------------
  public synchronized void paint(Graphics g)
  {
    g.drawImage(offScrImage, 0, 0, this);  //update screen;

      //enable button About after showing an image
    if (!(showAbout || standby))
    {
      m_about.enable();
    }
  }


  //--------------------------------------------------------
  // Update dubble buffer
  //--------------------------------------------------------
  public synchronized void paintOffScrGC()
  {
    int x=5, y=5, imWidth=0, imHeight=0, newWidth, newHeight;

    if (loaded && !showAbout && !imgError)
    {
      m_status.setText((String)imageLabels.elementAt(current));
      offScrGC.setColor(bgColor);
      offScrGC.fillRect(0, 0, frameWidth, frameHeight+30);

      while (imWidth<=0 || imHeight<=0)
      {
        imWidth = currentImage.getWidth(this);
        imHeight = currentImage.getHeight(this);
      }

      if (imgFitFrame)
      {
          // newWidth/newHeight = imWidth/imHeight
        newWidth  = frameWidth - 9;
        newHeight = (newWidth * imHeight / imWidth);
        if (newHeight>(frameHeight-9))
        {
          newHeight = frameHeight - 9;
          newWidth  = newHeight * imWidth / imHeight;
        }
        imWidth  = newWidth;
        imHeight = newHeight;
        x = (frameWidth  - imWidth)  / 2;
        y = (frameHeight - imHeight) / 2;
        offScrGC.drawImage(currentImage, x, y, imWidth, imHeight, this);
      }
      else
      {
        x = (frameWidth  - imWidth)  / 2;
        y = (frameHeight - imHeight) / 2;
        offScrGC.drawImage(currentImage, x, y, this);
      }

      imWidth  += 9;
      imHeight += 9;
      if (!noFrame)
      {
        x = (x-5<0) ? 5 : x;
        y = (y-5<0) ? 5 : y;

        imWidth  = (imWidth>frameWidth)   ? frameWidth  : imWidth;
        imHeight = (imHeight>frameHeight) ? frameHeight : imHeight;
        draw3DFrame(offScrGC, x-5, y-5, imWidth, imHeight);
      }
    }
  }


  //--------------------------------------------------------
  // update status
  //--------------------------------------------------------
  public synchronized void updateStatus(String inText)
  {
    if (inText != null)
    {
      statusText = inText;
    }

    if (!noStatusText)
    {
      this.showStatus(statusText);
    }
  }


  //--------------------------------------------------------
  // Catch events
  //--------------------------------------------------------
  public boolean handleEvent(Event event)
  {
    if (event.id == Event.KEY_RELEASE && event.target == m_userName)
    {
      keyReleaseUserName(event);
      return true;
    }
    else if (event.id == Event.KEY_RELEASE && event.target == m_password)
    {
      keyReleasePassword(event);
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_passOk)
    {
      clickedPassOk(event);
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_passCancel)
    {
      clickedPassCancel();
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_openDoor)
    {
      clickedOpenDoor(event);
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_goTo)
    {
      clickedGoTo();
      return true;
    }
    else if (event.id == Event.KEY_RELEASE && event.target == m_doorDelay)
    {
      keyReleaseDoorDelay(event);
      return true;
    }
    else if (event.id == Event.KEY_RELEASE && event.target == m_interval)
    {
      keyReleaseInterval(event);
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_about)
    {
      clickedAbout();
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_option)
    {
      clickedOption();
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_optionOk)
    {
      clickedOptionOk(event);
      start();
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_optionCancel)
    {
      clickedOptionCancel();
      start();
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_optionNext)
    {
      clickedOptionNext();
      return true;
    }
    else if (event.id == Event.ACTION_EVENT && event.target == m_optionPrev)
    {
      clickedOptionPrev();
      return true;
    }
    return super.handleEvent(event);
  }


  //--------------------------------------------------------
  // what to do when key release from interval-editbox
  //--------------------------------------------------------
  public synchronized boolean keyReleaseInterval(Event ev)
  {
      // to do: put event handler code here.
    int intTmp;

    m_status.setText(" ");
    if (ev.key == '\n')  //if input = 'Enter'
    {
      try
      {
        intTmp = Integer.parseInt(m_interval.getText())*1000;
      } catch (NumberFormatException e)
        {
          m_status.setText("No decimal!");
          System.out.println("[keyReleaseInterval()] "+e);
          return false;
        }
      m_optionOk.requestFocus();
      if (!(interval==intTmp || standby))
      {
        System.out.println("New Interval!");
      }
    }
    return true;
  }


  //--------------------------------------------------------
  // always overwrite browsers statusfield
  //--------------------------------------------------------
  public boolean mouseMove(Event ev, int x, int y)
  {
    if (standby)
    {
      mouseEnterP3(ev);
      return true;
    }
    else
    {
      updateStatus(null);
      return true;
    }
  }


  //--------------------------------------------------------
  // what to do when key release from interval-editbox
  //--------------------------------------------------------
  public synchronized boolean keyReleaseDoorDelay(Event ev)
  {
      // to do: put event handler code here.
    int intTmp;

    m_status.setText(" ");
    if (ev.key == '\n')  //if input = 'Enter'
    {
      m_openDoor.requestFocus();
    }
    try
    {
      intTmp = Integer.parseInt(m_doorDelay.getText())*1000;
    } catch (NumberFormatException e)
      {
        m_status.setText("An integer requires!");
        System.out.println("[keyReleaseDoorDelay()] "+e);
        return false;
      }
    if (doorOpenDelay!=intTmp)
    {
      System.out.println("New OpenDoor delay time!");
      doorOpenDelay = intTmp;
    }
    return true;
  }


  //--------------------------------------------------------
  // What to do when button About activated
  //--------------------------------------------------------
  public void clickedAbout()
  {
      // to do: put event handler code here.
    showAbout = true;
    m_about.disable();
    m_status.setText(" ");

    int x, y;

    Font f0 = offScrGC.getFont();
    Font f1 = new Font("Courier New", Font.BOLD,  30);
    Font f2 = new Font("Courier New", Font.BOLD,  24);
    Font f3 = new Font("Courier New", Font.BOLD,  22);
    Font f4 = new Font("Courier New", Font.PLAIN, 14);
    Font f5 = new Font("Courier New", Font.BOLD,  34);

    FontMetrics fm0 = offScrGC.getFontMetrics(f0);
    FontMetrics fm1 = offScrGC.getFontMetrics(f1);
    FontMetrics fm2 = offScrGC.getFontMetrics(f2);
    FontMetrics fm3 = offScrGC.getFontMetrics(f3);
    FontMetrics fm4 = offScrGC.getFontMetrics(f4);
    FontMetrics fm5 = offScrGC.getFontMetrics(f5);

    String t6 = binaryFile;
    String t7 = version;
    String t8 = "Copyright © 1996-1997 Axis Communications AB ";
    String t5 = "Email: neteye@axis.com";

    offScrGC.setColor(bgColor);
    offScrGC.fillRect(0, 0, frameWidth, frameHeight);

    offScrGC.setColor(Color.lightGray);
    offScrGC.drawRoundRect(0, 0, frameWidth,   frameHeight,   25, 25);
    offScrGC.drawRoundRect(1, 1, frameWidth-2, frameHeight-2, 25, 25);

    y = frameHeight/2 - 120;
    y = y<0 ? 0 : y;
    x = (frameWidth - axislogo1.getWidth(this)) / 2;
    offScrGC.drawImage(axislogo1, x, y, this);
    x = (frameWidth - axislogo2.getWidth(this)) / 2;
    offScrGC.drawImage(axislogo2, x, y, this);

    offScrGC.setFont(f2);
    offScrGC.setColor(Color.blue);
    x = (frameWidth - fm2.stringWidth(t6)) / 2;
    offScrGC.drawString(t6, x, (y+=90));

    offScrGC.setFont(f3);
    x = (frameWidth - fm3.stringWidth(t7)) / 2;
    offScrGC.setColor(Color.black);
    offScrGC.drawString(t7, x+3, (y+=42));

    offScrGC.setFont(f4);
    offScrGC.setColor(Color.gray);
    offScrGC.drawLine(30, (y+=30), frameWidth-31, y);
    offScrGC.drawLine(30, y-2,     frameWidth-31, y-2);

    x = (frameWidth - fm4.stringWidth(t8)) / 2;
    offScrGC.setColor(Color.black);
    offScrGC.drawString(t8, x+3, y+=30);

    x = (frameWidth - fm4.stringWidth(t5)) / 2;
    offScrGC.setColor(Color.blue);
    offScrGC.drawString(t5, x+3, y+=30);

    repaint();
  }


  //--------------------------------------------------------
  // add checkboxes to optionsmenu
  //--------------------------------------------------------
  public void addCheckBoxes()
  {
    if (checkList[checkListNumber][0]==1)
    {
      m_optionPrev.enable();
    }
    else
    {
      m_optionPrev.disable();
    }

    if (checkList[checkListNumber][3]==1)
    {
      m_optionNext.enable();
    }
    else
    {
      m_optionNext.disable();
    }

    for (int i=checkList[checkListNumber][1]; i<=checkList[checkListNumber][2]; i++)
    {
      p3.add(check[i]);
    }
  }


  //--------------------------------------------------------
  // remove checkboxes to optionsmenu
  //--------------------------------------------------------
  public void removeCheckBoxes()
  {
    for (int i=checkList[checkListNumber][1]; i<=checkList[checkListNumber][2]; i++)
    {
      p3.remove(check[i]);
    }
  }


  //--------------------------------------------------------
  // What to do when button Option activated
  //--------------------------------------------------------
  public void draw3DFrame(Graphics g, int x, int y, int width, int height)
  {
      //create 3-D frame
    g.setColor(Color.white);
    g.drawRect(x+1, y+1, width-2, height-2);
    g.drawRect(x+4, y+4, width-8, height-8);

    g.setColor(Color.lightGray);
    g.drawRect(x,   y,   width,   height);
    g.drawRect(x+2, y+2, width-4, height-4);
    g.drawRect(x+3, y+3, width-6, height-6);

    g.setColor(Color.gray);
    g.drawLine(x+1, y+height-1, x+width-1, y+height-1);
    g.drawLine(x+width-1, y+1,  x+width-1, y+height-1);

    g.drawLine(x+4, y+4, x+4,       y+height-5);
    g.drawLine(x+4, y+4, x+width-5, y+4);

    g.setColor(Color.black);
    g.drawLine(x, y+height, x+width, y+height);
    g.drawLine(x+width,  y, x+width, y+height);
  }


  //--------------------------------------------------------
  // What to do when button Option activated
  //--------------------------------------------------------
  public void clickedOption()
  {
      // to do: put event handler code here.
    m_option.disable();
    m_about.disable();
    stop();

    draw3DFrame(offScrGC, p3x-5, p3y-5, p3Width+9, p3Height+9);
    repaint();

    this.add("Center", p3);
    m_status.setText(" ");
  }


  //--------------------------------------------------------
  // What to do when button ">" (Next) i OptionMenu activated
  //--------------------------------------------------------
  public void clickedOptionNext()
  {
    removeCheckBoxes();
    checkListNumber++;
    addCheckBoxes();
  }


  //--------------------------------------------------------
  // What to do when button "<" (Prev) i OptionMenu activated
  //--------------------------------------------------------
  public void clickedOptionPrev()
  {
    removeCheckBoxes();
    checkListNumber--;
    addCheckBoxes();
  }


  //--------------------------------------------------------
  // What to do when button OK i Option menu activated
  //--------------------------------------------------------
  public void clickedOptionOk(Event ev)
  {
      // to do: put event handler code here.
    imgFitFrame    = m_imgFitFrame.getState();
    noFrame        = m_noFrame.getState();

    for (int i=0; i<amountOfImages; i++)
    {
      showStatus[i] = check[i].getState();
    }

      //read Interval
    ev.key = '\n';
    interval = keyReleaseInterval(ev) ?
               Integer.parseInt(m_interval.getText())*1000 : interval;
    doorOpenDelay = keyReleaseDoorDelay(ev) ?
                    Integer.parseInt(m_doorDelay.getText())*1000 :
                    doorOpenDelay;

    clickedOptionCancel();
  }


  //--------------------------------------------------------
  // What to do when button Cancel i Option menu activated
  //--------------------------------------------------------
  public void clickedOptionCancel()
  {
      // to do: put event handler code here.
    offScrGC.setColor(bgColor);
    offScrGC.fillRect(0, 0, frameWidth, frameHeight+30);
    repaint();

    removeCheckBoxes();
    this.remove(p3);

    m_imgFitFrame.setState(imgFitFrame);
    m_noFrame.setState(noFrame);
    m_interval.setText(Integer.toString(interval/1000));
    for (int i=0; i<amountOfImages; i++)
    {
      check[i].setState(showStatus[i]);
    }

    checkListNumber = 0;
    addCheckBoxes();
    m_option.enable();
  }


  //--------------------------------------------------------
  // What to do when button Go To i Option menu activated
  //--------------------------------------------------------
  public void clickedGoTo()
  {
    clickedOptionCancel();
    stop();
    try
    {
      URL url = new URL(codeBase, homeLink);
      getAppletContext().showDocument(url);
    } catch (MalformedURLException ex)
      {
        m_status.setText("...");
      }
  }


  //--------------------------------------------------------
  // What to do when mouse enter panel P3
  //--------------------------------------------------------
  public void mouseEnterP3(Event ev)
  {
    //System.out.println(ev);
    int target = 0;

    target = (ev.x>115 && ev.x<338 && ev.y>46  && ev.y<68 ) ? 1 : target; //Button 'Homepage'
    target = (ev.x>185 && ev.x<240 && ev.y>235 && ev.y<255) ? 2 : target; //Button '<'
    target = (ev.x>250 && ev.x<306 && ev.y>235 && ev.y<255) ? 3 : target; //Button '>'
    target = (ev.x>27  && ev.x<100 && ev.y>44  && ev.y<68 ) ? 4 : target; //Button 'OpenDoor'

    switch (target)
    {
      case 0:
        m_status.setText(" ");
        updateStatus(" ");
        break;
      case 1:
        updateStatus("Shortcut to " + homeLink);
        break;
      case 2:
        updateStatus("Previous group of image...");
        break;
      case 3:
        updateStatus("Next group of image...");
        break;
      case 4:
        if (openDoorNotValid)
        {
          updateStatus(" Action OpenDoor not valid!");
        }
        else
        {
          updateStatus(" ");
        }
        break;
    }
  }


  //--------------------------------------------------------
  // What to do when button Relay i Option menu activated
  //--------------------------------------------------------
  public void clickedOpenDoor(Event ev)
  {
    updateStatus(" Open the door (" + doorOpenDelay/1000 + "s) ...");
    m_optionOk.disable();
    m_optionCancel.disable();
    m_openDoor.disable();

    try
    {
      sendRequest("io/relayon.txt");
      if (!unauthorized)
      {
        engine.sleep(doorOpenDelay-300);
        sendRequest("io/relayoff.txt");
      }
    } catch(Exception e)
      {
        openDoorNotValid = true;
    	System.out.println("[clickedOpenDoor()] "+e);
      }
    m_openDoor.enable();
    m_optionOk.enable();
    m_optionCancel.enable();

    updateStatus(" ");
    if (unauthorized)
    {
      showPasswordPanel();
    }
    else if (openDoorNotValid)
    {
      m_status.setText(" Action OpenDoor not valid!");
    }
    else
    {
      clickedOptionOk(ev);
      start();
    }
  }


  //--------------------------------------------------------
  // Show password panel
  //--------------------------------------------------------
  public void showPasswordPanel()
  {
    m_status.setText(" Unauthorized!");
    this.remove(p3);
    this.add("Center", p2);
    m_userName.requestFocus();
    m_userName.selectAll();

  }


  //--------------------------------------------------------
  // what to do when key release from userName-editbox
  //--------------------------------------------------------
  public synchronized void keyReleaseUserName(Event ev)
  {
    if (ev.key == '\n')  //if input = 'Enter'
    {
      m_password.requestFocus();
      m_password.selectAll();
    }
  }


  //--------------------------------------------------------
  // what to do when key release from password-editbox
  //--------------------------------------------------------
  public synchronized void keyReleasePassword(Event ev)
  {
    if (ev.key == '\n')  //if input = 'Enter'
    {
      m_passOk.requestFocus();
    }
  }


  //--------------------------------------------------------
  // What to do when button Ok i password menu activated
  //--------------------------------------------------------
  public void clickedPassOk(Event ev)
  {
      // to do: put event handler code here.
    userName = m_userName.getText();
    password = m_password.getText();
    clickedPassCancel();
    clickedOpenDoor(ev);
  }


  //--------------------------------------------------------
  // What to do when button Cancel i password menu activated
  //--------------------------------------------------------
  public void clickedPassCancel()
  {
      // to do: put event handler code here.
    m_status.setText("");
    updateStatus("");
    this.remove(p2);
    this.add("Center", p3);
  }


  //--------------------------------------------------------
  // Sending a request
  //--------------------------------------------------------
  public void sendRequest(String textURL) throws IOException
  {
    Socket socket;
    String input, st;
    StringTokenizer token;

    socket = null;
    socket = connect(textURL);
    try
    {
      input = sendGET();
      token = new StringTokenizer(input, "\n");
      st = token.nextToken();
      System.out.println(st);

      token = new StringTokenizer(st, " ");
      st = token.nextToken();
      st = token.nextToken();
      unauthorized = Integer.parseInt(st)==401 ? true : false;
    } finally
      {
        socket.close();
      }
  }


  //--------------------------------------------------------
  //Make a socket connection
  //--------------------------------------------------------
  public Socket connect(String textURL) throws MalformedURLException, IOException
  {
    URL url;
    Socket socket;

    url    = new URL (codeBase, textURL);
    file   = url.getFile ();
    socket = new Socket(url.getHost(), url.getPort()==-1?80:url.getPort());

    out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
    in  = new DataInputStream(socket.getInputStream());

    return socket;
  }


  //--------------------------------------------------------
  // use metod GET
  //--------------------------------------------------------
  public String sendGET() throws IOException
  {
    String input, st;

    st = encodeBase64(userName + ":" + password);
    out.writeBytes ("GET " + file + " HTTP/1.0 Authorization: Basic " + st + "\r\n\r\n");
    out.flush ();

    st = "";
    while ((input = in.readLine ()) != null)
    {
      st += input + "\n";
    }
    return st;
  }


  //--------------------------------------------------------
  // Base64 encoder
  //--------------------------------------------------------
  public String encodeBase64(String inString)
  {
    String baseTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    String encodeString = "";

    int c, m = -1, n = 0, p, i, j;
    long val=0;
    int enc[] = new int[4];

    j = inString.length();
    while (++m<j)
    {
      c = inString.charAt(m);

      if (n++<=2)
      {
        val <<= 8;
        val += c;
        continue;
      }
      for (i=0; i<4; i++)
      {
        enc[i] = (int)val & 63;
        val >>= 6;
      }
      for (i=3; i>=0; i--)
      {
        encodeString += baseTable.charAt(enc[i]);
        n = 1;
        val = c;
      }
    }
	if (n==2)
	{
      val <<= 8;
      for (i=0; i<4; i++)
      {
        enc[i] = (int)val & 63;
        val >>= 6;
      }
      enc[0] = 64;
	}
	if (n == 3)
	{
      for (i=0; i<4; i++)
      {
        enc[i] = (int)val & 63;
        val >>= 6;
	  }
	}
	if (n!=0)
	{
      for (i=3; i>=0; i--)
      {
        encodeString += baseTable.charAt(enc[i]);
      }
	}
    return encodeString;
  }

}
