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

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

/**
 * Text edit control.
 */
public class WTextEdit extends WControl implements FocusListener,KeyListener,MouseListener,MouseMotionListener
{
	private EventListenerList m_pListners          = null;
	private Timer             m_pTimer             = null;
	private int               m_FlashCounter       = 0;	
	private JTextComponent    m_pTextbox           = null;
	private JComponent        m_pMainTxtComp       = null;
	private int               m_TextAlign          = HorizontalAlignment.Left;
	private boolean           m_MultiLine          = false;
	private boolean           m_ReadOnly           = false;
	private String            m_pPasswordChar      = "";
	private int               m_MaxLength          = 9999999;
	private Point             m_MousePos           = new Point(-1,-1);
	private Dimension         m_Size               = new Dimension(0,0);
	
	/**
	 * Default constructor.
	 *
	 */
	public WTextEdit()
	{
		super();
		
		m_pListners = new EventListenerList();		
		m_pTimer = new Timer(200,new ActionListener(){
			public void actionPerformed(ActionEvent evt){
		         On_m_pTimer_Tick();
		    }
		});
			
		this.addFocusListener(this);
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.setLayout(null);
		
		setMultiline(false);
	}
	
	
	/**
	 * Is called by system when control need to repaint.
	 */
	public void paintComponent(Graphics g)
	{
		if(!m_Size.equals(this.getSize())){
			m_Size = this.getSize();
			
			SizeChanged();			
		}
		
		Rectangle clientRect = new Rectangle(0,0,this.getSize().width,this.getSize().height);
		boolean hot =  this.isEnabled() && (Utils.ContainsFocus(this) || (clientRect.contains(m_MousePos)));
		Draw((Graphics2D)g,hot);
	}
	
	private void Draw(Graphics2D g,boolean hot)
	{
		Rectangle clientRect = new Rectangle(0,0,this.getSize().width - 1,this.getSize().height - 1);
		
		// Draw border
		g.setColor(getViewStyle().GetBorderColor(hot));		
		g.draw(clientRect);
	}
	
	/**
	 * Is called when size has changed.
	 */
	protected void SizeChanged()
	{
		if(m_pMainTxtComp != null){
			m_pMainTxtComp.setBounds(new Rectangle(1,1,this.getWidth() - 2,this.getHeight() - 2));
			m_pMainTxtComp.revalidate();
		}
	}
	
	/**
	 * 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());
	}
	
	
	public void focusGained(FocusEvent e)
	{
		if(!m_pTextbox.hasFocus()){
			m_pTextbox.grabFocus();
		}
		m_pTextbox.selectAll();
		m_pTextbox.setBackground(getViewStyle().GetEditColor(this.getReadOnly(),this.isEnabled(),true));
		
		this.repaint();
	}
	
	public void focusLost(FocusEvent e)
	{
		m_pTextbox.setBackground(getViewStyle().GetEditColor(this.getReadOnly(),this.isEnabled(),false));
		
		this.repaint();
	}
	
	//	--- Forward nested editbox keyevents to this -------------------//
	public void keyTyped(KeyEvent e)
	{		
		// Maximum allowed text length exceeded
		if((getText().length() - Math.abs(m_pTextbox.getSelectionEnd() - m_pTextbox.getSelectionStart())) >= getMaxLength()){
			e.consume();
		}
		
		// If read only, allow only arrows to be used
		if(m_ReadOnly){
			if(!ReadOnlyAllowedKey(e)){
				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 keyPressed(KeyEvent e)
	{		
		// If read only, allow only arrows to be used
		if(m_ReadOnly){
			if(!ReadOnlyAllowedKey(e)){
				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].keyPressed(e);			            
		}	
	}
	public void keyReleased(KeyEvent e)
	{	
		// If read only, allow only arrows to be used
		if(m_ReadOnly){
			if(!ReadOnlyAllowedKey(e)){
				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].keyReleased(e);			            
		}	
	}
	
	/**
	 * Checks if specified key is allowed when this is readonly.
	 * @param e
	 * @return Returns true if specified key is allowed.
	 */
	private boolean ReadOnlyAllowedKey(KeyEvent e)
	{
		if(e.getKeyCode() == KeyEvent.VK_LEFT){
			return true;
		}
		else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
			return true;
		}
		else if(e.getKeyCode() == KeyEvent.VK_UP){
			return true;
		}
		else if(e.getKeyCode() == KeyEvent.VK_DOWN){
			return true;
		}
		else if(e.getKeyCode() == KeyEvent.VK_C && e.isControlDown()){
			return true;
		}
		
