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

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

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.EventListenerList;

/**
 * Numeric edit control.
 */
public class WNumericEdit extends WSpinEdit_base implements FocusListener
{
	private EventListenerList m_pValueChangedLister = null;
	private JTextField        m_pTextbox            = null;
	private int               m_SpinnerStep         = 1;
	private double            m_MaximumValue        = Double.MAX_VALUE;
	private double            m_MinimumValue        = Double.MIN_VALUE;
	private int               m_DecPlaces           = 0;
	private int               m_TextAlign           = HorizontalAlignment.Left;
			
	/**
	 * Default constructor.
	 *
	 */
	public WNumericEdit()
	{		
		m_pTextbox = new JTextField();
		m_pTextbox.setBorder(null);
		m_pTextbox.setDisabledTextColor(getViewStyle().getEditDisabledTextColor());
		m_pTextbox.addKeyListener(new KeyAdapter(){
			public void keyPressed(KeyEvent e){
				On_m_pTextbox_KeyPressed(e);
			}
			
			public void keyReleased(KeyEvent e){
				On_m_pTextbox_KeyReleased(e);
			}
			
			public void keyTyped(KeyEvent e){
				On_m_pTextbox_KeyTyped(e);
			}
		});
		m_pTextbox.addFocusListener(this);
		m_pTextbox.addMouseListener(new MouseAdapter(){
			public void mousePressed(MouseEvent e){
				OnMousePressed(e);
			}
			
			public void mouseReleased(MouseEvent e){
				OnMouseReleased(e);
			}
			
			public void mouseEntered(MouseEvent e){
				OnMouseEntered(e);
			}
			
			public void mouseExited(MouseEvent e){
				OnMouseExited(e);
			}
		});
		m_pTextbox.addMouseMotionListener(new MouseMotionAdapter(){
			public void mouseMoved(MouseEvent e){
				OnMouseMoved(e);
			}
		});
		m_pTextbox.setText("0");
		m_pTextbox.getDocument().addDocumentListener(new DocumentListener(){
			public void changedUpdate(DocumentEvent e) { textChanged(e); }
		    public void insertUpdate(DocumentEvent e) { textChanged(e); }
		    public void removeUpdate(DocumentEvent e) { textChanged(e); }
		   
		    private void textChanged(DocumentEvent e){
		    	RaiseValueChanged();
		    }				
		});	
		
		m_pValueChangedLister = new EventListenerList();
		
		this.addFocusListener(this);
		this.add(m_pTextbox);
	}
	
	
	/**
	 * Is called when size has changed.
	 */
	protected void SizeChanged()
	{	
		m_pTextbox.setBounds(this.getEditRect());
	}
	
	/**
	 * Is called when ViewStyle has changed.
	 *
	 */
	protected void OnViewStyleChaned()
	{
		super.OnViewStyleChaned();
		
		m_pTextbox.setBackground(getViewStyle().GetEditColor(this.getReadOnly(),this.isEnabled(),m_pTextbox.hasFocus()));
		m_pTextbox.setDisabledTextColor(getViewStyle().getEditDisabledTextColor());
	}
	
	/**
	 * Is called when key has pressed down.
	 * @param e
	 */
	private void On_m_pTextbox_KeyPressed(KeyEvent e)
	{
		KeyListener[] listeners = this.getKeyListeners();
		for(int i=0;i < listeners.length;i++){
			// pass the event to the listeners event dispatch method
			listeners[i].keyPressed(e);			            
		}
	}
			
	/**
	 * Is called when key has released.
	 * @param e
	 */
	private void On_m_pTextbox_KeyReleased(KeyEvent e)
	{
		if(m_pTextbox.getText().startsWith("-")){
			m_pTextbox.setForeground(Color.RED);			
		}
		else{
			m_pTextbox.setForeground(Color.BLACK);			
		}
				
		KeyListener[] listeners = this.getKeyListeners();
		for(int i=0;i < listeners.length;i++){
			// pass the event to the listeners event dispatch method
			listeners[i].keyReleased(e);			            
		}
	}
	
	/**
	 * Is called when key has typed down.
	 * @param e
	 */
	private void On_m_pTextbox_KeyTyped(KeyEvent e)
	{	
		if(getReadOnly()){
			e.consume();
			return;
		}
		
		if(Handle_Numeric(e.getKeyChar())){
			e.consume();			
		}
		
		KeyListener[] listeners = this.getKeyListeners();
		for(int i=0;i < listeners.length;i++){
			// pass the event to the listeners event dispatch method
			listeners[i].keyTyped(e);			            
		}
	}
	
	public void focusGained(FocusEvent e)
	{
		if(!m_pTextbox.hasFocus()){
			m_pTextbox.grabFocus();
		}
		m_pTextbox.selectAll();
		m_pTextbox.setBackground(getViewStyle().GetEditColor(this.getReadOnly(),true,true));
		
		this.repaint();
	}
	
