//
// Authors                Name                 email
// =======                ====                 ======
// 24-Jan-1996/SBH        Steve Hill           100144.3120@compuserve.com
// 25-Jan-1996/TG         Terence Goggin       75023.2022@compuserve.com
//
// Revision History
// ================
// 24-Jan-1996/SBH      Created.
// 25-Jan-1996/SBH      Removed paint() handler, added updateImage() to
//                      remove flicker on load.
//                      Added code to show URL on status line
//                      Added recHotArea (hot area now limited to main image)
// 25-Jan-1996/TG 	Changed hint.show method to allow new coordinates
//			to be given. In the pbimg applet, this is used to 
//			make the hint follow the mouse.
//
// 27-Jan-1996/SBH      Added code to make position of hint configurable.
// (v0.4)               options are:
//
//                      position="DYNAMIC"   {follows mouse}
//                      position="FIXED,x,y" {fixed at x,y}
//                      position="DEFAULT"   {position determined automatically}
//                      
//                      Added code to ensure all of hint always displayed,
//                      unless "FIXED" specified.
//
// 27-Jan-1996/SBH      Fixed crash in appletviewer, which doesn't like
//                      URL's which are mailto: Added check for null
//                      before displaying on status line.
//
// 27-Jan-1996/SBH      Fixed bug whereby if multiple instances on a single
// (v0.5)               page, then paint() get's called for one instance
//                      before run() has completed. This caused redraw() to
//                      be called re-entrantly, and image not to be displayed.
//
//-------------------------------------------------------------------------


import java.awt.*;
import java.util.StringTokenizer;
import java.net.URL;
import java.net.MalformedURLException;

//========================================================================
class hint
{
  private int     x,y,h,w;  //position,height,width
  private boolean bVisible = false;
  private boolean bFirstDraw = true; 
  private String szMyText;  
  private String szDisplayMode; 

  public hint (int _x,int _y, String _szMyText, String _szDisplayMode)
  {
  int iComma;
  String sParams = "";

  x = _x;
  y = _y;
  szMyText = _szMyText;

  if (_szDisplayMode == null)
        _szDisplayMode = "DEFAULT";

//  System.out.println("_szDisplayMode = " + _szDisplayMode );

  //if this mode has parameters, save them, and remove from main param
  if ((iComma = _szDisplayMode.indexOf(",")) > 0)
  {
    sParams = _szDisplayMode.substring(iComma);
    _szDisplayMode = _szDisplayMode.substring(0,iComma);
  }

//  System.out.println("Mode,Params = " + _szDisplayMode + sParams );


  _szDisplayMode = _szDisplayMode.toUpperCase();

  if ((_szDisplayMode.equals("DYNAMIC")) || (_szDisplayMode.equals("FIXED")))
    szDisplayMode = _szDisplayMode;
  else
    szDisplayMode = "DEFAULT";

  if (szDisplayMode.equals("FIXED"))
  {
    try
    {
      StringTokenizer st = new StringTokenizer(sParams, ", ");
      x = Integer.parseInt(st.nextToken());
      y = Integer.parseInt(st.nextToken());
    }
    catch (Exception e)
    {
    x=5;
    y=5;
    }
  }

        
  }

  public boolean isDynamic()
  {// This is provided becuase the parent needs to know if we are dynamic,
   // so they can redraw on every mouse move.

    return szDisplayMode.equals("DYNAMIC");
  }

  public void hide()
  {
    bVisible = false;
  }

  public void show(int _x, int _y, Dimension _dArea,Graphics g )
  //
  // Will turn the hint 'on' - at the location specified by (x,y),
  // but limited by d_Area (ie, attempt to fit in this area, and
  // override x,y if necessary.
  {
        if (bFirstDraw)
          {
           w = g.getFontMetrics().stringWidth(szMyText+"  ");
           h = g.getFontMetrics().getHeight();
           bFirstDraw = false;
          }
        

        if ((szDisplayMode.equals("DYNAMIC"))
                       ||
        ((szDisplayMode.equals("DEFAULT")) && (bVisible==false)))
        {
          x = _x;
          y = _y;
          limit_position(_dArea);
        }

        bVisible = true;
  }

