package oracle.forms.enhancedItems;

import java.awt.*;
import java.awt.event.*;
import java.net.URL;

import oracle.forms.ui.VButton;
import oracle.forms.handler.IHandler;
import oracle.forms.properties.ID;
import oracle.forms.properties.Property;


/**
 * <H3>UTL VRNTK enhanced button</H3>
 *
 * This is a PJC that implements a web style <B>Rollover image button</b>
 * where the image changes when the user moves the mouse over the component with ability to show label.
 * The standard Forms <code>oracle.forms.ui.VButton</code> is subclassed so only the additional
 * functionality is required to be added.
 *
 * We register two new forms properties, IMAGE_NAME_ON and IMAGE_NAME_OFF.  These properties form the
 * the URL of the image files that the button will display.  These properties will be set by PL/SQL
 * trigger code or can be set by defining the LABEL of the button with the prefix [ROLLOVER] followed by
 * the ON inage and OFF image names separated by a comma
 *
 * @version 1.0 09/13/1999 created<br>
 * @version 1.1 12/09/1999 modified to support 6i features to allow dynamic custom property manipulation
 * @version 1.2 02/20/2000 removed 6.0 stuff and renamed package to oracle.forms.demos for distribution
 * @version 2.0 09/24/2001 amemded for Forms 9i.  Moved to the oracle.forms.demos.enhancedItems package.  Made the setting of the on/off images declaritive, using the Label Property. Allowed loading from a JAR file as well as codebase.
 * @version 3.0 17/02/2006 renamed to LabledIconButton. Extend functionality to show both label and image by reload of paint methods
 * @author Steve Button, Duncan Mills, Oleh Tyshchenko
 */
public class LabledIconButton extends VButton
{
  /**
   * the property registered to specify the on image to be used
   */
  public final static ID IMAGE_NAME_ON      = ID.registerProperty("IMAGE_NAME_ON");
  /**
   * the property registered to specify the off image to be used
   */
  public final static ID IMAGE_NAME_OFF     = ID.registerProperty("IMAGE_NAME_OFF");
  /**
   * Forms property used to indicate that we want the utility to switch
   * Messaging on to the Java Console so we can see what is going on
   */
  private static final ID DEBUGMESSAGES     = ID.registerProperty("DEBUGMESSAGES");
  /**
   * Forms property used to indicate that we want the utility to switch
   * Messaging on to the Java Console for ALL INSTANCES of this PJC
   * so we can see what is going on
   * This will generate a <u>LOT</u> of messages
   */
  private static final ID DEBUGMESSAGES_ALL = ID.registerProperty("DEBUGMESSAGES_ALL");

  /**
   * define ON
   */
  private final int ON = 1;
  /**
   * define OFF
   */
  private final int OFF = 0;
  /**
   * the classname used for debugging purposes
   * use getClass().getName(); if you want the
   * full package name
   */
  private final String CLASSNAME = this.getDefaultName();

  /**
   * the hardcoded root directory for buttons in the JAR
   */
  private final String  JARBUTTONSDIR = "/";

  /**
   * the hardcoded button icon type in the JAR
   */
  private final String  JARBUTTONSEXT = ".gif";

  /**
   * the name of the on image
   */
  private String  m_imageNameOn;
  /**
   * the name of the off image
   */
  private String  m_imageNameOff;
  /**
   * storage for the handler for this class
   */
  private IHandler  m_handler;

  /**
   * The codebase from which the JAR was loaded - used to locate images
   */
  private URL m_codeBase;

  /**
   * the current state ON | OFF
   */
  private int m_state = OFF;

  /**
   * array to hold the images used to represent the state of the button
   */
  private Image[] m_images = { null, null };

  /**
   * Boolean value which describes if the button is currently a rollover or
   * if it is a general rounded button.
   */
   private boolean m_isRollover = false;

  /**
   * do we want to debug for this class?  Set this to true to see debug messages.
   */
  private boolean m_debug = false;

  /**
   * do we want to debug for all instances of this class?
   */
  private static boolean m_debugAll = false;


  public LabledIconButton()
  {
    super();
    log("Debugging on: Creating Button Instance");
    setLeftmost(false);
    setRightmost(false);
  }

  /**
   * Implementation of IView interface which provides an initialization opportunity for the component
   *
   * @param handler - message handler associated with this view.
   * @see oracle.forms.ui.IView
   */
  public void init(IHandler handler)
  {
    m_handler = handler;
    m_codeBase = handler.getCodeBase();
    super.init(handler);
  }

  /**
   * Implementation of IView interface which provides an custom paint ability
   *
   * @param handler - Graphics object to draw.
   * @see java.awt.Graphics
   */
  public void paint(Graphics p0)
  {
    int x = 5, y = 4;
    
    log( "Paint " + p0.toString());
    super.paint(p0);
    // Do some adjustion for left rounded button
    if (this.isLeftmost())
      x = x + 5;
    // Depend on current button state pick to draw one of the images
    if (m_images[m_state] != null)
      p0.drawImage(m_images[m_state],x,y,this);
  }

