/*
   Tamot --- Task Modelling Editor
   Copyright (C) 2000 Commonwealth Scientific and Industrial Research Organisation
   (CSIRO), Australia. All rights reserved. 

   The Software, owned by CSIRO Australia, has been developed by the CSIRO 
   Intelligent Interactive Technology group. 
   For more information, please see http://www.cmis.csiro.au/iit/.

   The Software is a beta-test version for internal research and evaluation purposes 
   by the Licensee. The Software is still at a development stage. 
   It has not undergone complete testing and may have inherent errors,
   bugs or deficiencies that can affect the operation of the Software. 

   We encourage your feedback and suggestions. Any bugs / suggestions found please email to
   Shijian.Lu@cmis.csiro.au and Thomas.Lo@cmis.csiro.au. 

   After you have signed the Beta-Test Software License Agreement with the CSIRO,
   CSIRO grants you a non-exclusive, non-transferable license to use it only for your 
   personal, non-commercial and lawful end use. Implied licenses are negated. 

   Warranty Disclaimer :  

   CSIRO supplies the Software on an "as is" basis and makes no warranties that use of
   the Software by the Licensee will not infringe any third party’s 
   Intellectual Property Rights nor that the Software will be of merchantable quality,
   or suitable for a particular purpose. CSIRO excludes all terms, conditions and
   warranties implied by custom, the general law or statute in relation to the
   Software.
*/


/**
 * Title:        slider<p>
 * Description:  <p>
 * Copyright:    Copyright (c) <p>
 * Company:      <p>
 * @author
 * @version 1.0
 */
package parser.slider;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.*;


/**
 * A slider widget with multiple knobs. Code from Visad, modified to add more
 * sliders, and change the look. Perhaps more modifications could be made to
 * allow for an arbitrary number of sliders.
 */
