/*
 * Created on Dec 30, 2003
 *
 */
package LumiSoft.UI.Controls;

import java.text.SimpleDateFormat;
import java.util.*;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

/**
 * DateTime edit control.
 */
public class WDateTime extends WButtonEdit_base implements FocusListener
{
	private JTextField     m_pTextbox = null;
	private WCalendarPopup m_pPopup   = null;
	
	/**
	 * Calendar popup window.
	 */
	private class WCalendarPopup extends JWindow
	{
		private WDateTime m_pParent   = null;
		private WCalendar m_pCalendar = null;
		private AWTEventListener m_pAllMouseListener = null;
				
		/**
		 * Default constructor.
		 * @param parent
		 */
		public WCalendarPopup(WDateTime parent)
		{
			super(SwingUtilities.getWindowAncestor(parent));
			
			m_pParent = parent;
			m_pParent.addHierarchyBoundsListener(new HierarchyBoundsAdapter(){
				public void ancestorMoved(HierarchyEvent e){	
					m_pParent.removeHierarchyBoundsListener(this);
					Hide();
				}
				
				public void ancestorResized(HierarchyEvent e){
					m_pParent.removeHierarchyBoundsListener(this);
					Hide();
				}
			});
					
			m_pCalendar = new WCalendar();
			m_pCalendar.setSize(194,162);
			m_pCalendar.setLocation(0,0);
			m_pCalendar.addValueChangedListener(new ActionListener(){
				public void actionPerformed(ActionEvent e){
					OnValueChanged();
				}
			});
			m_pCalendar.setValue(parent.getValue());
							
			this.setSize(196,164);
			this.add(m_pCalendar);		
		}
		
			
		/**
		 * Shows menu at specified location.
		 * @param screenX
		 * @param screenY
		 */
		public void Show(int screenX,int screenY)
		{	
			m_pAllMouseListener = new AWTEventListener(){
				public void eventDispatched(AWTEvent e){
					if(e.getID() == MouseEvent.MOUSE_PRESSED){
						// If mouse clicked outside control(not datetime or it's sub controls), close menu.
						if(e.getSource() == m_pParent || e.getSource() == this || e.getSource() == m_pCalendar){						
						}
						else{
							// Seis some sub control clicked 
							for(Component c : m_pCalendar.getComponents()){
								if(e.getSource() == c){
									return;
								}
							}
							
							// Ignore Month combo JList click 
							if(!(e.getSource() instanceof JList)){
								Hide();
							}							
						}
					}
				}
			};
			Toolkit.getDefaultToolkit().addAWTEventListener(m_pAllMouseListener,AWTEvent.MOUSE_EVENT_MASK);
			
			this.setLocation(screenX,screenY);		
			this.setVisible(true);
		}
		
		/**
		 * Hides menu.
		 *
		 */
		public void Hide()
		{	
			if(m_pAllMouseListener != null){
				Toolkit.getDefaultToolkit().removeAWTEventListener(m_pAllMouseListener);
				m_pAllMouseListener = null;
			}
			
			this.setVisible(false);
			this.dispose();
			
			m_pParent.On_m_pPopup_Closed();
		}
		
		private void OnValueChanged()
		{
			m_pParent.setValue(m_pCalendar.getValue());
			On_m_pPopup_ValueChanged();
		}
	}
	