  /**
   * Implementation of IView interface which sets a requested property to a given value
   * If the property being set is LABEL then special processing is undertaken.
   * If the Label is Prefixed with the string <b>[ROLLOVER]</b> then the rest of the label is assumed
   * to be a comma separated list of the ON and OFF icon names trailed by button's label.
   * If the String after the rollover tag does not contain a pair e.g. no comma
   * then we assume that it's one of the Icons in the JAR which will be called
   * xxx_on and xxx_off where xxx is the supplied string.  We then set up the pair for
   * you e.g. a label of [ROLLOVER]firstrec will cause the icons
   * /oracle/forms/demos/images/firstrec_on.gif and
   * /oracle/forms/demos/images/firstrec_off.gif to be loaded
   * If the label does not begin with [ROLLOVER] then we treat it a a normal text label
   * except that leading or trailing round brackets can be used to indicate if
   * that edge of the button is rounded in Oracle look and feel
   * 
   * The programmer can control the Rounded button look in Oracle Look and Feel 
   * by placing a round bracket at the start or the end (or both) of the label
   * 
   * @param property    property to be set.
   * @param value value of the property id.
   * @return      true if the property could be set, false otherwise.
   * @see oracle.forms.ui.IView
   */
  public boolean setProperty(ID property, Object value)
  {
    if (property == ID.LABEL)
    {
      log("Setting Label to " + value.toString());
      String label = value.toString().trim();

      if (label.equals(""))
      {
        enableRollover();
        return true;
      }
      if (label.startsWith("[ROLLOVER]"))
      {
        enableRollover();
        label = label.substring(10);
        int i = label.indexOf(",");
        if (i > 0)
        {
          m_imageNameOn = JARBUTTONSDIR + label.substring(0,i) + JARBUTTONSEXT;
          // Check if label specified
          if (label.indexOf(",",i+2) > 0)
          {
            // Button's label specified
            m_imageNameOff = JARBUTTONSDIR + label.substring(i+1,label.indexOf(",", i+2)) + JARBUTTONSEXT;
            label = label.substring(label.indexOf(",", i+2)+1);
          }
          else
          {
            // Button's label doesn't specified
            m_imageNameOff = JARBUTTONSDIR + label.substring(i+1) + JARBUTTONSEXT;
            label = "";
          }
        }
        else
        {
          m_imageNameOn  = JARBUTTONSDIR + label + "_on" + JARBUTTONSEXT;
          m_imageNameOff = JARBUTTONSDIR + label + "_off" + JARBUTTONSEXT;
        }
        log("Detected Image Names + ON='" + m_imageNameOn + "', OFF= '" + m_imageNameOff + "' LABEL '" + label + "'");
        loadImage(ON,m_imageNameOn);
        loadImage(OFF,m_imageNameOff);
        setImage(OFF);
      }
      //If button's label beginig with ( round up the left edge
      if (label.startsWith("("))
      {
        log("Rounding left edge of " + label);
        setLeftmost(true);
        label = label.substring(1);
      }
      //If button's label beginig with ) round up the right edge
      if (label.endsWith(")"))
      {
        log("Rounding right edge of " + label);
        setRightmost(true);
        label = label.substring(0,label.length()-1);
      }
      return super.setProperty(property, label);
    }
    else if (property == IMAGE_NAME_ON)
    {
      // make sure we are in rollover mode
      enableRollover();
      log("setProperty - IMAGE_NAME_ON value=" + value.toString());
      // load the requested image
      loadImage(ON,m_imageNameOn);
      m_state = ON;
      // reset the currrently drawn image if needed
      this.repaint();
      return true;
    }
    else if (property == IMAGE_NAME_OFF)
    {
      // make sure we are in rollover mode
      enableRollover();
      log("setProperty - IMAGE_NAME_OFF value=" + value.toString());
      // load the requested image
      m_imageNameOff = (String) value;
      loadImage(OFF,m_imageNameOff);
      m_state = OFF;
      // reset the currrently drawn image if needed
      this.repaint();
      return true;
    }
    else if (property == DEBUGMESSAGES)
    {
      if (value.toString().equalsIgnoreCase("true"))
        m_debug = true;
      else
        m_debug = false;

      log("Debugging " + m_debug);
      return true;
    }
    else if (property == DEBUGMESSAGES_ALL)
    {
      if (value.toString().equalsIgnoreCase("true"))
        m_debugAll = true;
      else
        m_debugAll = false;
      log("Debugging " + m_debugAll);

      return true;
    }
    else
    {
     return super.setProperty(property, value);
    }
  }

  /**
   * Implementation of IView interface which returns the value of a requested property
   *
   * @param pid the property id that represents the property to be set
   * @return the value of the property id
   * @see oracle.forms.ui.IView
   */
  public Object getProperty(ID pid)
  {
    if ( pid == IMAGE_NAME_OFF )
    {
      return m_imageNameOff;
    }
    else if ( pid == IMAGE_NAME_ON )
    {
      return m_imageNameOn;
    }
    else
      return super.getProperty(pid);
  }


