package com.ht.ui.controls.calendar;

import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import com.ht.util.DateType;

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.ToolBar;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;

public class FXCalendar extends VBox {

    private SimpleIntegerProperty selectedDate = new SimpleIntegerProperty();
    private SimpleIntegerProperty selectedMonth = new SimpleIntegerProperty();
    private SimpleIntegerProperty selectedYear = new SimpleIntegerProperty();
    private SimpleBooleanProperty triggered = new SimpleBooleanProperty();
    private final SimpleObjectProperty<Color> baseColor = new SimpleObjectProperty<Color>();
    private SimpleDoubleProperty dateTextWidth = new SimpleDoubleProperty(74);
    private SimpleObjectProperty<Date> value = new SimpleObjectProperty<Date>();
    private boolean showWeekNumber;
    private FXCalendarUtility fxCalendarUtility;
    private DateTypeButton dateTypeButton;
    private ChangeListener<Boolean> focusOutListener;
    private DatePicker datePicker;
    private final SimpleObjectProperty<Locale> locale = new SimpleObjectProperty<Locale>();
    private final String DEFAULT_STYLE_CLASS = "fx-calendar";
    private Runnable runnable;
    private boolean showDateType = true;

    public FXCalendar() {
        this(Color.web("#313131"), true);
    }
    
    public FXCalendar(boolean showDateType) {
    	
        this(Color.web("#313131"), showDateType);
    }
    
    public FXCalendar(Color baseColor) {
    	
        this(baseColor, true);
    }
    
    public FXCalendar(Color baseColor, boolean showDateType) {
        super();
        this.showDateType = showDateType;
        FXCalendarUtility.setBaseColorToNode(this, baseColor);
        super.getStyleClass().add(DEFAULT_STYLE_CLASS);
        this.locale.set(Locale.SIMPLIFIED_CHINESE);
        this.baseColor.set(baseColor);
        setAlignment(Pos.CENTER_LEFT);
        configureCalendar();
        configureListeners();
    }
    
    public void setOnDateSelected(Runnable run) {
    	this.runnable = run;
    }
    
    public Runnable getRunnable() {
		return runnable;
	}
    
    private void configureCalendar() {
        this.setValue(new Date());
        fxCalendarUtility = new FXCalendarUtility();

        addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {

            public void handle(KeyEvent event) {
                if (KeyCode.UP.equals(event.getCode()) || KeyCode.DOWN.equals(event.getCode()) || KeyCode.ENTER.equals(event.getCode())) {
//                    initiatePopUp();
//                    showPopup();
                } else if (KeyCode.TAB.equals(event.getCode())) {
//                    hidePopup();
                }
            }
        });

        dateTypeButton = new DateTypeButton(this);
        datePicker = new DatePicker(this);
        