	/**
	 * Default constructor.
	 *
	 */
	public WDateTime()
	{	
		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 keyTyped(KeyEvent e){
				On_m_pTextbox_KeyTyped(e);
			}
		});
		m_pTextbox.addFocusListener(this);
		m_pTextbox.addMouseListener(this);
		m_pTextbox.addMouseMotionListener(this);
		
		this.addFocusListener(this);
		this.add(m_pTextbox);
		
		this.setValue(GregorianCalendar.getInstance().getTime());
		
		//--- Set font -----------------------------------//
		String desktop = System.getProperty("sun.desktop");	
		if(("windows").equals(desktop)){
			setFont(new Font("SansSerif",Font.PLAIN,12));
		}
		else{
			setFont(new Font("SansSerif",Font.PLAIN,11));
		}		
	}
	
	/**
	 * Clean up any resources being used.
	 */
	public void Dispose()
	{
		super.Dispose();
		
		if(m_pPopup != null){
			m_pPopup.Hide();
			m_pPopup.dispose();
			m_pPopup = null;
		}
	}
	
	
	/**
	 * Is called when size has changed.
	 */
	protected void SizeChanged()
	{	
		m_pTextbox.setLocation(this.getEditRect().getLocation());		
		m_pTextbox.setSize(this.getEditRect().getSize());
	}
	
	/**
	 * 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)
	{
		if(e.getKeyCode() == KeyEvent.VK_DELETE || e.getKeyCode() == KeyEvent.VK_BACK_SPACE){
			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);			            
		}
	}
	
	/**
	 * Is called when key has typed down.
	 * @param e
	 */
	private void On_m_pTextbox_KeyTyped(KeyEvent e)
	{		
		if(!getEditable()){
			e.consume();
			return;
		}		
		
		e.consume();

		char   keyChar  = e.getKeyChar();
		int    curIndex = m_pTextbox.getCaretPosition();
		String text     = m_pTextbox.getText();
	        			
		if(curIndex >= 10 || text.length() < 10){
			return;
		}

		//--- Get if day->month->year or month->day->year ---//
		int monthIndex = 0;
		int dayIndex   = 3;
		GregorianCalendar cal = new GregorianCalendar(2000,2,1);
		Date t = cal.getTime();
		SimpleDateFormat f = new SimpleDateFormat();
		String tStr  = f.format(t);
		if(tStr.indexOf("1") < tStr.indexOf("3")){ // 3 because month is zero based
			dayIndex   = 0;
			monthIndex = 3;
		}
		//--------------------------------------------------//
	
		//--- Digit was pressed -------------------------------------//
		if(IsDigit(keyChar)){
		
			String nText = text.substring(0,curIndex) + keyChar + text.substring(curIndex + 1);

			int newCursPos = ++curIndex;
				
			//--- If cursor in month block -------------------------//
			if(curIndex >= monthIndex && curIndex < monthIndex + 2){		
				int mPos1Val = Integer.parseInt(nText.substring(monthIndex,monthIndex + 1));
				int mPos2Val = Integer.parseInt(nText.substring(monthIndex + 1,monthIndex + 2));
				
				// eg. |13 = 03 ('3' was pressed, '|' = cursor position where key pressed)
				// NOTE: month start can't be bigger than 1
				if(mPos1Val > 1){
					nText = text.substring(0,monthIndex) + "0" + keyChar + text.substring(monthIndex + 2);
					newCursPos = monthIndex + 3;
				}
				// eg. |13 = 10 ('1' was pressed, '|' = cursor position where key pressed)
				else if(mPos1Val == 1 && mPos2Val > 2  && monthIndex == curIndex-1){
					nText = text.substring(0,monthIndex) + "0" + text.substring(monthIndex + 1);
				}
				// eg. |10 = 01 ('0' was pressed, '|' = cursor position where key pressed)
				else if(mPos1Val == 0 && mPos2Val == 0){
					nText = text.substring(0,monthIndex) + "1" + text.substring(monthIndex + 1);
				}
			}
			//----------------------------------------------------------------//

			//--- If cursor in day block ------------------------------------//
			else if(curIndex >= dayIndex && curIndex < dayIndex + 2){
				int dPos1Val = Integer.parseInt(nText.substring(dayIndex,dayIndex + 1));
				int dPos2Val = Integer.parseInt(nText.substring(dayIndex + 1,dayIndex + 2));
				int month    = Integer.parseInt(nText.substring(monthIndex,monthIndex + 2));
				int year     = Integer.parseInt(nText.substring(6,6 + 4));
			
				int maxDayPos1Val = 2;
				int maxDayPos2Val = 0;
				GregorianCalendar calendar = new GregorianCalendar(year,month,1);
				if(calendar.getActualMaximum(Calendar.DAY_OF_MONTH) >= 30){
					maxDayPos1Val = 3;
					maxDayPos2Val = calendar.getActualMaximum(Calendar.DAY_OF_MONTH) - 30;
				}
				else{
					maxDayPos2Val = calendar.getActualMaximum(Calendar.DAY_OF_MONTH) - 20;
				}

				// eg. |41 = 04 ('4' was pressed, '|' = cursor position where key pressed)
				if(dPos1Val > maxDayPos1Val){
					nText = text.substring(0,dayIndex) + "0" + keyChar + text.substring(dayIndex + 2);

					dPos2Val = dPos1Val;
					dPos1Val = 0;

					newCursPos = dayIndex + 3;
				}

				// eg. |33 = 03 ('3' was pressed, '|' = cursor position where key pressed)
				if(dPos1Val == maxDayPos1Val && dPos2Val > maxDayPos2Val && dayIndex == curIndex-1){
					nText = text.substring(0,dayIndex) + "0" + text.substring(dayIndex + 1);
				}

				// eg. |10 = 01 ('0' was pressed, '|' = cursor position where key pressed)
				if(dPos1Val == 0 && dPos2Val == 0){
					nText = text.substring(0,dayIndex) + "1" + text.substring(dayIndex + 1);
				}
			}
			//----------------------------------------------------//
			
			if(IsDateOk(nText)){
				m_pTextbox.setText(nText);

				if(newCursPos < 9 && (nText.substring(newCursPos,newCursPos + 1).equals(",") || nText.substring(newCursPos,newCursPos + 1).equals(".") || nText.substring(newCursPos,newCursPos + 1).equals("/"))){
					newCursPos++;
				}
				m_pTextbox.setCaretPosition(newCursPos);
			}
		}

		//---- , or . was pressed -----------------------//
		else if(keyChar == ',' || keyChar == '.'){
			if(text.indexOf(String.valueOf(keyChar),curIndex) > -1){
				m_pTextbox.setCaretPosition(text.indexOf(String.valueOf(keyChar),curIndex) + 1);
			}		
		}
		
		
		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.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();
	}
	
	
	/**
	 * Is called when date popup value has changed.
	 *
	 */
	protected void On_m_pPopup_ValueChanged()
	{
		if(m_pPopup != null){
			m_pPopup.Hide();
			m_pPopup = null;
		}
	}
	
	/**
	 * Is called when date popup has closed.
	 *
	 */
	protected void On_m_pPopup_Closed()
	{
		if(m_pPopup != null){
			m_pPopup.dispose();
			m_pPopup = null;
		}
	}
	
	/**
	 * Is called when button edit button has clicked.
	 *
	 */
	protected void RaiseButtonClicked()
	{
		super.RaiseButtonClicked();
	
		if(!m_pTextbox.hasFocus()){
			m_pTextbox.grabFocus();
		}
		
		if(m_pPopup == null){
			m_pPopup = new WCalendarPopup(this);
			m_pPopup.Show(getLocationOnScreen().x,getLocationOnScreen().y + getHeight());
		}
		else{
			m_pPopup.Hide();
		}
	}
	
	/**
	 * Determines if specified string is valid Date value.
	 * @param date
	 * @return
	 */
	private boolean IsDateOk(String date)
	{
		try{
			SimpleDateFormat f = new SimpleDateFormat("dd.MM.yyyy");
			Date d = f.parse(date);
			if(d != null){
				return true;
			}
		}
		catch(Exception x){	
		}
	
		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 a date value.
	 * @return
	 */
	public Date getValue()
	{
		try{
			SimpleDateFormat f = new SimpleDateFormat("dd.MM.yyyy");
			if(f.parse(m_pTextbox.getText()) != null){
				return f.parse(m_pTextbox.getText());
			}
		}
		catch(Exception x){			
		}
		
		return GregorianCalendar.getInstance().getTime();		
	}
	/**
	 * 
	 * @param value
	 */
	public void setValue(Date value)
	{
		SimpleDateFormat f = new SimpleDateFormat("dd.MM.yyyy");
		m_pTextbox.setText(f.format(value));
	}
	
	/**
	 * Sets if enabled.
	 */
	public void setEnabled(boolean value)
	{
		super.setEnabled(value);
		m_pTextbox.setEnabled(value);
		m_pTextbox.setBackground(getViewStyle().GetEditColor(false,value));
	}
	
	/**
	 * Sets if control is editable.
	 */
	public void setEditable(boolean value)
	{
		m_pTextbox.setBackground(getViewStyle().GetEditColor(!value,this.isEnabled(),this.hasFocus()));
		super.setEditable(value);
	}
	
	/**
	 * Sets read only.
	 *
	 */
	public void setReadOnly(boolean value)
	{		
		setEditable(!value);
		setSelectable(!value);
	}
	/**
	 * Gets if read only.
	 * @return
	 */
	public boolean getReadOnly()
	{
		return !getEditable() && !getSelectable();
	}
}
