/*
 * Created on Jan 23, 2004
 *
 */
package LumiSoft.UI.Controls;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;

/**
 * Scrollbar control.
 */
public class WScrollBar extends JPanel
{
	private EventListenerList m_pListners       = null;
	private ViewStyle         m_ViewStyle       = null;
	private int               m_Minimum         = 0;
	private int               m_Maximum         = 0;
	private int               m_Position        = 0;
	private Point             m_MousePos        = new Point(-1,-1);
	private int               m_MouseModifiers  = 0;
	private String            m_HittedObject    = "";
	private boolean           m_Vertical        = true;
	private boolean           m_Dragging        = false;
	private Rectangle         m_pScrollPaneRect = new Rectangle(0,0,17,17);
	
	/**
	 * Default constructor.
	 *
	 */
	public WScrollBar()
	{
		m_pListners = new EventListenerList();
		m_ViewStyle = new ViewStyle();
		
		this.addMouseListener(new MouseAdapter(){
			public void mousePressed(MouseEvent e){
				OnMousePressed(e);
			}
			
			public void mouseReleased(MouseEvent e){
				OnMouseReleased(e);
			}
			
			public void mouseExited(MouseEvent e){
				OnMouseExited(e);
			}
			
			public void mouseClicked(MouseEvent e){
				OnMouseClicked(e);
			}
		});

		this.addMouseMotionListener(new MouseMotionAdapter(){
			public void mouseMoved(MouseEvent e){
				OnMouseMoved(e);
			}
			
			public void mouseDragged(MouseEvent e){
				OnMouseDragged(e);
			}
		});
	}
	
	
	/**
	 * Is called by system when control need to repaint.
	 */
	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		
		// Create painting buffer
		Image graphicsBuffer = this.createImage(this.getBounds().width,this.getBounds().height);
		if(graphicsBuffer != null){
			Graphics2D gBuff = (Graphics2D)graphicsBuffer.getGraphics();
					
			// Paint to buffer
			Paint(gBuff);			
			gBuff.dispose();
			
			// Paint buffer to screen
			g.drawImage(graphicsBuffer,0,0,this);
		}
	}
	
	private void Paint(Graphics2D g)
	{				
		// Fill backround
		g.setColor(ViewStyle.GetColorFromRgb(242,242,242));
		g.fillRect(0,0,this.getWidth(),this.getHeight());
		g.setColor(ViewStyle.GetColorFromRgb(212,208,200));
		g.drawRect(0,0,this.getWidth() - 1,this.getHeight() - 1);
		
		// Draw scroll pane
		g.setColor(m_ViewStyle.GetButtonColor(getScrollPaneRect().contains(m_MousePos) || m_Dragging,m_MouseModifiers == MouseEvent.BUTTON1_DOWN_MASK));
		g.fill(new Rectangle(m_pScrollPaneRect.x,m_pScrollPaneRect.y,m_pScrollPaneRect.width,m_pScrollPaneRect.height));
		g.setColor(m_ViewStyle.GetBorderColor(getScrollPaneRect().contains(m_MousePos) || m_Dragging));
		g.draw(new Rectangle(m_pScrollPaneRect.x,m_pScrollPaneRect.y,m_pScrollPaneRect.width,m_pScrollPaneRect.height));
				
		// Draw decrease button
		g.setColor(m_ViewStyle.GetButtonColor(getDecreaseButtonRect().contains(m_MousePos) && !m_Dragging,m_MouseModifiers == MouseEvent.BUTTON1_DOWN_MASK));
		g.fill(getDecreaseButtonRect());
		g.setColor(Color.BLACK);
		if(m_Vertical){
			Paint.DrawTriangle(g,getTriangleRect(getDecreaseButtonRect()),Direction.Up);
		}
		else{
			Paint.DrawTriangle(g,getTriangleRect(getDecreaseButtonRect()),Direction.Left);
		}
		g.setColor(m_ViewStyle.GetBorderColor(getDecreaseButtonRect().contains(m_MousePos) && !m_Dragging));		
		g.draw(getDecreaseButtonRect());
		
		// Draw increase button
		g.setColor(m_ViewStyle.GetButtonColor(getIncreaseButtonRect().contains(m_MousePos) && !m_Dragging,m_MouseModifiers == MouseEvent.BUTTON1_DOWN_MASK));
		g.fill(getIncreaseButtonRect());
		g.setColor(Color.BLACK);
		if(m_Vertical){
			Paint.DrawTriangle(g,getTriangleRect(getIncreaseButtonRect()),Direction.Down);
		}
		else{
			Paint.DrawTriangle(g,getTriangleRect(getIncreaseButtonRect()),Direction.Right);
		}
		g.setColor(m_ViewStyle.GetBorderColor(getIncreaseButtonRect().contains(m_MousePos) && !m_Dragging));		
		g.draw(getIncreaseButtonRect());
	}
	
	
	/**
	 * Is called when mouse cursor has exited this control.
	 * @param e
	 */
	private void OnMouseExited(MouseEvent e)
	{
		if(m_Dragging){
			return;
		}
		
		m_MouseModifiers = 0;
		m_MousePos       = new Point(-1,-1);
		m_HittedObject   = "";
		m_Dragging       = false;
		
		repaint();
	}
	
	/**
	 * Is called when mouse has pressed down.
	 * @param e
	 */
	private void OnMousePressed(MouseEvent e)
	{
		if(getScrollPaneRect().contains(e.getPoint())){
			// See if we may begin dragging
			if(RaiseMayChangePosition()){
				m_Dragging = true;
			}
		}
		
		m_MouseModifiers = e.getModifiersEx();
	
		repaint();
	}
	
	/**
	 * Is called when mouse has released.
	 * @param e
	 */
	private void OnMouseReleased(MouseEvent e)
	{
		if(m_Dragging){
			this.setPosition(m_Position);
		}
		
		m_Dragging = false;
		m_MouseModifiers = 0;
								
		repaint();
	}
	
	/**
	 * Is called when mouse has clicked.
	 * @param e
	 */
	private void OnMouseClicked(MouseEvent e)
	{
		if(!RaiseMayChangePosition()){
			return;
		}
		
		if(this.getDecreaseButtonRect().contains(e.getPoint()) && m_Position > m_Minimum){
			setPosition(m_Position - 1);
			RaisePositionChanged();
		}
		else if(this.getIncreaseButtonRect().contains(e.getPoint()) && m_Position < m_Maximum){
			setPosition(m_Position + 1);
			RaisePositionChanged();
		}
	}
			
	/**
	 * Is called when mouse has moved.
	 * @param e
	 */
	public void OnMouseMoved(MouseEvent e)
	{
		m_MousePos = e.getPoint();
		
		String hittedObject = "";
		if(this.getDecreaseButtonRect().contains(e.getPoint())){
			hittedObject = "DecreaseButton";
		}
		else if(this.getIncreaseButtonRect().contains(e.getPoint())){
			hittedObject = "IncreaseButton";
		}
		else if(getScrollPaneRect().contains(e.getPoint())){
			hittedObject = "ScrollPane";
		}
		
		if(!m_HittedObject.equals(hittedObject)){
			m_HittedObject = hittedObject;
			
			this.repaint();
		}
	}
	
	/**
	 * Is called when mouse has dragged.
	 * @param e
	 */
	private void OnMouseDragged(MouseEvent e)
	{
		if(!m_Dragging){
			return;
		}
	//	if(!m_pScrollPaneRect.contains(e.getPoint())){
	//		return;
	//	}
		
		int position = m_Position;		
		if(m_Vertical){			
			int y = (e.getPoint().y - m_MousePos.y);
			if(m_pScrollPaneRect.y + y >= getDecreaseButtonRect().getMaxY() + 1 && m_pScrollPaneRect.getMaxY() + y + 1 <= getIncreaseButtonRect().y){
				m_pScrollPaneRect = new Rectangle(m_pScrollPaneRect.x,m_pScrollPaneRect.y + y,m_pScrollPaneRect.width,m_pScrollPaneRect.height);
				
				if(m_pScrollPaneRect.getMaxY() >= this.getHeight() - 19){
					m_Position = this.getMaximum();
				}
				else{
					int scrollArea = getBounds().height - getScrollPaneRect().height - 34;
					float pos = (float)(m_pScrollPaneRect.y - 17) / scrollArea;
					m_Position = (int)(this.getMaximum() * pos);
				}
				
				if(position != m_Position){
					RaisePositionChanged();
				}
				
				this.repaint();
			}
		}
		else{
			int x = (e.getPoint().x - m_MousePos.x);			
			if(m_pScrollPaneRect.x + x >= getDecreaseButtonRect().getMaxX() + 1 && m_pScrollPaneRect.getMaxX() + x + 1 <= getIncreaseButtonRect().x){
				m_pScrollPaneRect = new Rectangle(m_pScrollPaneRect.x + x,m_pScrollPaneRect.y,m_pScrollPaneRect.width,m_pScrollPaneRect.height);
				
				if(m_pScrollPaneRect.getMaxX() >= this.getWidth() - 19){
					m_Position = this.getMaximum();
				}
				else{
					int scrollArea = getBounds().width - getScrollPaneRect().width - 34;
					float pos = (float)(m_pScrollPaneRect.x - 17) / (float)scrollArea;
					m_Position = (int)(this.getMaximum() * pos);
				}
				
				if(position != m_Position){
					RaisePositionChanged();
				}
			
				this.repaint();
			}
		}
		
		m_MousePos = e.getPoint();
	}
	
	
	private Rectangle getTriangleRect(Rectangle buttonRect)
	{
		if(m_Vertical){
			return new Rectangle(buttonRect.x + 4,buttonRect.y + 7,buttonRect.width - 7,buttonRect.height - 12);
		}
		else{
			return new Rectangle(buttonRect.x + 7,buttonRect.y + 4,buttonRect.width - 12,buttonRect.height - 7);
		}
	}
	
	/**
	 * Gets scroll pane start X position.
	 * @return
	 */
	private int GetScrollPaneX()
	{
		if(m_Vertical){
			return 0;	
		}
		
		int x = 18;
		if(m_Position != 0){
			if(m_Position < this.getMaximum()){
				x += ((this.getWidth() - 36 - GetScrollPaneWidth()) / (float)this.getMaximum()) * (float)m_Position;
			}
			else{
				x = this.getWidth() - 18 - GetScrollPaneWidth() - 1;
			}
		}
		
		return x;
	}
	
	/**
	 * Gets scroll pane start Y position.
	 * @return
	 */
	private int GetScrollPaneY()
	{
		if(!m_Vertical){
			return 0;			
		}
		
		int y = 18;
		if(this.getMaximum() != 0){
			if(m_Position < this.getMaximum()){
				y += ((this.getHeight() - 36 - GetScrollPaneHeight()) / (float)this.getMaximum()) * (float)m_Position;
			}
			else{
				y = this.getHeight() - 18 - GetScrollPaneHeight() - 1;
			}
		}
		
		return y;
	}
	
	/**
	 * Gets scroll pane width.
	 * @return
	 */
	private int GetScrollPaneWidth()
	{
		if(m_Vertical){
			return getBounds().width - 1;			
		}
		if(this.getMaximum() == 0){
			return this.getWidth() - 34;
		}
		
		int width = (this.getWidth() - 34) / this.getMaximum();
		if(width < 12){
			width = 12;
		}
		
		return width;
	}
	
	/**
	 * Gets scroll pane height.
	 * @return
	 */
	private int GetScrollPaneHeight()
	{
		if(!m_Vertical){
			return getBounds().height - 1;			
		}
		if(this.getMaximum() == 0){
			return this.getHeight() - 34;
		}
		
		int height = (this.getHeight() - 34) / this.getMaximum();
		if(height < 12){
			height = 12;
		}
		
		return height;
	}
	
		
	private Rectangle getScrollPaneRect()
	{
		return m_pScrollPaneRect;
	}
	
	private Rectangle getDecreaseButtonRect()
	{
		if(m_Vertical){
			return new Rectangle(0,0,this.getWidth() - 1,17);
		}
		else{
			return new Rectangle(0,0,this.getHeight(),this.getHeight() - 1);			
		}
	}
	
	private Rectangle getIncreaseButtonRect()
	{
		if(m_Vertical){
			return new Rectangle(0,this.getHeight() - 18,this.getWidth() - 1,17);
		}
		else{
			return new Rectangle(this.getWidth() - 18,0,this.getHeight(),this.getHeight() - 1);
		}
	}
	
		
	
	/**
	 * Gets minimum value.
	 * @return
	 */
	public int getMinimum()
	{
		return m_Minimum;
	}
	/**
	 * Sets minimum value.
	 * @param value
	 */
	public void setMinimum(int value)
	{
		if(m_Minimum != value){
			m_Minimum = value;
			
			setPosition(0);
		}
	}
	
	/**
	 * Gets maximum value.
	 * @return
	 */
	public int getMaximum()
	{
		return m_Maximum;
	}
	/**
	 * Sets maximum value.
	 * @param value
	 */
	public void setMaximum(int value)
	{
		if(m_Maximum != value){
			m_Maximum = value;
			
			if(getPosition() > m_Maximum){
				setPosition(m_Maximum);
			}
			else{
				setPosition(getPosition());
			}
		}
	}
	
	/**
	 * Gets scrollbar position.
	 * @return
	 */
	public int getPosition()
	{
		return m_Position;
	}
	/**
	 * Sets scrollbar position.
	 * @param value
	 */
	public void setPosition(int value)
	{
	//	if(m_Position != value){
			if(value <= m_Minimum){
				value = m_Minimum;
			}
			else if(value >= m_Maximum){
				value = m_Maximum;
			}
			m_Position = value;
	//	}
		m_pScrollPaneRect = new Rectangle(GetScrollPaneX(),GetScrollPaneY(),GetScrollPaneWidth(),GetScrollPaneHeight());
		repaint();
	}
	
	public void setVertical(boolean value)
	{
		if(m_Vertical != value){
			m_Vertical = value;
		}
	}
	
	
	/**
	 * Adds the specified action listener to receive action events
	 * from this button.
	 * @param l the action listener
	 */ 
	public void addScrollBarListener(WScrollBarListener l) 
	{
		m_pListners.add(WScrollBarListener.class,l);
	}

	/**
	 * Removes the specified action listener so it no longer receives
	 * action events from this button.
	 * @param l the action listener
	 */ 
	public void removeScrollBarListener(WScrollBarListener l) 
	{
		m_pListners.remove(WScrollBarListener.class,l);
	}
	
	/**
	 * Raises PositionChanged event.
	 *
	 */
	private void RaisePositionChanged()
	{
		Object[] listeners = m_pListners.getListenerList();
		// loop through each listener and pass on the event if needed		
		for(int i=0;i < listeners.length;i++){
			if(listeners[i] == WScrollBarListener.class){
				// pass the event to the listeners event dispatch method
				((WScrollBarListener)listeners[i+1]).PositionChanged();
			}            
		}
	}
	
	/**
	 * Raise MayChangePosition event.
	 * @return
	 */
	private boolean RaiseMayChangePosition()
	{
		boolean retVal = true;
		
		Object[] listeners = m_pListners.getListenerList();
		// loop through each listener and pass on the event if needed		
		for(int i=0;i < listeners.length;i++){
			if(listeners[i] == WScrollBarListener.class){
				// pass the event to the listeners event dispatch method
				retVal = ((WScrollBarListener)listeners[i+1]).MayChangePosition();
			}            
		}
		
		return retVal;
	}
	
}
