/*  Changes:
    0.1   : First try
    0.2   : added setSelected(String nameOfTab) and setSelectedNum(int numOfTabInArray)
            fix in the handlePenDown(int x, int y) by Bernd R. Fix
    0.3   : added second Constructor to easiness instanciation
            create just once a Graphics object
*/
package brf.pilot.dynapage.ui;

import brf.pilot.dynapage.UserWidget;
import brf.pilot.dynapage.ActionEvent;
import com.sun.kjava.Graphics;
import com.sun.kjava.Bitmap;

/**
 * Custom user control widget for Tabbed Buttons<p>
 * Implements a horizontally button pane at the top of a Page
 * including two arrows at the right side to scroll the tabs<br>
 * To use it you have two constructors:<p>
 * One where you need to create two arrays, one with the width of the tabs, the other including the names like: <br>
 * int [] tabLength;
 * String [] tabName;<br>
 * You have to have for each name a specific Size, like <br>
 * tabName[0] = "First"; tabLength[0] = 30;<br>
 * tabName[1] = "Second"; tabLenght[1] = 35;<p>
 * instanciate the UI calling its constructor like <br>
 * TabbedButtons tb = new TabbedButtons(tabLength, tabName, selectedTab);<br>
 * where selectedTab is the ArrayNumber of the selected Tab, so if selectedTab
 * is 0 the tab "First" will show up as selected.<p>
 * The other constructor is more simple:<br>
 * You do not need the tabLength array the widget is calculating the width of each tab on its best guess ;-)<p>
 * To use it in the handleEvent method call the methods getSelected() or getSelectedNum()
 * to get to know on which tab the clicked.<br>
 * No other methods are important to you!<p>
 * You have to care on your own to switch to other pages!!!<br>
 * This is just a widget like a button, nothing more<p>
 * Anyway,<br>
 * Have fun!
 *
 * <p>Copyright:    <a href="mailto:bernie@stimpfle.net">Bernhard Stimpfle</a><p>
 *
 * @author Bernhard Stimpfle
 * @version 0.2
 */

public class TabbedButtons implements UserWidget {
  // get a graphics object.
  Graphics g = Graphics.getGraphics ();

  /*
  maximum size for tabbs
  */
  private int xl = 2;   // most left point
  private int yt = 1;   // most upper top point
  private int th = 15;  // complete height of a tab
  private int tw = 135; // complete width of the tabs
  private int cor = 3;  // size of a corner
  private int sh = 3;   // horizontal space between tab and beginning of text
  private int ss = 3;   // vertical space between roof of tab and start of string
  private int mostRightXOfTabs = tw;  // most right really painted pixel of the tabs

  // position and size of scroll arrows
  private int sa_r = 160-3;   // most right position of the scrollarrow
  private int sa_sv = yt+3;   // space to the top
  private int sa_vsize = 9;    // vertical size of scroll arrow
  private int sa_vHsize = 5; // (int)(sa_vsize/2)+1;    // half size of scroll arrow
  private int sa_hsize = sa_vsize;

  private int spaceBetweenArrows = 3;
  private int sa_l = sa_r - sa_vsize - spaceBetweenArrows - sa_vsize;  // most left position of the scrollarrows

  private Bitmap leftArrow = new Bitmap((short)1, new byte[] {
          (byte)3, (byte)15, (byte)63, (byte)255, (byte)255, (byte)255, (byte)63, (byte)15, (byte)3
          });
  private Bitmap rightArrow = new Bitmap((short)1, new byte[] {
          (byte)192, (byte)240, (byte)252, (byte)255, (byte)255, (byte)255, (byte)252, (byte)240, (byte)192
          });

  private int numOfFirstTab;  // the Array-Index number of the first tab shown in the TabRow
  private int numOfSelectedTab;

  private int [] tabL;        // complete Length of each tab in an Array
  private String [] tabName;  // Name of each tab in an Array
  private int [] xName;       // x coordinate of drawString start of the tab name
  private String selectedName;  // name of the tab selected on startup
  // private static String newSelectedName;
  private static int newSelectedNum;


