/*
 * Created on Feb 16, 2004
 *
 */
package LumiSoft.UI.Controls;

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

/**
 * Calendar control.
 */
public class WCalendar extends WControl implements MouseListener,MouseMotionListener
{	
	private EventListenerList m_pListners       = null;
	private GregorianCalendar m_pToday          = null;
	private GregorianCalendar calendar          = null;
	private GregorianCalendar m_pSelectedDate   = null;
	private ArrayList         m_pDates          = null;
	private Point             m_MousePos        = new Point(-1,-1);
	private String            m_HittedObject    = "-1";
	private Rectangle         m_pHittedDateRect = null;
	private char[]            m_pDayNameLetters = null;
	
	private WComboBox         m_pMonth          = null;
	private WNumericEdit      m_pYear           = null;
	private WButton           m_pPervMonth      = null;
	private WButton           m_pNextMonth      = null;
	
	/**
	 * Default constructor.
	 *
	 */
	public WCalendar()
	{
		this.setLayout(null);
		
		m_pListners = new EventListenerList();
				
		calendar = new GregorianCalendar();
		calendar.setFirstDayOfWeek(Calendar.MONDAY);
		m_pSelectedDate = (GregorianCalendar)calendar.clone();
		m_pToday = new GregorianCalendar();
		
		m_pDates = new ArrayList();
		
		m_pMonth = new WComboBox();
		m_pMonth.setSize(94,20);
		m_pMonth.setLocation(24,1);
		m_pMonth.setEditable(false);
		m_pMonth.setMaxVisibleItems(12);
		m_pMonth.addSelectionChangedListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				On_m_pMonth_Changed();
			}
		});
		
		m_pYear = new WNumericEdit();
		m_pYear.setSize(50,20);
		m_pYear.setLocation(120,1);
		m_pYear.setDecimalPlaces(0);