        if (showDateType) {
        	getChildren().addAll(dateTypeButton, datePicker);
        } else {
        	getChildren().add(datePicker);
        }
    }

    private void configureListeners() {

        /*
         * Adding listeners when the date cell is selected.
         */
        triggeredProperty().addListener(new ChangeListener<Boolean>() {

            @Override
            public void changed(ObservableValue<? extends Boolean> paramObservableValue, Boolean paramT1, Boolean paramT2) {
                if (paramT2) {
                    FXCalendarUtility cu = new FXCalendarUtility();
                    final Integer day = selectedDateProperty().get();
                    final Integer month = selectedMonthProperty().get();
                    final Integer year = selectedYearProperty().get();
                    if (day != 0 && month != 0 && year != 0) {
                        String d = cu.getFormattedDate(day, month, year);
                        valueProperty().set(cu.convertStringtoDate(d));
                    }
                    setTriggered(false);
                }
            }
        });

        /*
         * Changes to be done in text box on change of seletedDate ,
         * selectedMonth and selectedYear in DatePicker.
         */
        ChangeListener<Object> listener = new ChangeListener<Object>() {

            @Override
            public void changed(ObservableValue<? extends Object> arg0, Object arg1, Object arg2) {
                updateDateValue();
            }
        };

        selectedDateProperty().addListener(listener);
        selectedMonthProperty().addListener(listener);
        selectedYearProperty().addListener(listener);
        updateDateValue();

        /*
         * Adding change listeners for locale.
         */
        ChangeListener<Locale> localeListener = new ChangeListener<Locale>() {

            @Override
            public void changed(ObservableValue<? extends Locale> arg0, Locale arg1, Locale arg2) {
                if (datePicker != null) {
                    refreshLocale(arg2);
                }
            }
        };
        localeProperty().addListener(localeListener);

        /*
         * Adding listeners for styles.
         */
        getStyleClass().addListener(new ListChangeListener<String>() {

            @Override
            public void onChanged(javafx.collections.ListChangeListener.Change<? extends String> paramChange) {
//				dateTxtField.getStyleClass().clear();
//				dateTxtField.getStyleClass().addAll("text-input", "text-field");
//				for (String clazz : getStyleClass()) {
//					if (!clazz.equals(DEFAULT_STYLE_CLASS)) {
//						dateTxtField.getStyleClass().add(clazz);
//					}
//				}
            }
        });
    }

    public void updateDateValue() {
        int date = selectedDate.get();
        int month = selectedMonth.get();
        int year = selectedYear.get();

        switch (dateTypeButton.getDateType()) {
            case YEARLY:
                if (year != 0) {
                    Calendar cal = Calendar.getInstance();
                    cal.set(Calendar.YEAR, year);
                    this.setValue(cal.getTime());
                } 
                break;
            case MONTHLY:
                if (month != -1 && year != 0) {
                    Calendar cal = Calendar.getInstance();
                    cal.set(Calendar.YEAR, year);
                    cal.set(Calendar.MONTH, month);
                    this.setValue(cal.getTime());
                }
                break;
            case DAILY:
                if (date != 0 && month != -1 && year != 0) {
                    this.setValue(FXCalendarUtility.getDate(date, month, year).getTime());
                }
                break;
            default:
                throw new AssertionError();
        }

    }

    public void refreshLocale(Locale locale) {
        fxCalendarUtility.resetShortestWeekDays(locale);
        fxCalendarUtility.resetShortMonths(locale);
        fxCalendarUtility.resetMonths(locale);
        datePicker.getBasePane().setLabelText();
        datePicker.getBasePane().setWeekLabels();
        datePicker.getTopPane().setTopMonths();
    }

    /**
     * @return the baseColor
     */
    public Color getBaseColor() {
        return baseColor.get();
    }

    /**
     * @return baseColor Property
     */
    public SimpleObjectProperty<Color> baseColorProperty() {
        return baseColor;
    }

    /**
     * @return the locale
     */
    public Locale getLocale() {
        return locale.get();
    }

    /**
     * @param locale the locale to set
     */
    public void setLocale(Locale locale) {
        this.locale.set(locale);
    }

    /**
     * @return locale Property
     */
    public SimpleObjectProperty<Locale> localeProperty() {
        return locale;
    }

    /**
     * @return the dateTextWidth
     */
    public Double getDateTextWidth() {
        return dateTextWidth.get();
    }

    /**
     * @param dateTextWidth the dateTextWidth to set
     */
    public void setDateTextWidth(Double width) {
        this.dateTextWidth.set(width);
    }

    /**
     * @return dateTextWidth Property
     */
    public SimpleDoubleProperty dateTextWidthProperty() {
        return dateTextWidth;
    }

    public int getSelectedDate() {
        return selectedDate.get();
    }

    public int getSelectedMonth() {
        return selectedMonth.get();
    }

    public int getSelectedYear() {
        return selectedYear.get();
    }

    public void setSelectedDate(int selectedDate) {
        this.selectedDate.set(selectedDate);
    }

    public void setSelectedMonth(int selectedMonth) {
        this.selectedMonth.set(selectedMonth);
    }

    public void setSelectedYear(int selectedYear) {
        this.selectedYear.set(selectedYear);
    }

    public SimpleIntegerProperty selectedDateProperty() {
        return selectedDate;
    }

    public SimpleIntegerProperty selectedMonthProperty() {
        return selectedMonth;
    }

    public SimpleIntegerProperty selectedYearProperty() {
        return selectedYear;
    }

    public boolean getShowWeekNumber() {
        return showWeekNumber;
    }

    public void setShowWeekNumber(boolean showWeekNumber) {
        this.showWeekNumber = showWeekNumber;
    }

    /**
     * @return the value
     */
    public Date getValue() {
        return this.value.get();
    }

    /**
     * @param value the value to set
     */
    public void setValue(Date date) {
        this.value.set(date);
        if (date != null) {
            Calendar c = FXCalendarUtility.getDateCalendar(date);
            selectedDateProperty().set(c.get(Calendar.DAY_OF_MONTH));
            selectedMonthProperty().set(c.get(Calendar.MONTH));
            selectedYearProperty().set(c.get(Calendar.YEAR));
        } else {
            selectedDateProperty().set(0);
            selectedMonthProperty().set(0);
            selectedYearProperty().set(0);
        }

        if (datePicker != null) {
            datePicker.refreshDate();
        }
    }

    /**
     * Method to clear the value in the calendar.
     */
    public void clear() {
        setValue(null);
    }

    public SimpleObjectProperty<Date> valueProperty() {
        return value;
    }

    public FXCalendarUtility getFXCalendarUtility() {
        return fxCalendarUtility;
    }

    public void setTriggered(Boolean triggered) {
        this.triggered.set(triggered);
    }

    public SimpleBooleanProperty triggeredProperty() {
        return triggered;
    }

    public DateTypeButton getDateTypeButton() {
        return dateTypeButton;
    }