		return false;
	}
	//-----------------------------------------------------------------//
	
	public void mouseReleased(MouseEvent e)
	{
		// Reset, we don't need them anymore
	//	m_MouseModifiers = 0;		
	}
	
	public void mouseEntered(MouseEvent e)
	{			
		this.repaint();
	}
	
	public void mouseExited(MouseEvent e)
	{
		m_MousePos = new Point(-1,-1);
		
		this.repaint();
	}
	
	public void mouseMoved(MouseEvent e)
	{
		m_MousePos = e.getPoint();
	}
	
	public void mousePressed(MouseEvent e){}
	public void mouseClicked(MouseEvent e){}	
	public void mouseDragged(MouseEvent e){}
	
	
	/**
	 * Is called when timer elapsed.
	 */
	private void On_m_pTimer_Tick()
	{
		if(m_pTextbox.getBackground() == getViewStyle().getFlashColor()){
			m_pTextbox.setBackground(getViewStyle().GetEditColor(this.getReadOnly(),this.isEnabled(),false));
		}
		else{
			m_pTextbox.setBackground(getViewStyle().getFlashColor());
		}
		
		m_FlashCounter++;
		
		if(m_FlashCounter > 5){
			m_pTimer.stop();
		}
	}
	
	
	/**
	 * Flashes control.
	 */
	public void FlashControl()
	{	
		if(!m_pTimer.isRunning()){
			m_FlashCounter = 0;
			m_pTimer.start();
		}
	}
	
	
	/**
	 * Gets text.
	 * @return
	 */
	public String getText()
	{
		return m_pTextbox.getText();
	}
	/**
	 * Sets text.
	 * @param value
	 */
	public void setText(String value)
	{
		m_pTextbox.setText(value);		
		m_pTextbox.setCaretPosition(0);
		
		if(m_pTextbox instanceof JTextField){
			if(getFontMetrics(getFont()).stringWidth(value) > getWidth()){
				m_pTextbox.setToolTipText(value);
			}
		}
		else if(m_pTextbox instanceof JTextArea){
			if(m_pTextbox.getPreferredSize().height > getHeight()){
				String[] lines = value.replaceAll("\r","").split("\n");
				String tText = "<html>";
				int i = 0;
				for(String line : lines){
					tText += "<p>" + line + "</p>";
					i++;
					
					// Don't allow over 10 lines
					if(i > 10){
						break;
					}
				}
				tText += "</html>";
				
				m_pTextbox.setToolTipText(tText);
			}
		}
	}
	
	/**
	 * 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 password char.
	 * @return
	 */
	public String getPasswordChar()
	{
		return m_pPasswordChar;
	}
	/**
	 * Sets password char.
	 * @param value
	 */
	public void setPasswordChar(String value)
	{
		if(m_pPasswordChar != value){
			m_pPasswordChar = value;
			
			if(value.length() == 0){
				m_pTextbox = null;
				setMultiline(this.getMultiline());
			}
			else{
				m_pTextbox = new JPasswordField();
				m_pTextbox.setBorder(null);
				m_pTextbox.addKeyListener(this);
				m_pTextbox.addFocusListener(this);
				m_pTextbox.addMouseListener(this);
				m_pTextbox.addMouseMotionListener(this);
				((JPasswordField)m_pTextbox).setEchoChar(value.toCharArray()[0]);
				m_pMainTxtComp = m_pTextbox; 
				this.add(m_pTextbox);
			}
		}
	}
	
	/**
	 * Gets text alignmet.
	 * @return
	 */
	public int getTextAlign()
	{
		return m_TextAlign;
	}
	/**
	 * Sets text alignment.
	 * @param value
	 */
	public void setTextAlign(int value)
	{
		if(m_TextAlign != value && !getMultiline()){
			JTextField textField = (JTextField)m_pTextbox;
			switch(value)
			{
				case HorizontalAlignment.Left:
					textField.setHorizontalAlignment(SwingConstants.LEFT);
					break;
					
				case HorizontalAlignment.Center:
					textField.setHorizontalAlignment(SwingConstants.CENTER);
					break;
					
				case HorizontalAlignment.Right:
					textField.setHorizontalAlignment(SwingConstants.RIGHT);
					break;
			}
		}
		
		m_TextAlign = value;
	}
	
	/**
	 * Gets if editbox allows multiline.
	 * @return
	 */
	public boolean getMultiline()
	{
		return m_MultiLine;
	}
	/**
	 * Sets if editbox allows multiline.
	 * @param value
	 */
	public void setMultiline(boolean value)
	{
		if(m_MultiLine != value || m_pTextbox == null){
			if(m_pMainTxtComp != null){
				this.remove(m_pMainTxtComp);
			}
			
			if(value){
				m_pTextbox = new JTextArea();
				((JTextArea)m_pTextbox).setLineWrap(true);
				JScrollPane p = new JScrollPane(m_pTextbox);				
				p.setBorder(null);
				p.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
				p.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
				m_pMainTxtComp = p;					
			}
			else{
				m_pTextbox = new JTextField();
				m_pMainTxtComp = m_pTextbox;				
			}
					
			m_pTextbox.setFont(new Font("SansSerif",Font.PLAIN,12));
			m_pTextbox.setDisabledTextColor(getViewStyle().getEditDisabledTextColor());
			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_pTextbox.addKeyListener(this);
			m_pTextbox.addFocusListener(this);
			m_pTextbox.addMouseListener(this);
			m_pTextbox.addMouseMotionListener(this);
			m_pMainTxtComp.setBorder(null);
			
			this.add(m_pMainTxtComp);
			
			m_MultiLine = value;
		}
	}
	
	/**
	 * Sets if enabled.
	 */
	public void setEnabled(boolean value)
	{
		super.setEnabled(value);
		m_pTextbox.setEnabled(value);
		m_pTextbox.setBackground(getViewStyle().GetEditColor(getReadOnly(),value,this.hasFocus()));
		
		this.repaint();
	}
	
	/**
	 * Gets if numeric box is readonly.
	 * @return
	 */
	public boolean getReadOnly()
	{
		return m_ReadOnly;
	}
	/**
	 * Sets if numeric box is readonly.
	 * @param value
	 */
	public void setReadOnly(boolean value)
	{
		if(m_ReadOnly != value){
			m_ReadOnly = value;	
			m_pTextbox.setBackground(getViewStyle().GetEditColor(value,this.isEnabled(),this.hasFocus()));
		}
	}
	
	/**
	 * Gets text selection start position.
	 * @return
	 */
	public int getSelectionStart()
	{
		return m_pTextbox.getSelectionStart();
	}
	/**
	 * Sets text selection start position.
	 * @param value
	 */
	public void setSelectionStart(int value)
	{
		m_pTextbox.setSelectionStart(value);
	}
	
	/**
	 * Gets text selection end position.
	 * @return
	 */
	public int getSelectionEnd()
	{
		return m_pTextbox.getSelectionEnd();
	}
	/**
	 * Sets text selection end position.
	 * @param value
	 */
	public void setSelectionEnd(int value)
	{
		m_pTextbox.setSelectionEnd(value);
	}
	
	/**
	 * Gets the maximum number of characters allowed in the text box.
	 * @return
	 */
	public int getMaxLength()
	{
		return m_MaxLength;
	}
	/**
	 * Sets the maximum number of characters allowed in the text box.
	 * @param value
	 */
	public void setMaxLength(int value)
	{
		if(m_MaxLength != value){
			m_MaxLength = value;
		}
	}
	
	
	/**
	 * Adds the specified action listener to receive action events.
	 * @param l the action listener
	 */ 
	public void addValueChangedListener(ActionListener l) 
	{
		m_pListners.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_pListners.remove(ActionListener.class,l);
	}
	
	/**
	 * Raises value changed event.
	 *
	 */
	protected void RaiseValueChanged()
	{
		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] == ActionListener.class){
				// pass the event to the listeners event dispatch method
				((ActionListener)listeners[i + 1]).actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,""));
			}            
		}
	}

}