public class RangeSlider extends JComponent implements MouseListener,
  MouseMotionListener {

  /** slider constants */
  public static final int SLIDER_PREF_HEIGHT = 13; //42;
  public static final int SLIDER_PREF_WIDTH = 300;

  /** grip constants */
  public static final int GRIP_WIDTH = 8;
  public static final int GRIP_HEIGHT = 26; //17;
  public static final int GRIP_TOP_Y = 0; //4;
  public static final int GRIP_BOTTOM_Y = GRIP_TOP_Y + GRIP_HEIGHT;
  public static final int GRIP_MIDDLE_Y = GRIP_TOP_Y + (GRIP_HEIGHT / 2);

  /*** track constants */
  public static final int SLIDER_LINE_HEIGHT = GRIP_HEIGHT + 2;
  public static final int SLIDER_LINE_WIDTH = 2;

  /** font constants */
  public static final int FONT_HEIGHT = 15;
  public static final int FONT_TOP_Y = 27;
  public static final int FONT_BOTTOM_Y = FONT_TOP_Y + FONT_HEIGHT - 2;

  /** percent through scale of grippers */
  float svKnob = 0;
  float vdoKnob = 100;
  float ppKnobOne = 100;
  float ppKnobTwo = 100;
  float ppKnobThree = 100;
  float ppKnobFour = 100;

  /** minimum slider value */
  float minLimit = 0.0f;

  /** maximum slider value */
  float maxLimit = 1.0f;

  /** location of grippers */
  private int svGrip = GRIP_WIDTH;
  private int vdoGrip = SLIDER_PREF_WIDTH/2;
  private int ppGripOne = SLIDER_PREF_WIDTH - GRIP_WIDTH;
  private int ppGripTwo = SLIDER_PREF_WIDTH - GRIP_WIDTH;
  private int ppGripThree = SLIDER_PREF_WIDTH - GRIP_WIDTH;
  private int ppGripFour = SLIDER_PREF_WIDTH - GRIP_WIDTH;

  /** flag whether mouse is currently affecting a particular gripper */
  private boolean svSlide = false;
  private boolean vdoSlide = false;
  private boolean ppSlideOne = false;
  private boolean ppSlideTwo = false;
  private boolean ppSlideThree = false;
  private boolean ppSlideFour = false;

  /** flag whether particular gripper has moved */
  protected boolean svSlideMoved = false;
  protected boolean vdoSlideMoved = false;
  protected boolean ppSlideOneMoved = false;
  protected boolean ppSlideTwoMoved = false;
  protected boolean ppSlideThreeMoved = false;
  protected boolean ppSlideFourMoved = false;

  /** label state variables */
  private float lastMinLimit = 0.0f;
  private float lastMaxLimit = 0.0f;
  private String lastCurStr = "";

  /** widget sizes */
  Dimension minSize = null;
  Dimension prefSize = null;
  Dimension maxSize = null;

  protected int[] headArray = new int[0];


  /** construct a RangeSlider with range of values (min, max) */
  public RangeSlider(String n, float min, float max) {
    resetValues(min, max);
    addMouseListener(this);
    addMouseMotionListener(this);
  }

  void resetValues(float min, float max)
  {
    minLimit = min;
    maxLimit = max;
    svGrip = GRIP_WIDTH;
    vdoGrip = getSize().width-GRIP_WIDTH;
    ppGripOne = getSize().width-GRIP_WIDTH;
    ppGripTwo = getSize().width-GRIP_WIDTH;
    ppGripThree = getSize().width-GRIP_WIDTH;
    ppGripFour = getSize().width-GRIP_WIDTH;
    svSlide = false;
    vdoSlide = false;
    ppSlideOne = false;
    ppSlideTwo = false;
    ppSlideThree = false;
    ppSlideFour = false;
    svSlideMoved = true;
    vdoSlideMoved = true;
    ppSlideOneMoved = true;
    ppSlideTwoMoved = true;
    ppSlideThreeMoved = true;
    ppSlideFourMoved = true;

    int w = getSize().width;
    svKnob = gripToValue(svGrip, w);
    vdoKnob = gripToValue(vdoGrip, w);
    ppKnobOne = gripToValue(ppGripOne, w);
    ppKnobTwo = gripToValue(ppGripTwo, w);
    ppKnobThree = gripToValue(ppGripThree, w);
    ppKnobFour = gripToValue(ppGripFour, w);
  }

  /** sets the slider's bounds to the specified values */
  public void setBounds(float min, float max) {
    resetValues(min, max);
    valuesUpdated();
    repaint();
  }

  public void setValues(float sv, float vdo, float ppone, float pptwo, float ppthree, float ppfour)
  {
    int w = getSize().width;
    int g;

    svKnob = sv;
    g = svGrip;
    svGrip = valueToGrip(svKnob, w);
    if (g != svGrip) {
      svSlideMoved = true;
    }

    vdoKnob = vdo;
    g = vdoGrip;
    vdoGrip = valueToGrip(vdoKnob, w);
    if (g != vdoGrip) {
      vdoSlideMoved = true;
    }

    ppKnobOne = ppone;
    g = ppGripOne;
    ppGripOne = valueToGrip(ppKnobOne, w);
    if (g != ppGripOne) {
      ppSlideOneMoved = true;
    }

    ppKnobTwo = pptwo;
    g = ppGripTwo;
    ppGripTwo = valueToGrip(ppKnobTwo, w);
    if (g != ppGripTwo) {
      ppSlideTwoMoved = true;
    }

    ppKnobThree = ppthree;
    g = ppGripThree;
    ppGripThree = valueToGrip(ppKnobThree, w);
    if (g != ppGripThree) {
      ppSlideThreeMoved = true;
    }

    ppKnobFour = ppfour;
    g = ppGripFour;
    ppGripFour = valueToGrip(ppKnobFour, w);
    if (g != ppGripFour) {
      ppSlideFourMoved = true;
    }

    repaint();
  }

  public void setHeads(int[] heads) {
     headArray = heads;
  }

  /** redraw slider if widget width changes */
  public void reshape(int x, int y, int w, int h) {
    int lastW = getSize().width;
    super.reshape(x, y, w, h);
    if (lastW != w) {
      svGrip = valueToGrip(svKnob, w);
      vdoGrip = valueToGrip(vdoKnob, w);
      ppGripOne = valueToGrip(ppKnobOne, w);
      ppGripTwo = valueToGrip(ppKnobTwo, w);
      ppGripThree = valueToGrip(ppKnobThree, w);
      ppGripFour = valueToGrip(ppKnobFour, w);
    }
  }

  /** return true if (px,py) is inside (x,y,w,h) */
  private boolean containedIn(int px, int py, int x, int y, int w, int h)
  {
    return new Rectangle(x, y, w, h).contains(px, py);
  }

  /** MouseListener method for moving slider */
  public void mousePressed(MouseEvent e) {
    int w = getSize().width;
    int x = e.getX();
    int y = e.getY();
    oldX = x;

    if (containedIn(x, y, svGrip-(GRIP_WIDTH/2), GRIP_TOP_Y,
                    GRIP_WIDTH, GRIP_HEIGHT)) {
      // mouse pressed in sv grip
      svSlide = true;
      //System.out.println("marker positon = " + svGrip);
    }

    else if (containedIn(x, y, vdoGrip-(GRIP_WIDTH/2), GRIP_TOP_Y,
                           GRIP_WIDTH, GRIP_HEIGHT)) {
      // mouse pressed in vdo grip
      vdoSlide = true;
      //System.out.println("marker positon = " + vdoGrip);
    }

    else if (containedIn(x, y, ppGripOne-(GRIP_WIDTH/2), GRIP_TOP_Y,
                           GRIP_WIDTH, GRIP_HEIGHT))
    {
      // mouse pressed in pp grip
      ppSlideOne = true;
      //System.out.println("marker positon = " + ppGripOne);
   }

    else if (containedIn(x, y, ppGripTwo-(GRIP_WIDTH/2), GRIP_TOP_Y,
                           GRIP_WIDTH, GRIP_HEIGHT))
    {
      // mouse pressed in pp grip
      ppSlideTwo = true;
      //System.out.println("marker positon = " + ppGripTwo);
   }

    else if (containedIn(x, y, ppGripThree-(GRIP_WIDTH/2), GRIP_TOP_Y,
                           GRIP_WIDTH, GRIP_HEIGHT))
    {
      // mouse pressed in pp grip
      ppSlideThree = true;
      //System.out.println("marker positon = " + ppGripThree);
 }

    else if (containedIn(x, y, ppGripFour-(GRIP_WIDTH/2), GRIP_TOP_Y,
                           GRIP_WIDTH, GRIP_HEIGHT))
    {
      // mouse pressed in pp grip
      //System.out.println("marker positon = " + ppGripFour);
     ppSlideFour = true;
    }
  }

  /** MouseListener method for moving slider */
  public void mouseReleased(MouseEvent e) {
    svSlide = false;
    vdoSlide = false;
    ppSlideOne = false;
    ppSlideTwo = false;
    ppSlideThree = false;
    ppSlideFour = false;
    repaint();
  }

  // unneeded MouseListener methods
  public void mouseClicked(MouseEvent e) { }
  public void mouseEntered(MouseEvent e) { }
  public void mouseExited(MouseEvent e) { }

  /** previous mouse X position */
  private int oldX;

  /** MouseMotionListener method for moving slider */
  public void mouseDragged(MouseEvent e) {
    int w = getSize().width;
    int x = e.getX();
    int y = e.getY();


    // move sv gripper if it is held
    if (svSlide) {
      if (x < GRIP_WIDTH) svGrip = GRIP_WIDTH;
      else if (x >= vdoGrip) svGrip = vdoGrip-1;
      else svGrip = x;
      svKnob = gripToValue(svGrip, w);
      svSlideMoved = true;
      valuesUpdated();
      repaint();
    }

    // move vdo gripper if it is held
    else if (vdoSlide) {
      if (x > w-GRIP_WIDTH) vdoGrip = w-GRIP_WIDTH;
      else if (x <= svGrip) vdoGrip = svGrip+1;
      else if (x >= ppGripOne) vdoGrip = ppGripOne-1;
      //else if (x >= ppGripTwo) vdoGrip = ppGripTwo-1;
      //else if (x >= ppGripThree) vdoGrip = ppGripThree-1;
      //else if (x >= ppGripFour) vdoGrip = ppGripFour-1;
      else vdoGrip = x;
      vdoKnob = gripToValue(vdoGrip, w);
      vdoSlideMoved = true;
      valuesUpdated();
      repaint();
    }

    //move pp gripper if it is held
    else if (ppSlideOne) {
      //if (x > w-GRIP_WIDTH) ppGripOne = w-GRIP_WIDTH;
      if (x <= vdoGrip) ppGripOne = vdoGrip+1;
      else if (x >= ppGripTwo) ppGripOne = ppGripTwo-1;
      else ppGripOne = x;
      ppKnobOne = gripToValue(ppGripOne, w);
      ppSlideOneMoved = true;
      valuesUpdated();
      repaint();
    }

    else if (ppSlideTwo) {
      //if (x > w-GRIP_WIDTH) ppGripTwo = w-GRIP_WIDTH;
      if (x <= ppGripOne) ppGripTwo = ppGripOne+1;
      else if (x >= ppGripThree) ppGripTwo = ppGripThree-1;
      else ppGripTwo = x;
      ppKnobTwo = gripToValue(ppGripTwo, w);
      ppSlideTwoMoved = true;
      valuesUpdated();
      repaint();
    }

    else if (ppSlideThree) {
      //if (x > w-GRIP_WIDTH) ppGripThree = w-GRIP_WIDTH;
      if (x <= ppGripTwo) ppGripThree = ppGripTwo+1;
      else if (x >= ppGripFour) ppGripThree = ppGripFour-1;
      else ppGripThree = x;
      ppKnobThree = gripToValue(ppGripThree, w);
      ppSlideThreeMoved = true;
      valuesUpdated();
      repaint();
    }

    else if (ppSlideFour) {
      if (x > w-GRIP_WIDTH) ppGripFour = w-GRIP_WIDTH;
      else if (x <= ppGripThree) ppGripFour = ppGripThree+1;
      else ppGripFour = x;
      ppKnobFour = gripToValue(ppGripFour, w);
      ppSlideFourMoved = true;
      valuesUpdated();
      repaint();
    }

    oldX = x;
  }

  /** not used */
  public void mouseMoved(MouseEvent e) { }

  /** return minimum size of range slider */
  public Dimension getMinimumSize() {
    if (minSize == null) {
      minSize = new Dimension(0, SLIDER_PREF_HEIGHT);
    }
    return minSize;
  }

  /** set minimum size of range slider */
  public void setMinimumSize(Dimension dim) { minSize = dim; }

  /** return preferred size of range slider */
  public Dimension getPreferredSize() {
    if (prefSize == null) {
      prefSize = new Dimension(SLIDER_PREF_WIDTH, SLIDER_PREF_HEIGHT);
    }
    return prefSize;
  }

  /** set preferred size of range slider */
  public void setPreferredSize(Dimension dim) { prefSize = dim; }

  /** return maximum size of range slider */
  public Dimension getMaximumSize() {
    if (maxSize == null) {
      maxSize = new Dimension(Integer.MAX_VALUE, SLIDER_PREF_HEIGHT);
    }
    return maxSize;
  }

  /** set preferred size of range slider */
  public void setMaximumSize(Dimension dim) { maxSize = dim; }

  private float gripToValue(int pos, int width)
  {
    return (((maxLimit - minLimit) * ((float )(pos - GRIP_WIDTH))) /
            (float )(width - (GRIP_WIDTH*2))) + minLimit;
  }

  private int valueToGrip(float value, int width)
  {
    float rfloat = (((value - (float )minLimit) *
                     (float )(width - (GRIP_WIDTH*2))) /
                    (maxLimit - minLimit));

    // round away from zero
    if (rfloat < 0.0f) {
      rfloat -= 0.5f;
    } else {
      rfloat += 0.5f;
    }

    return (int )rfloat + GRIP_WIDTH;
  }

  /** called whenever the min or max value is updated.
   *  This method does nothing and is meant to be overridden
   *  by classes which extend this class.
   */
  public void valuesUpdated()
  {
  }

  /** draws the slider from scratch */
  public void paint(Graphics g) {
    int w = getSize().width;

    // clear old graphics
    g.setColor(Color.white);
    g.fillRect(0, 0, w, SLIDER_PREF_HEIGHT);

    // draw slider lines
    int right = w - 1;
    g.setColor(Color.black);
    g.drawLine(0, GRIP_MIDDLE_Y, right, GRIP_MIDDLE_Y);
    g.drawLine(0, GRIP_TOP_Y-4, 0, GRIP_TOP_Y+SLIDER_LINE_HEIGHT);
    g.drawLine(0, GRIP_TOP_Y-4, SLIDER_LINE_WIDTH, GRIP_TOP_Y-4);
    g.drawLine(0, GRIP_TOP_Y+SLIDER_LINE_HEIGHT,
               SLIDER_LINE_WIDTH, GRIP_TOP_Y+SLIDER_LINE_HEIGHT);
    g.drawLine(right, GRIP_TOP_Y-4, right, GRIP_TOP_Y+SLIDER_LINE_HEIGHT);
    g.drawLine(right, GRIP_TOP_Y-4, right-SLIDER_LINE_WIDTH, GRIP_TOP_Y-4);
    g.drawLine(right, GRIP_TOP_Y+SLIDER_LINE_HEIGHT,
               right-SLIDER_LINE_WIDTH, GRIP_TOP_Y+SLIDER_LINE_HEIGHT);

    // refresh everything
    svSlideMoved = true;
    vdoSlideMoved = true;
    ppSlideOneMoved = true;
    ppSlideTwoMoved = true;
    ppSlideThreeMoved = true;
    ppSlideFourMoved = true;
    paintMinimum(g);
  }

  /** repaints anything that needs it */
  public void repaint() {
    Graphics g = getGraphics();
    if (g != null) {
      paintMinimum(g);
      g.dispose();
    }
  }

  public void paintHead(Graphics g, int start, int finish) {
    int w = getSize().width;

    start = valueToGrip(start-1, w);
    finish = valueToGrip(finish-1, w);

    if (g != null) {
      g.setColor(Color.gray);
      g.fillRect(start+2, 0, finish-start-5, 2);
    }
  }

  private void paintMinimum(Graphics g)
  {
    int w = getSize().width;
    g.setColor(Color.white);
    g.fillRect(SLIDER_LINE_WIDTH, GRIP_TOP_Y, w, GRIP_HEIGHT);
    g.setColor(Color.black);
    g.drawLine(SLIDER_LINE_WIDTH, GRIP_MIDDLE_Y, w, GRIP_MIDDLE_Y);

    for (int i = 0; i < headArray.length; i+=2) {
      paintHead(g, headArray[i], headArray[i+1]);
    }
    
	//shijian 2001-3-30
    if (headArray.length <4)
    	return;
    	
    g.setColor(Color.black);
    int pos = numOfSlidersAtPoint(vdoGrip);
    int location = vdoGrip;
    for (int i = 0; i < pos; i++) {
      int[] mpts = {location, location+(GRIP_WIDTH/2)+1, location+(GRIP_WIDTH/2)+1, location-(GRIP_WIDTH/2), location-(GRIP_WIDTH/2)};
      int[] npts = {GRIP_TOP_Y, GRIP_HEIGHT/4, GRIP_MIDDLE_Y, GRIP_MIDDLE_Y, GRIP_HEIGHT/4};
      g.drawPolygon(mpts, npts, 5);
      location += 2;
    }

    pos = numOfSlidersAtPoint(ppGripOne);
    location = ppGripOne;
    for (int i = 0; i < pos; i++) {
      int[] mpts = {location, location+(GRIP_WIDTH/2)+1, location+(GRIP_WIDTH/2)+1, location-(GRIP_WIDTH/2), location-(GRIP_WIDTH/2)};
      int[] npts = {GRIP_TOP_Y, GRIP_HEIGHT/4, GRIP_MIDDLE_Y, GRIP_MIDDLE_Y, GRIP_HEIGHT/4};
      g.drawPolygon(mpts, npts, 5);
      location += 2;
    }

    pos = numOfSlidersAtPoint(ppGripTwo);
    location = ppGripTwo;
    for (int i = 0; i < pos; i++) {
      int[] mpts = {location, location+(GRIP_WIDTH/2)+1, location+(GRIP_WIDTH/2)+1, location-(GRIP_WIDTH/2), location-(GRIP_WIDTH/2)};
      int[] npts = {GRIP_TOP_Y, GRIP_HEIGHT/4, GRIP_MIDDLE_Y, GRIP_MIDDLE_Y, GRIP_HEIGHT/4};
      g.drawPolygon(mpts, npts, 5);
      location += 2;
    }

    pos = numOfSlidersAtPoint(ppGripThree);
    location = ppGripThree;
    for (int i = 0; i < pos; i++) {
      int[] mpts = {location, location+(GRIP_WIDTH/2)+1, location+(GRIP_WIDTH/2)+1, location-(GRIP_WIDTH/2), location-(GRIP_WIDTH/2)};
      int[] npts = {GRIP_TOP_Y, GRIP_HEIGHT/4, GRIP_MIDDLE_Y, GRIP_MIDDLE_Y, GRIP_HEIGHT/4};
      g.drawPolygon(mpts, npts, 5);
      location += 2;
    }

    pos = numOfSlidersAtPoint(ppGripFour);
    location = ppGripFour;
    for (int i = 0; i < pos; i++) {
      int[] mpts = {location, location+(GRIP_WIDTH/2)+1, location+(GRIP_WIDTH/2)+1, location-(GRIP_WIDTH/2), location-(GRIP_WIDTH/2)};
      int[] npts = {GRIP_TOP_Y, GRIP_HEIGHT/4, GRIP_MIDDLE_Y, GRIP_MIDDLE_Y, GRIP_HEIGHT/4};
      g.drawPolygon(mpts, npts, 5);
      location += 2;
    }

    int[] xpts = {svGrip, svGrip+(GRIP_WIDTH/2)+1, svGrip+(GRIP_WIDTH/2)+1, svGrip-(GRIP_WIDTH/2), svGrip-(GRIP_WIDTH/2)};
    int[] ypts = {GRIP_TOP_Y, GRIP_HEIGHT/4, GRIP_MIDDLE_Y, GRIP_MIDDLE_Y, GRIP_HEIGHT/4};
    g.drawPolygon(xpts, ypts, 5);

    svSlideMoved = false;
    vdoSlideMoved = false;
    ppSlideOneMoved = false;
    ppSlideTwoMoved = false;
    ppSlideThreeMoved = false;
    ppSlideFourMoved = false;
  }

  public float[] getAllValues() {
    return new float[] {svKnob, vdoKnob, ppKnobOne, ppKnobTwo, ppKnobThree, ppKnobFour};
  }

  private int numOfSlidersAtPoint(float point) {
    int count = 0;
    if (svGrip == point)
      count += 1;
    if (vdoGrip == point)
      count += 1;
    if (ppGripOne == point)
      count += 1;
    if (ppGripTwo == point)
      count += 1;
    if (ppGripThree == point)
      count += 1;
    if (ppGripFour == point)
      count += 1;

    return count;
  }
  
  //shijian
  //16-11-2001
  public boolean pressedOnMarker() {
    return (svSlide |
    		vdoSlide |
		    ppSlideOne |
		    ppSlideTwo |
		    ppSlideThree |
		    ppSlideFour );
  }
}