//	public TextField getTextField() {
//		return dateTxtField;
//	}
    public ChangeListener<Boolean> getFocusOutListener() {
        return this.focusOutListener;
    }

    /**
     * Cell Inteface
     *
     * @author Sai.Dandem
     *
     * @param <ItemType>
     */
    public static interface Cell {

        Node getNode();

        void updateItem(String item);
    }

    public static class DateTypeButton extends HBox {

        private DateType dateType = DateType.DAILY;

        public DateTypeButton(final FXCalendar fxCalendar) {
            setId("dateButton");
            setStyle("-fx-base:" + FXCalendarUtility.rgbToHex(fxCalendar.getBaseColor()) + ";");

            setTranslateX(0);
            setPrefWidth(201);
            setMaxWidth(201);
//            setPrefHeight(30);

            final ToggleButton tb1 = new ToggleButton("按年");
            final ToggleButton tb2 = new ToggleButton("按月");
            final ToggleButton tb3 = new ToggleButton("按日");
            tb1.setPrefWidth(200/3);
            tb2.setPrefWidth(200/3);
            tb3.setPrefWidth(200/3 + 1);
            
            HBox.setHgrow(tb1, Priority.ALWAYS);
            HBox.setHgrow(tb2, Priority.ALWAYS);
            HBox.setHgrow(tb3, Priority.ALWAYS);

            final ToggleGroup group = new ToggleGroup();
            tb1.setToggleGroup(group);
            tb2.setToggleGroup(group);
            tb3.setToggleGroup(group);
            // select the first button to start with
            group.selectToggle(tb3);

            group.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {

                @Override
                public void changed(ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle selectedToggle) {
                    if (selectedToggle != null) {
                        if (selectedToggle.equals(tb1)) {
                            dateType = DateType.YEARLY;
                            fxCalendar.datePicker.showTopPane();
                        } else if (selectedToggle.equals(tb2)) {
                            dateType = DateType.MONTHLY;
                            fxCalendar.datePicker.showTopPane();
                        } else {
                            dateType = DateType.DAILY;
                            fxCalendar.datePicker.showBasePane();
                        }
                    } else {
                        group.selectToggle(oldValue);
                    }
                }
            });


//            Region spacer1 = new Region();
//            HBox.setHgrow(spacer1, Priority.ALWAYS);
//            
//            Region spacer2 = new Region();
//            HBox.setHgrow(spacer2, Priority.ALWAYS);
//            
//            getChildren().addAll(tb1, spacer1, tb2, spacer2, tb3);
            getChildren().addAll(tb1, tb2, tb3);
            //getChildren().addAll(tb1, tb2, tb3);
        }

        public DateType getDateType() {
            return dateType;
        }
    }
}