//		m_pYear.setEnabled(false);
		m_pYear.setValue(calendar.get(Calendar.YEAR));		
		m_pYear.addValueChangedListener(new  ActionListener(){
			public void actionPerformed(ActionEvent e){
				On_m_pYear_Changed();
			}
		});
		
		m_pPervMonth = new WButton();
		m_pPervMonth.setSize(20,18);
		m_pPervMonth.setLocation(2,2);
		m_pPervMonth.setText("<<");
		m_pPervMonth.addClickedListener(new  ActionListener(){
			public void actionPerformed(ActionEvent e){
				On_m_pPrevMonth_Clicked();
			}
		});
		
		m_pNextMonth = new WButton();
		m_pNextMonth.setSize(20,18);
		m_pNextMonth.setLocation(172,2);
		m_pNextMonth.setText(">>");
		m_pNextMonth.addClickedListener(new  ActionListener(){
			public void actionPerformed(ActionEvent e){
				On_m_pNextMonth_Clicked();
			}
		});
		
		this.add(m_pMonth);
		this.add(m_pYear);
		this.add(m_pPervMonth);
		this.add(m_pNextMonth);
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		
		FillMonthNames();
		m_pMonth.setSelectedIndex(calendar.get(Calendar.MONTH));
	}
		
	
	/**
	 * Is called by system when control need to repaint.
	 */
	public void paintComponent(Graphics g)
	{		
		m_pDates.clear();
			
		// Paint backround
		g.setColor(Color.WHITE);
		g.fillRect(0,0,200,200);
		
		g.setColor(Color.BLUE);
		g.fillRect(0,0,200,22);
		
		g.setColor(Color.BLACK);
		g.drawLine(0,37,200,37);
		
		g.setColor(Color.BLACK);
		g.drawLine(0,143,200,143);
		
		// Print day name letters
		char[] dayLetters = getDayNameLetters();
		g.drawString(String.valueOf(dayLetters[0]),12,34);
		g.drawString(String.valueOf(dayLetters[1]),38,34);
		g.drawString(String.valueOf(dayLetters[2]),64,34);
		g.drawString(String.valueOf(dayLetters[3]),92,34);
		g.drawString(String.valueOf(dayLetters[4]),118,34);
		g.drawString(String.valueOf(dayLetters[5]),146,34);
		g.drawString(String.valueOf(dayLetters[6]),174,34);
		
		// Today
		g.setColor(Color.RED);
		g.drawRect(2,147,22,13);
		m_pDates.add(new Object[]{new Rectangle(2,147,22,13),new Integer(-99)});
		g.drawString("Today",28,158);
	
		GregorianCalendar cal = ((GregorianCalendar)calendar.clone());		
		cal.set(Calendar.DAY_OF_MONTH,1);
		int firstDay = cal.get(Calendar.DAY_OF_WEEK) - 1;
		if(firstDay == 0){ // ?? Sunday == 0, for some reason. Seems Java reports days from sunday
			firstDay = 7;		
		}
	
		int y = 40;
		int counter = 1;
		for(int i=0;i<6;i++){
			int x = 3;
			for(int d=0;d<7;d++){
				if(counter >= firstDay && (counter - firstDay < calendar.getActualMaximum(Calendar.DAY_OF_MONTH))){
					m_pDates.add(new Object[]{new Rectangle(x,y,26 - 1,15),new Integer(counter - firstDay + 1)});
					
					PaintDate((Graphics2D)g,counter - firstDay + 1,new Rectangle(x,y,26 - 1,15));
				}
				x += 26 + 1;				
				counter++;
			}
			y += 15 + 2;
		}
		
		// Paint border
		g.setColor(Color.GRAY);
		g.drawRect(0,0,this.getWidth() - 1,this.getHeight() - 1);
	}
	
	private void PaintDate(Graphics2D g,int date,Rectangle rect)
	{
		boolean selected = calendar.get(Calendar.YEAR) == m_pSelectedDate.get(Calendar.YEAR) && calendar.get(Calendar.MONTH) == m_pSelectedDate.get(Calendar.MONTH) && date == m_pSelectedDate.get(Calendar.DAY_OF_MONTH);
		
		// Paint backround
		if(rect.contains(m_MousePos) || selected){
			g.setColor(ViewStyle.GetColorFromRgb(182,193,214));
		}
		else{
			g.setColor(Color.WHITE);
		}
		g.fill(rect);
							
		// if today
		if(calendar.get(Calendar.YEAR) == m_pToday.get(Calendar.YEAR) && calendar.get(Calendar.MONTH) == m_pToday.get(Calendar.MONTH) && date == m_pToday.get(Calendar.DAY_OF_MONTH)){
			g.setColor(Color.RED);
		}
		else if(date == -99){
			g.setColor(Color.RED);
		}
		// if selected date
		else if(selected){
			g.setColor(Color.BLACK);
		}
		// if mouse date
		else if(rect.contains(m_MousePos)){
			g.setColor(Color.BLACK);
		}
		else{
			g.setColor(Color.LIGHT_GRAY);
		}
		g.draw(rect);
		
		// Paint day
		g.setColor(Color.BLACK);
		// Don't paint 
		if(date != -99){
			Paint.DrawText(g,getFont(),(new Integer(date)).toString(),rect,HorizontalAlignment.Center);
		}
	}
		
	
	public void mouseMoved(MouseEvent e)
	{
		m_MousePos = e.getPoint();
		
		String hittedObject = "-1";
		Rectangle dateRect = null;
		for(int i=0;i<m_pDates.size();i++){
			Object[] data = (Object[])m_pDates.get(i);
			dateRect = (Rectangle)data[0];
			if(dateRect.contains(e.getPoint())){				
				hittedObject = data[1].toString();				
				break;
			}
		}
		
		// redraw old hot item and new hot item
		if(!m_HittedObject.equals(hittedObject)){
			if(m_pHittedDateRect != null && Integer.parseInt(m_HittedObject) != -1){
				PaintDate((Graphics2D)this.getGraphics(),Integer.parseInt(m_HittedObject),m_pHittedDateRect);
			}
			
			m_HittedObject = hittedObject;
			m_pHittedDateRect = dateRect;
			
			if(Integer.parseInt(hittedObject) != -1){
				PaintDate((Graphics2D)this.getGraphics(),Integer.parseInt(hittedObject),dateRect);
			}
		}
	}
	
	public void mouseExited(MouseEvent e)
	{
		m_MousePos = new Point(-1,-1);
	}
	
	public void mouseClicked(MouseEvent e)
	{
		// Find clicked date
		for(int i=0;i<m_pDates.size();i++){
			Object[] data = (Object[])m_pDates.get(i);
			Rectangle dateRect = (Rectangle)data[0];
			if(dateRect.contains(e.getPoint())){
				// Today clicked
				if(((Integer)data[1]).intValue() == -99){
					calendar.setTime(m_pToday.getTime());
				}
				else{
					calendar.set(Calendar.DAY_OF_MONTH,((Integer)data[1]).intValue());
				}
				
				m_pSelectedDate.setTime(calendar.getTime());
				this.repaint();
				RaiseDateChanged();
				return;
			}
		}
	}
	
	public void mousePressed(MouseEvent e){}
	public void mouseReleased(MouseEvent e){}
	public void mouseEntered(MouseEvent e){}		
	public void mouseDragged(MouseEvent e){}
	
	/**
	 * Is called when previous month is clicked.
	 *
	 */
	private void On_m_pPrevMonth_Clicked()
	{
		calendar.add(GregorianCalendar.MONTH,-1);
		m_pYear.setValue(calendar.get(Calendar.YEAR));
		m_pMonth.setSelectedIndex(calendar.get(Calendar.MONTH));
		this.repaint();
	}
	
	/**
	 * Is called when next month is clicked.
	 *
	 */
	private void On_m_pNextMonth_Clicked()
	{
		calendar.add(GregorianCalendar.MONTH,1);
		m_pYear.setValue(calendar.get(Calendar.YEAR));
		m_pMonth.setSelectedIndex(calendar.get(Calendar.MONTH));
		this.repaint();		
	}
	
	private void On_m_pYear_Changed()
	{
		calendar.set(GregorianCalendar.YEAR,(int)m_pYear.getValue());
		this.repaint();	
	}
	
	private void On_m_pMonth_Changed()
	{		
		if(m_pMonth.getSelectedIndex() != calendar.get(Calendar.MONTH)){
			calendar.set(GregorianCalendar.MONTH,m_pMonth.getSelectedIndex());
			repaint();	
		}
	}
	
	
	private void FillMonthNames()
	{
		m_pMonth.getItems().Clear();
		
		java.text.DateFormatSymbols s = new java.text.DateFormatSymbols();
		String[] monthNames = s.getMonths();
		for(String monthName : monthNames){
			m_pMonth.getItems().Add(monthName);
		}
	}
	
		
	/**
	 * Gets value.
	 * @return
	 */
	public Date getValue()
	{
		return calendar.getTime();
	}
	/**
	 * Sets value.
	 * @param value
	 */
	public void setValue(Date value)
	{
		calendar.setTime(value);
		m_pSelectedDate.setTime(value);
		m_pYear.setValue(calendar.get(Calendar.YEAR));
		m_pMonth.setSelectedIndex(calendar.get(Calendar.MONTH));
	}
	
	/**
	 * Gets day name letters. For example Sunday=S,Monaday=M,... .
	 * @return
	 */
	public char[] getDayNameLetters()
	{
		if(m_pDayNameLetters != null){
			return m_pDayNameLetters;
		}
		else{					
			java.text.DateFormatSymbols s = new java.text.DateFormatSymbols();
			String[] weekdays = s.getWeekdays();			
			return new char[]{
				weekdays[2].toUpperCase().charAt(0),
				weekdays[3].toUpperCase().charAt(0),
				weekdays[4].toUpperCase().charAt(0),
				weekdays[5].toUpperCase().charAt(0),
				weekdays[6].toUpperCase().charAt(0),
				weekdays[7].toUpperCase().charAt(0),
				weekdays[1].toUpperCase().charAt(0)
			};
		}
	}
	/**
	 * Sets day name letters. For example Sunday=S,Monaday=M,... .
	 * @param value
	 */
	public void setDayNameLetters(char[] value) throws Exception
	{
		if(value.length != 7){
			throw new Exception("You must specify 7 day name chars, starting from first day of week");
		}
		
		m_pDayNameLetters = value;
	}
	
	
	/**
	 * Adds the specified action listener to receive action events
	 * from this button.
	 * @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 from this button.
	 * @param l the action listener
	 */ 
	public void removeValueChangedListener(ActionListener l) 
	{
		m_pListners.remove(ActionListener.class,l);
	}
	
	/**
	 * Raises DateChanged event.
	 *
	 */
	protected void RaiseDateChanged()
	{
		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,1,""));
			}            
		}
	}
	
}