  private boolean moreOnRightSide = false;
  private boolean moreOnLeftSide = false;

  /**
   * constructor, where lengthOfTab the width of each single tab in the tabName array is<br>
   * @param lengthOfTab Array containing the width of each tab
   * @param tabName Array containing the name of each tab
   * @param numInArray sets, which tab is selected (it is obviously shown different than the others)<br>
   * at the first startup the selected tab will always be the first shown
   */
  public TabbedButtons(int[] lengthOfTab, String[] tabName, int numInArray) {
    this.tabL = lengthOfTab;
    this.tabName = tabName;
    this.numOfSelectedTab = numInArray;
    init();
  }

  /**
   * constructor, which needs an array of names for the tabs, and the number in the array of the selected tabName<br>
   * @param tabName Array containing the name of each tab
   * @param numInArray sets, which tab is selected (it is obviously shown different than the others)<br>
   * at the first startup the selected tab will always be the first shown
   */
  public TabbedButtons(String[] tabName, int numInArray) {
    this.tabName = tabName;
    this.numOfSelectedTab = numInArray;
    tabL = new int[tabName.length];
    for (int i=0; i<tabName.length; i++) {
      tabL[i] = g.getWidth(tabName[i]) + 5;
    }
    init();
  }

  /**
   * initializes some variables
   */
  private void init() {
    newSelectedNum = numOfSelectedTab;
    numOfFirstTab = numOfSelectedTab;      // the first tab shown is the selected one
    if (numOfFirstTab != 0) moreOnLeftSide = true;  // first shown != first
    selectedName = tabName[numOfFirstTab];
    setStartPositions(numOfFirstTab);
  }

  /**
   * returns the name of the tab the user clicked on
   */
  public String getSelected() {
    return tabName[newSelectedNum];
  }

  /**
   * returns the number of the tab the user clicked on<br>
   * this is the number the has in the array given to the constructor
   */
  public static int getSelectedNum() {
    return newSelectedNum;
  }

  /**
   * set a tab as the selectedTab
   * @param numInArray sets, which tab is selected
   * You just need this method, if you want to make it shown selected WITHOUT to tab on it
   */
  public void setSelectedNum(int numOfTabInArray) {
    numOfSelectedTab = numOfTabInArray;
  }

  /**
   * set a tab as the selectedTab
   * @param nameOfTab sets, which tab is selected
   * You just need this method, if you want to make it shown selected WITHOUT to tab on it
   */
  public void setSelected(String nameOfTab) {
    for (int i = 0; i < tabName.length; i++) {
      if (tabName[i] == nameOfTab) {
        numOfSelectedTab = i;
        break;
      }
    }
  }

  private void setStartPositions(int mostLeftTab) {
    // define drawString start of tabNames
    xName = new int[tabL.length];
    if (!moreOnLeftSide) {
      xName[0] = xl+sh;   // first is as always an exception
    } else {
      xName[mostLeftTab] = sh;
    }

    int h;
    for (int i=mostLeftTab+1; i< tabL.length; i++) {
      h = xName[i-1];
      xName[i] = h+tabL[i-1];   //start point of the previous + length of the previous
    }
  }

  // check if point is inside the widget
  public boolean contains (int x, int y) {
    // find out, if the point lays within our bounds...
    return ( (x >= xl) && (x <= 160) && (y >= yt) && (y <= yt+th) );
  }