	public void focusLost(FocusEvent e)
	{
		m_pTextbox.setBackground(getViewStyle().GetEditColor(this.getReadOnly(),true,false));
		
		this.repaint();
		
		if(m_pTextbox.getText().length() == 0 || m_pTextbox.getText() == "-"){
			m_pTextbox.setText("0");
		}
		
		if(m_pTextbox.getText().endsWith(".")){System.out.println();
			m_pTextbox.setText(m_pTextbox.getText().substring(0,m_pTextbox.getText().length() - 1));
		}
	}
	
	
	private boolean Handle_Numeric(char pressedChar)
	{
		char    decSep      = '.';
		String  text        = m_pTextbox.getText();
		boolean isSeparator = text.indexOf(decSep) > -1;
		int     sepPos      = text.indexOf(decSep);
		
		if(pressedChar == ',' || pressedChar == '.'){
			pressedChar = decSep;
		}
		
		//--- Clear selection if any and insert pressedChar to buffer string
		if(IsDigit(pressedChar) || pressedChar == decSep || pressedChar == '-'){
			text = text.substring(0,m_pTextbox.getSelectionStart()) + pressedChar + text.substring(m_pTextbox.getSelectionEnd());
		}
		
		if(pressedChar == decSep){
			if(!isSeparator){	
				if(text.equals(String.valueOf(decSep))){
					m_pTextbox.setText("0" + decSep);
					return true;
				}
				// Check that decimal places aren't exceeded,
				// when setting decimal place in the middle on number.
				// eg. 232,312 and allowed decimal places = 2.
				if(text.length() - text.indexOf(decSep) - 1 > m_DecPlaces){	
					return true;
				}
			}
			else{
				// If there is already decimal separator, move after it.
				if(m_pTextbox.getText().indexOf(decSep) > -1){
					m_pTextbox.setSelectionStart(m_pTextbox.getText().indexOf(decSep) + 1);
					return true;
				}
			}
			
			int start = m_pTextbox.getCaretPosition();
			m_pTextbox.setText(text);
			m_pTextbox.setCaretPosition(start + 1);
			
			return true;
		}
		else if(pressedChar == '-'){
			if(m_pTextbox.getSelectionStart() > 0 || m_pTextbox.getText().startsWith("-")){					
				return true;
			}			
		}
		else if(pressedChar == '\b'){
			return false;			
		}
		else if(IsDigit(pressedChar)){			
			//--- Avoid following numbers '06' ------------------------------------------
			if(text.startsWith("0") && text.length() > 1 && text.indexOf(decSep) != 1){
				if(m_pTextbox.getSelectionStart() == 1 && text.length() == 2){
					m_pTextbox.setText("" + pressedChar);
				}
				return true;
					
			}
			if(text.startsWith("-0") && text.length() > 2 && text.indexOf(decSep) != 2){
				if(m_pTextbox.getSelectionStart() == 2 && text.length() == 3){
					m_pTextbox.setText("" + pressedChar);
				}					
				return true;				
			}
			//-----------------------------------------------------------------------------
				
			// Check that decimal places aren't exceeded.
			if(isSeparator){
				// If current position is inside deciaml places '_,x|xx'.
				if(m_pTextbox.getSelectionStart() > sepPos){
					// There is max number of dicmal places and current pos is at end
					if((m_pTextbox.getSelectionStart() - sepPos) > m_DecPlaces){
						return true;
					}

					// There is max number of dicmal places
					if((text.length() - sepPos) > m_DecPlaces){
						String newVal = text.substring(0,m_pTextbox.getSelectionStart());
						newVal += pressedChar;

						m_pTextbox.setText(newVal);
						m_pTextbox.setSelectionStart(m_pTextbox.getSelectionStart() + 1); 
						return true;
					}
				}
			}			
		}
		else{
			return true;
		}
		
		return false;		
	}
	
	/**
	 * Determines if specified char is digit.
	 * @param c Char what to check.
	 * @return Returns true if char is digit.
	 */
	private boolean IsDigit(char c)
	{
		switch(c)
		{
			case '0':
				return true;				
			case '1':
				return true;				
			case '2':
				return true;				
			case '3':
				return true;				
			case '4':
				return true;				
			case '5':
				return true;			
			case '6':
				return true;				
			case '7':
				return true;				
			case '8':
				return true;				
			case '9':
				return true;
		}
		
		return false;
	}
	
	
	/**
	 * Gets font.
	 */
	public Font getFont()
	{
		if(m_pTextbox != null){
			return m_pTextbox.getFont();
		}
		else{
			return super.getFont();
		}
	}
	/**
	 * Sets font.
	 */
	public void setFont(Font value)
	{
		if(m_pTextbox != null){
			m_pTextbox.setFont(value);
		}
	}
	