  /**
   * Loads the requested image from the Document base, loaded JAR files,
   * or from the Codebase
   * Here is a breakdown of the logic:
   * <ol>
   * <li>First we check the the loaded JAR files for the images</li>
   * <li>If it's not an inbuilt image, the code then checks the
   * image name supplied for http or https, if that is present
   * then it assumes the name is a full URL and loads the image from there.</li>
   * <li>If there is no Protocol in the image name then we assume it is a
   * relative URL to the docbase of the machine that Forms is running on.
   * We also pick up the protocol, and port and re-use those</li>
   * <li>If that fails to find the image, then we search relative to the
   * codebase e.g. forms90/java</li></ol>
   *
   * @param which the image state to set, value values ON | OFF
   * @param imageName the name of the image to load, including extension
   */
  private void loadImage(int which, String imageName)
  {
    URL imageURL = null;
    boolean loadSuccess = false;

    //Current JAR
    log("Searching JAR for " + imageName);
    imageURL = getClass().getResource(imageName);
    if (imageURL != null)
    {
      log("URL: " + imageURL.toString());
      try
      {
        m_images[which] = Toolkit.getDefaultToolkit().getImage(imageURL);
        loadSuccess = true;
        log("Image found: " + imageURL.toString());
      }
      catch (Exception ilex)
      {
        log("Error loading image from JAR: " + ilex.toString());
      }
    }
    else
    {
      log("Unable to find " + imageName + " in JAR");
    }

    //DOCBASE
    if (loadSuccess == false)
    {
      log("Searching docbase for " + imageName);
      try
      {
        if (imageName.toLowerCase().startsWith("http://")||imageName.toLowerCase().startsWith("https://"))
        {
          imageURL = new URL(imageName);
        }
        else
        {
          imageURL = new URL(m_codeBase.getProtocol() + "://" + m_codeBase.getHost() + ":" + m_codeBase.getPort() + imageName);
        }
        log("Constructed URL: " + imageURL.toString());
        try
        {
          m_images[which] = createImage((java.awt.image.ImageProducer) imageURL.getContent());
          loadSuccess = true;
          log("Image found: " + imageURL.toString());
        }
        catch (Exception ilex)
        {
          log("Error reading image - " + ilex.toString());
        }
      }
      catch (java.net.MalformedURLException urlex)
      {
        log("Error creating URL - " + urlex.toString());
      }
    }

    //CODEBASE
    if (loadSuccess == false)
    {
      log("Searching codebase for " + imageName);
      try
      {
        imageURL = new URL(m_codeBase, imageName);
        log("Constructed URL: " + imageURL.toString());
        try
        {
          m_images[which] = createImage((java.awt.image.ImageProducer) imageURL.getContent());
          loadSuccess = true;
          log("Image found: " + imageURL.toString());
        }
        catch (Exception ilex)
        {
                log("Error reading image - " + ilex.toString());
        }
      }
      catch (java.net.MalformedURLException urlex)
      {
        log("Error creating URL - " + urlex.toString());
      }
    }

    if (loadSuccess == false)
      log("Error image " + imageName + " could not be located");
  }

  /**
   * Set the image displayed to the requested image if the requested image is the current image.
   * Effectively does a redraw of the currently displayed image if the image is changed by the user.
   * 
   * @param which the image state to draw, value values ON | OFF
   * @param current the current state being represented by the image
   */
  private void setImage(int which, int current)
  {
    if(which==current)
      setImage(which);
  }

  /**
   * Set the image displayed to the appropriate image depending on the user action.
   *
   * @param which the image to display, valid values ON and OFF
   */
  private void setImage(int which)
  {
    m_state=which;
    if(which==ON)
      log("setImage ON");
    else
      log("setImage OFF");
  }

  /**
   * Creates the mouse listener for rollover mode
   * Also sets the button to fully rounded
   */
  private void enableRollover()
  {
    if (!m_isRollover)
    {
      log("Enabling Rollover");
      addMouseListener(new LabledIconButtonMouseAdapter());
      m_isRollover = true;
    }
  }

  /**
   * Utility function to print out a debug message to the Java Console
   * @param msg string to display, this will be prefixed with the classname of the PJC
   */
  public void log(String msg)
  {
    if(m_debug||m_debugAll)
        System.out.println(CLASSNAME + ": " + msg);
  }

  /**
   * Private class to handle user mouse actions and to switch images when the
   * user moves the mouse into and out of the button object.
   */
  class LabledIconButtonMouseAdapter extends MouseAdapter
  {
    /**
     * User moved the mouse over the button, swap to the on image.
     */
    public void mouseEntered(MouseEvent me)
    {
      setImage(ON);
      me.getComponent().repaint();
    }

    /**
     * User moved the mouse out of the button, swap to the off image.
     */
    public void mouseExited(MouseEvent me)
    {
      setImage(OFF);
      me.getComponent().repaint();
    }
  }
}