  // check if point is inside the ArrowButtons
  private boolean containsInArrowButtonSpace (int x, int y) {
    if ( (x >=sa_l) && (x <= sa_r) && (y >= yt) && (y <= yt+th) ) {
      // check which button was pressed
      if ( (x <= sa_l+sa_hsize) && moreOnLeftSide) {   // left arrow
        // numOfFirstTab = 1;
        --numOfFirstTab;
        if (numOfFirstTab == 0) {
          moreOnLeftSide = false;
          xl = 2;
        }
        setStartPositions(numOfFirstTab);
        paint();
      } else if (x >= sa_r-sa_hsize && moreOnRightSide) {  // right arrow
        moreOnLeftSide = true;
        setStartPositions(++numOfFirstTab);
        paint();
      } else return false;
      return true;
    } else {
      return false;
    }
  }

  // Does the control have the focus? This should return true
  // only if the widget has a TextField child that has the
  // focus and false otherwise.
  public boolean hasFocus() {
    return false;
  }

  // control is losing the focus. Normally you do
  // nothing in this method except there is a TextField
  // child inside the control that has the focus.
  public void loseFocus() {
  }

  private boolean checkPosition(int x) {
    boolean set = false;
    for (int i=numOfFirstTab; i< tabL.length; i++) {
      // check if it is in the range of the shown tabs
      if ( (x < mostRightXOfTabs) && (x > xl) ){
        // check if it is the last tab of all OR which one it is
        if ( (i == tabL.length-1) || (x < xName[i+1]-sh) ) {
          newSelectedNum = i;
          set = true;
          break;
        }
      }
    }
    if (!set) {
      newSelectedNum = numOfSelectedTab;
      return false;
    } else return true;
  }

  // Handle a "pen down" event. Return an ActionEvent
  // (or a "null" reference in case there is no event)
  // The generated event is passed to the 'handleEvent()'
  // method of the page the widget is added to.
  public ActionEvent handlePenDown (int x, int y) {
    // is this point inside the bounds?
    if (!contains (x, y)) return null;

    if (containsInArrowButtonSpace (x, y)) return null;

    // set new position and trigger event.
    if (checkPosition (x)) {
      // make the label tabbed on the selected label; thanx to Bernd R. Fix
      numOfSelectedTab = newSelectedNum;
      paint();
      return new ActionEvent (this, ActionEvent.VALUE_CHANGED);
    }
    else return null;
  }

  // Handle a "pen move" event. Return an ActionEvent
  // (or a "null" reference in case there is no event)
  // The generated event is passed to the 'handleEvent()'
  // method of the page the widget is added to.
  public ActionEvent handlePenMove (int x, int y) {
    return handlePenDown (x, y);
  }

  // Handle a "pen up" event. Return an ActionEvent
  // (or a "null" reference in case there is no event)
  // The generated event is passed to the 'handleEvent()'
  // method of the page the widget is added to.
  public ActionEvent handlePenUp (int x, int y) {
    return null;
  }

  // Handle a "key" event. Return an ActionEvent
  // (or a "null" reference in case there is no event)
  // The generated event is passed to the 'handleEvent()'
  // method of the page the widget is added to.
  public ActionEvent handleKeyDown (int keyCode) {
    return null;
  }

  /*
  public void paintLeftArrow() {
    Graphics g = Graphics.getGraphics();
    g.setDrawRegion(sa_l+1, sa_sv, sa_vHsize, sa_hsize);
    g.drawBitmap(sa_l+1, sa_sv+1, leftArrow);
    g.resetDrawRegion ();
  }

  public void paintRightArrow() {
    Graphics g = Graphics.getGraphics();
    g.drawBitmap(sa_r-sa_hsize+1, sa_sv+1, rightArrow);
    g.resetDrawRegion();
  }
  */