	/**
	 * Gets numericbox value. 
	 * @return
	 */
	public double getValue()
	{
		try{
			if(m_pTextbox.getText().replaceAll(",",".").length() > 0){
				return Double.parseDouble(m_pTextbox.getText().replaceAll(",","."));
			}
			else{
				return 0;
			}
		}
		catch(Exception x){
			return 0;
		}
	}
	/**
	 * Sets numericbox value.
	 * @param value
	 */
	public void setValue(double value)
	{		
		java.text.DecimalFormat format = new java.text.DecimalFormat();
		format.setMinimumFractionDigits(getDecimalPlaces());
		format.setMaximumFractionDigits(getDecimalPlaces());
		format.setGroupingUsed(false);
				
		m_pTextbox.setText(format.format(value).replaceAll(",","."));
	}
	
	/**
	 * Gets maximum value allowed to enter.
	 * @return
	 */
	public double getMaximumValue()
	{
		return m_MaximumValue;
	}
	/**
	 * Sets maximum value allowed to enter.
	 * @param value
	 */
	public void setMaximumValue(double value)
	{
		if(m_MaximumValue != value){
			m_MaximumValue = value;
		}
	}
	
	/**
	 * Gets minimum value allowed to enter.
	 * @return
	 */
	public double getMinimumValue()
	{
		return m_MinimumValue;
	}
	/**
	 * Sets minimum value allowed to enter.
	 * @param value
	 */
	public void setMinimumValue(double value)
	{
		if(m_MinimumValue != value){
			m_MinimumValue = value;
		}
	}
	
	/**
	 * Gets number of decimal places.
	 * @return
	 */
	public int getDecimalPlaces()
	{
		return m_DecPlaces;
	}
	/**
	 * Sets number of decimal places.
	 * @param value
	 */
	public void setDecimalPlaces(int value)
	{
		if(m_DecPlaces != value){
			m_DecPlaces = value;
		}
	}
	
	/**
	 * Gets spinner step to use for incrementing and decrrementing. 
	 * @return
	 */
	public int getSpinnerStep()
	{
		return m_SpinnerStep;
	}
	/**
	 * Sets spinner step to use for incrementing and decrrementing. 
	 * @param value
	 */
	public void setSpinnerStep(int value)
	{
		if(m_SpinnerStep != value){
			m_SpinnerStep = value;			
		}
	}
	
	/**
	 * Gets text alignmet.
	 * @return
	 */
	public int getTextAlign()
	{
		return m_TextAlign;
	}
	/**
	 * Sets text alignment.
	 * @param value
	 */
	public void setTextAlign(int value)
	{
		if(m_TextAlign != value){
			switch(value)
			{
				case HorizontalAlignment.Left:
					m_pTextbox.setHorizontalAlignment(SwingConstants.LEFT);
					break;
				
				case HorizontalAlignment.Center:
					m_pTextbox.setHorizontalAlignment(SwingConstants.CENTER);
					break;
				
				case HorizontalAlignment.Right:
					m_pTextbox.setHorizontalAlignment(SwingConstants.RIGHT);
					break;
			}
		}
		
		m_TextAlign = value;
	}
	
	
	/**
	 * Sets if numeric box is readonly.
	 * @param value
	 */
	public void setReadOnly(boolean value)
	{
		if(super.getReadOnly() != value){
			super.setReadOnly(value);
			
			m_pTextbox.setBackground(getViewStyle().GetEditColor(value,this.isEnabled(),false));
		}
	}
	
	/**
	 * Sets if numericbox is enabled.
	 */
	public void setEnabled(boolean value)
	{
		super.setEnabled(value);
		
		m_pTextbox.setEnabled(value);		
		m_pTextbox.setBackground(getViewStyle().GetEditColor(this.getReadOnly(),value,false));
		
		this.repaint();
	}
	
	
	protected void OnUpButtonClicked() 
	{
		super.OnUpButtonClicked();
	
		if(!this.getReadOnly()){
			this.setValue(Double.parseDouble(m_pTextbox.getText()) + m_SpinnerStep);
		}
	}
	
	protected void OnDownButtonClicked() 
	{
		super.OnDownButtonClicked();
	
		if(!this.getReadOnly()){
			this.setValue(Double.parseDouble(m_pTextbox.getText()) - m_SpinnerStep);
		}
	}	
	
	
	/**
	 * Adds the specified action listener to receive action events.
	 * @param l the action listener
	 */ 
	public void addValueChangedListener(ActionListener l) 
	{
		m_pValueChangedLister.add(ActionListener.class,l);
	}

	/**
	 * Removes the specified action listener so it no longer receives action events.
	 * @param l the action listener
	 */ 
	public void removeValueChangedListener(ActionListener l) 
	{
		m_pValueChangedLister.remove(ActionListener.class,l);
	}
	
	/**
	 * Raises value changed event.
	 *
	 */
	protected void RaiseValueChanged()
	{
		Object[] listeners = m_pValueChangedLister.getListenerList();
		// loop through each listener and pass on the event if needed		
		for(int i=0;i < listeners.length;i++){
			if(listeners[i] == ActionListener.class){
				// pass the event to the listeners event dispatch method
				((ActionListener)listeners[i + 1]).actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,""));
			}            
		}
	}

}