  private void limit_position(Dimension _dArea)
  {
  // checks we are currently within the applet area, and if not
  // performs the minimum adjustment necessary to make us fit.

    //ensure that we fit horizontally

    if ((x+w) >= _dArea.width-1)
      {
        x -= 1+ ((x+w)-_dArea.width);
      }
    //ensure that we fit vertically

    if ((y+h) >= _dArea.height-1)
      {
        y -= 1+ ((y+h)-_dArea.height);
      }

    //make sure we're not now off the lh edge...

    if (x<0) x=0;
    if (y<0) y=0;

  }

  public void draw(Graphics g)
  {
  if (bVisible)
    {
      g.setColor(Color.yellow);
      g.fillRect(x,y,w,h);
      g.setColor(Color.black);
      g.drawRect(x,y,w,h);
      g.drawString(szMyText,x+2,y+h-2);
    }
  }
}
//========================================================================
public class pbimg extends java.applet.Applet implements Runnable {

    public String szVersion = "pbimg.class v0.5 (c) Steve Hill 100144.3120@compuserve.com";


    Thread timer;

    Image offscreen = null;
    Image Imgbackground = null;
    Image ImgMain = null;

    int iBackWidth;
    Dimension offscreensize;
    Graphics offG = null;
    Graphics onGC = null;

    String szBackGroundFile;
    String szImageFile;

    boolean bBackgroundOK = false;
    boolean bImageOK      = false;

    URL uURL = null;
    hint myHint;

    Color colMyBackground;
    Rectangle recHotArea = new Rectangle(0,0,0,0);       //This is where where the user can click

    boolean bFirst=true;    //used to make sure redraw() not called rentrantly
    boolean bImageUp=false; //don't let hints appear until 1st image up

    public void init()
    {

        colMyBackground = getColorFromParam("bgcolor",Color.gray);

        myHint = new hint(15,15,getParameter("hint_text"),
                        getParameter("position"));
        myHint.hide();



        try
        {
            uURL = new URL(getDocumentBase(), getParameter("url"));
	}
          catch (MalformedURLException e)
          {
	    uURL = null;
	  }

        boolean bDone = false;

        // we always use the same graphics context, get it now..

        onGC = getGraphics();
        szBackGroundFile = getParameter("background");

        if (null != (Imgbackground = getImage(getDocumentBase(),szBackGroundFile)))
        {
           bBackgroundOK = true;
        }

        szImageFile = getParameter("image");

        if (null != (ImgMain = getImage(getDocumentBase(),szImageFile)))
        {
           bImageOK = true;
        }

    } //init()

    public boolean mouseUp(Event evt,int x, int y)
    {
    if ((uURL != null) && (recHotArea.inside(x,y)))
    {
        // let's jump..
        getAppletContext().showDocument(uURL);
    }
    return(true);
    }


    public boolean mouseMove(Event evt,int x, int y)
    {

      // Image up check disabled below, because if netscape 2b6 reloads
      // the applet, it resets bImageUp to false, but never sends us an
      // ALLBITS message when it displays - so no hints following a reload.

  
      if (recHotArea.inside(x,y)) // &&  bImageUp) 
      {
        myHint.show(x,y,size(),onGC);
         
        if (uURL != null) 
          showStatus(uURL.toString());

        redraw(onGC);
      }
      else
      {
        myHint.hide();
        redraw(onGC);
        showStatus("");
      }
      return(true);
    }