  // Paint the user control.
  public void paint () {
    // use the canvas attribute to paint on screen.
    // get a graphics object.
    // Graphics g = Graphics.getGraphics ();

    // set the draw region.
    g.setDrawRegion (0, 0, 160, th+yt+2);

    // erase background.
    g.drawRectangle (0, 0, 160, th+yt+2, Graphics.ERASE, 0);

    // first tab is special, it is just painted the special way, if it's really the first
    if (!moreOnLeftSide) {
      // first corner of the first tab
      g.drawLine(0, yt+th, xl, yt+th, Graphics.PLAIN);
      g.drawLine(xl, yt+th, xl, yt+cor, Graphics.PLAIN);
    } else {
      xl = 0;
    }

    // for any first tab paint the upper corner
    g.drawLine(xl, yt+cor, xl+cor, yt, Graphics.PLAIN);

    int lup = xl+cor; // x coordinate of the last upper point painted
    int tablength = tabL[numOfFirstTab]; // length of the first tab

    // drawing the roof of the tab
    g.drawLine(lup, yt, lup+tablength-cor-cor, yt, Graphics.PLAIN);

    // drawing the name of the tab
    g.drawString(tabName[numOfFirstTab], xName[numOfFirstTab], yt+ss, Graphics.PLAIN);

    // drawing the bottom of the tab
    if (numOfFirstTab != numOfSelectedTab) {
      g.drawLine(lup-cor, yt+th, lup+tablength-cor, yt+th, Graphics.PLAIN);
    }

    lup = lup+tablength-cor-cor;
    // set the moreOnRightSide correctly
    moreOnRightSide = false;

    // paint now for every tab available
    // one is already painted, so start i with 1
    for (int i=numOfFirstTab+1; i<tabL.length; i++) {
      tablength = tabL[i];

      // check if there are more tabs which cannot be painted any more
      if (lup+cor+cor+tablength > tw+xl) {
        moreOnRightSide = true;
        break;
      }

      // drawing the angle between two tabs
      g.drawLine(lup, yt, lup+cor, yt+cor, Graphics.PLAIN);
      lup += cor;
      g.drawLine(lup, yt+cor, lup, yt+th, Graphics.RAISED);
      g.drawLine(lup, yt+cor, lup+cor, yt, Graphics.PLAIN);
      lup += cor;

      // drawing the roof of the tab
      g.drawLine(lup, yt, lup+tablength-cor-cor, yt, Graphics.PLAIN);

      // drawing the name of the tab
      g.drawString(tabName[i], xName[i], yt+ss, Graphics.PLAIN);

      // drawing the bottom of the tab
      if (i != numOfSelectedTab) {
        g.drawLine(lup-cor, yt+th, lup+tablength-cor, yt+th, Graphics.PLAIN);
      }

      lup = lup+tablength-cor-cor;
    }

    // close the last tab
    g.drawLine(lup, yt, lup+cor, yt+cor, Graphics.PLAIN);
    g.drawLine(lup+cor, yt+cor, lup+cor, yt+th, Graphics.PLAIN);
    g.drawLine(lup+cor, yt+th, 160, yt+th, Graphics.PLAIN);
    mostRightXOfTabs = lup+cor;

    // check if there are more Tabs on the right
    if (moreOnRightSide) g.drawBitmap(sa_r-sa_hsize+1, sa_sv+1, rightArrow);
    g.drawLine(sa_r-sa_hsize, sa_sv, sa_r, sa_sv+sa_vHsize, Graphics.PLAIN);
    g.drawLine(sa_r, sa_sv+sa_vHsize, sa_r-sa_hsize, sa_sv+sa_vsize, Graphics.PLAIN);
    g.drawLine(sa_r-sa_hsize, sa_sv+sa_vsize, sa_r-sa_hsize, sa_sv, Graphics.PLAIN);

    if (moreOnLeftSide) g.drawBitmap(sa_l+1, sa_sv+1, leftArrow);
    g.drawLine(sa_l+sa_hsize, sa_sv, sa_l, sa_sv+sa_vHsize, Graphics.PLAIN);
    g.drawLine(sa_l, sa_sv+sa_vHsize, sa_l+sa_hsize, sa_sv+sa_vsize, Graphics.PLAIN);
    g.drawLine(sa_l+sa_hsize, sa_sv+sa_vsize, sa_l+sa_hsize, sa_sv, Graphics.PLAIN);

    // unset draw region.
    g.resetDrawRegion ();
  }
}
