package net.sourceforge.ganttproject.gui.projectwizard;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.SwingConstants;

import org.jdesktop.swingx.calendar.DatePickerFormatter;


import net.sourceforge.ganttproject.calendar.GPCalendar;
import net.sourceforge.ganttproject.gui.options.OptionsPageBuilder;
import net.sourceforge.ganttproject.gui.options.SpringUtilities;
import net.sourceforge.ganttproject.gui.options.model.ChangeValueEvent;
import net.sourceforge.ganttproject.gui.options.model.ChangeValueListener;
import net.sourceforge.ganttproject.gui.options.model.DefaultDateOption;
import net.sourceforge.ganttproject.gui.options.model.DefaultEnumerationOption;
import net.sourceforge.ganttproject.gui.options.model.DefaultStringOption;
import net.sourceforge.ganttproject.gui.options.model.EnumerationOption;
import net.sourceforge.ganttproject.gui.options.model.GPOption;
import net.sourceforge.ganttproject.gui.options.model.GPOptionGroup;
import net.sourceforge.ganttproject.gui.options.model.ValidationException;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.task.TaskManager;
import net.sourceforge.ganttproject.time.TimeUnit;
import net.sourceforge.ganttproject.time.TimeUnitStack;
import net.sourceforge.ganttproject.time.gregorian.GPTimeUnitStack;

public class TimeUnitsPage implements WizardPage, ChangeValueListener {
    private final GPTimeUnitStack myTimeUnitStack;
    private GPOptionGroup myUnitRangeGroup;
    private TimeUnitRangeOption myUnitOption1;
    //private TimeUnitRangeOption myUnitOption2;
    private TimeUnit myHighestUnit;
    private TimeUnit myLowestUnit;
    private JComponent myBusinessHoursComponent;
    private final GPCalendar myCalendar;
    private GPOptionGroup myBusinessHoursGroup;
    private DefaultDateOption myBusinessDayStartOption;
    private DefaultDateOption myBusinessDayEndOption;

    TimeUnitsPage(TimeUnitStack timeUnitStack, GPCalendar calendar) {
        myTimeUnitStack = (GPTimeUnitStack) timeUnitStack;
        myCalendar = calendar;
    }
    public Component getComponent() {
        myUnitOption1 = new TimeUnitRangeOption("projectwizard.timeUnitFrom");
        //myUnitOption2 = new TimeUnitRangeOption("projectwizard.timeUnitTo");
        myUnitOption1.addChangeValueListener(this);
        //myUnitOption2.addChangeValueListener(this);
        myUnitRangeGroup = new GPOptionGroup("projectwizard.timeUnitRange", new GPOption[] {myUnitOption1});
        //
        OptionsPageBuilder builder = new OptionsPageBuilder();
        JComponent timeunitRangeComponent = builder.buildPlanePage(new GPOptionGroup[] {myUnitRangeGroup});
        myBusinessDayStartOption = new DefaultDateOption("projectwizard.dayStart", GanttLanguage.getInstance().getBusinessHoursStart()) {
            public DateFormat getDateFormat() {
                return null;
            }
            public DateFormat getTimeFormat() {
                return GanttLanguage.getInstance().getTimeFormat();
            }
        };
        myBusinessDayEndOption = new DefaultDateOption("projectwizard.dayEnd", GanttLanguage.getInstance().getBusinessHoursEnd()) {
            public DateFormat getDateFormat() {
                return null;
            }
            public DateFormat getTimeFormat() {
                return GanttLanguage.getInstance().getTimeFormat();
            }
            
        };
        myBusinessHoursGroup = new GPOptionGroup("projectwizard.businessHours", new GPOption[] {myBusinessDayStartOption, myBusinessDayEndOption}); 
        myBusinessHoursComponent = builder.buildPlanePage(new GPOptionGroup[] {
                myBusinessHoursGroup
        });
        
        Box box = Box.createVerticalBox();
        box.add(timeunitRangeComponent);
        box.add(Box.createVerticalStrut(5));
        box.add(Box.createVerticalGlue());
        box.add(myBusinessHoursComponent);
        
        JPanel result = new JPanel(new BorderLayout());
        result.add(box, BorderLayout.NORTH);
        updateBusinessHours();
        
        return result;
    }

    public String getTitle() {
        return "Configure time granularity";
    }

    public void setActive(boolean isActive) {
        if (isActive) {
            myUnitRangeGroup.lock();
            myBusinessHoursGroup.lock();
        }
        else {
            myUnitRangeGroup.commit();
            myBusinessHoursGroup.commit();
            myTimeUnitStack.setTimeUnitRange(myLowestUnit, myHighestUnit);
            if (myTimeUnitStack.HOUR.isConstructedFrom(myLowestUnit)) {
                myCalendar.setBusinessHours(
                        myBusinessDayStartOption.getValue(), 
                        myBusinessDayEndOption.getValue());
            }
            else {
                myCalendar.setBusinessHours(null, null);
            }
            DatePickerFormatter.setDateFormats(myTimeUnitStack.getDateFormats());
        }
    }

    private class TimeUnitRangeOption extends DefaultEnumerationOption {
        public TimeUnitRangeOption(String id) {
            super(id, getTimeUnitIDList());
            setValue(getTimeUnitIDList().get(0));
        }

        public String getUncommitedValue() {
            return super.getLockedValue();
        }
        
    }

    private List<String> getTimeUnitIDList() {
        List<TimeUnit> timeUnits = myTimeUnitStack.getTimeUnits();
        List<String> ids = new ArrayList<String>(timeUnits.size());
        for (TimeUnit nextUnit : timeUnits) {
            ids.add(nextUnit.getName());
        }
        return ids;
    }
    
    private void updateBusinessHours() {
        String unitName1 = myUnitOption1.getUncommitedValue();
        //String unitName2 = myUnitOption2.getUncommitedValue();
        if (unitName1==null) {
            return;
        }
        myLowestUnit = null;
        myHighestUnit = null;
        for (TimeUnit nextUnit: myTimeUnitStack.getTimeUnits()) {
            if (nextUnit.getName().equals(unitName1)) {
                if (myLowestUnit==null) {
                    myLowestUnit = nextUnit;
                }
                else if (myHighestUnit==null) {
                    myHighestUnit = nextUnit;
                }
            }
        }
        if (myTimeUnitStack instanceof GPTimeUnitStack) {
            GPTimeUnitStack gpStack = (GPTimeUnitStack) myTimeUnitStack;
            if (gpStack.HOUR.isConstructedFrom(myLowestUnit)) {
                setBusinessHoursEnabled(true);
            }
            else {
                setBusinessHoursEnabled(false);
            }
        }
    }
    private void setBusinessHoursEnabled(boolean b) {
        setEnabledTree(myBusinessHoursComponent, b);
    }
    public void changeValue(ChangeValueEvent event) {
        updateBusinessHours();
    }
    
    private void setEnabledTree(JComponent root, boolean isEnabled) {
        root.setEnabled(isEnabled);
        Component[] components = root.getComponents();
        for (int i = 0; i < components.length; i++) {
            if (components[i] instanceof JComponent) {
                setEnabledTree((JComponent) components[i], isEnabled);
            }
        }
    }
    
}