    public boolean mouseExit(Event evt,int x, int y)
    //
    // If the mouse leaves the applet, must hide the hint. This catches the
    // case when the mouse goes from the hotarea to the outside world without
    // touching the background.
    {
      myHint.hide();

      redraw(onGC);
      showStatus("");
      return(true);
    }


public boolean imageUpdate(Image img, int flags,
			       int x, int y, int w, int h)

// This function is called whenever more data is recieved for the
// background image being drawn by g.drawImage(Imgbackground,bx,by,this);
//
// All this function does is prevent an auto repaint occuring when
// parts of the image are recieved, thus preventing flicker

{
   if ((flags & (ALLBITS)) != 0)
   {

        // Returning false here means that the browser doesn't perform
        // a full repaint every time we recieve a little more of the image.
        // Without this function, the display flickers as we load the image.

        return (false);
   }
   else
   {
        // we now have all the information - so we don't need informing
        // of further events

        bImageUp = true; 
        redraw(onGC); 
        return(true);

   }
}

   
    public void redraw(Graphics g)
    {
        Dimension d = size();
	if ((offscreen == null)
                ||
            (d.width != offscreensize.width)
                ||
           (d.height != offscreensize.height)
                ||
            (offG==null))
        {
            // create an offscreen graphics context identical in size
            // to the one we've been given.
	    offscreen = createImage(d.width, d.height);
	    offscreensize = d;
            offG = offscreen.getGraphics();

            // fill background
         offG.setColor(colMyBackground);
         offG.fillRect(0, 0, d.width, d.height);

       }

        // tile image onto background, if one was specified.
        if (bBackgroundOK)
        {
          Dimension mysize = size();
          if ((iBackWidth = Imgbackground.getWidth(this))>0)
          {
            int bx,by,iBackHeight;
            // we tile the background with repeated draws, starting top left

            if((iBackHeight = Imgbackground.getHeight(this))>0)
              for (bx=0;bx < mysize.width;bx += iBackWidth)
                for (by=0;by < mysize.height;by += iBackHeight)
                 offG.drawImage(Imgbackground,bx,by,this);
          }

        }

        if (bImageOK)
        {
           offG.drawImage(ImgMain,0,0,this);
           recHotArea.width  = ImgMain.getWidth(this);
           recHotArea.height = ImgMain.getHeight(this);

        }

        // All application graphics code must go here, drawing on offG
        // which is already setup with background & image.
        myHint.draw(offG);

        // and end here...

        // copy offscreen to onscreen
        g.drawImage(offscreen, 0, 0, null);

        // This is put in so that this function is not called from paint()
        // before it returns from it's inial invokation from run().

        bFirst = false;
    }

    Color getColorFromParam(String szParam,Color col_default)
//
// Returns a Color based on a Hex triplet specified as a parameter to
// the applet. If the parameter doesn't exist, or doesn't parse
// correctly, we return col_default.
//
// 01-Jan-1996/SBH      Created

  {
    String szColor;
    String szTemp;

    int Red,Blue,Green;
    int offset = 0;


    if((szColor = getParameter(szParam)) == null)
    {
      return (col_default);
    }
    else
    {
      //Hex triplets optionally start with '#'

      if (szColor.startsWith("#"))
      {
        offset = 1;
      }

      try
      {
        Red   = Integer.parseInt(szColor.substring(0+offset,2+offset),16);
        Green = Integer.parseInt(szColor.substring(2+offset,4+offset),16);
        Blue  = Integer.parseInt(szColor.substring(4+offset,6+offset),16);
      }
      catch (Exception e)
      {
        return (col_default);
      }

      return new Color ( Red,Green,Blue);
    }
  }
    public void paint(Graphics g)
    {
    // Found a really strange problem, that in Netscape 2.0b6, paint() is
    // called before run() completes, thus trashing redraw(). Symptom was
    // no image appeared at all, though everything else worked. Answer was
    // interlock with bFirst to ensure not called re-entrantly.

    if (!bFirst)        
      redraw(onGC);
    }

    public void start() {
        timer = new Thread(this);
        timer.start();
    }
    public void stop() {
        timer.stop();
    }

    public String getAppletInfo()
    {
      return(szVersion);
    }

    public String[][] getParameterInfo()
    {
     String pinfo[][] =
     {
        {"hint_text",      "string",    "Hint text to display"},
        {"bgcolor", "RGB hex triplet", "Color of background (optional)"},
        {"background","URL","Image to tile on background."},
        {"image","URL","Image which forms button(optional - hidden link!)"},
        {"url","URL","Where to go when clicked.."},
        {"position","[FIXED<x>,<y>|DYNAMIC|DEFAULT]","Where to display the hint.(Optional)"}
     };

      return (pinfo);
  }

    public void run()
    
    {
        System.out.println(szVersion);
        redraw(onGC);
    }
}

