package net.sourceforge.ganttproject.gui.taskproperties;

import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;

import net.sourceforge.ganttproject.GanttCalendar;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.gui.options.model.DefaultDateOption;
import net.sourceforge.ganttproject.gui.options.model.DefaultStringOption;
import net.sourceforge.ganttproject.gui.options.model.ValidationException;
import net.sourceforge.ganttproject.gui.options.model.WritableStateHint;
import net.sourceforge.ganttproject.task.DurationParsingException;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.TaskLength;
import net.sourceforge.ganttproject.task.TaskMutator;
import net.sourceforge.ganttproject.task.TaskTimeInterval;
import net.sourceforge.ganttproject.time.gregorian.GPTimeUnitStack;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;

abstract class TaskScheduleFields {
    final EndDateOption myEndDateOption = new EndDateOption();
    final StartDateOption myStartDateOption = new StartDateOption();
    final DurationOption myDurationOption = new DurationOption();
    
    protected abstract IGanttProject getProject(); 
    protected abstract Task getTask();
    protected abstract void apply();
    protected abstract TaskTimeInterval getTimeInterval();
        
    class StartDateOption extends DefaultDateOption {
        public StartDateOption() {
            super("taskProperties.main.start", getTask().getStart().getTime());
            addHint(WritableStateHint.class, new WritableStateHint() {
                public boolean isWritable() {
                    return StartDateOption.this.isWritable();
                }

                public void setWritable(boolean isWritable) {
                    StartDateOption.this.setWritable(isWritable);
                    if (!isWritable) {
                        onLockingStartDate();
                    }
                }

                public IStatus trySetWritable(boolean isWritable) {
                    return false==isWritable ? Status.OK_STATUS : Status.CANCEL_STATUS;
                }
            });
        }
        @Override
        public void commit() {
            super.commit();
            getTimeInterval().setStartDate(getValue());
        }
        @Override
        public void setValue(Date value) {
            if (!myEndDateOption.isWritable() && value.after(myEndDateOption.getValue())) {
                throw new ValidationException();
            }
            super.setValue(value);
            //new Exception("setting start value="+value).printStackTrace();
            onChangingStartDate();
        }
        void reloadValue() {
            super.setValue(getTask().getStart().getTime(), true);
        }
        @Override
        public DateFormat getTimeFormat() {
            return getProject().getTimeUnitStack().getTimeFormat();
        }
        @Override
        public DateFormat getDateFormat() {
            DateFormat[] formats = getProject().getTimeUnitStack().getDateFormats();
            return formats.length>0 ? formats[formats.length-1] : super.getDateFormat();
        }
        
    };
    class EndDateOption extends DefaultDateOption {
        public EndDateOption() {
            super("taskProperties.main.end", getTask().getEnd().getTime());
            addHint(WritableStateHint.class, new WritableStateHint() {
                public boolean isWritable() {
                    return EndDateOption.this.isWritable();
                }

                public void setWritable(boolean isWritable) {
                    EndDateOption.this.setWritable(isWritable);
                    if (!isWritable) {
                        onLockingEndDate();
                    }
                }

                public IStatus trySetWritable(boolean isWritable) {
                    return false==isWritable ? Status.OK_STATUS : Status.CANCEL_STATUS;
                }
            });
        }
        @Override
        public void commit() {
            super.commit();
            getTimeInterval().setEndDate(getValue());
        }
        public void setValue(Date value) {
            //new Exception("setting end value="+value).printStackTrace();
            super.setValue(value);
            onChangingEndDate();
        }
        void reloadValue() {
            //new Exception("reloading end value="+getTask().getEnd().getTime()).printStackTrace();
            super.setValue(getTask().getEnd().getTime(), true);
        }
        @Override
        public DateFormat getTimeFormat() {
            return getProject().getTimeUnitStack().getTimeFormat();
        }
        @Override
        public DateFormat getDateFormat() {
            DateFormat[] formats = getProject().getTimeUnitStack().getDateFormats();
            return formats.length>0 ? formats[formats.length-1] : super.getDateFormat();
        }
    };
    class DurationOption extends DefaultStringOption {
        public DurationOption() {
            super("taskProperties.main.duration", getTask().getManager().encode(getTask().getDuration()));
            addHint(WritableStateHint.class, new WritableStateHint() {
                public boolean isWritable() {
                    return DurationOption.this.isWritable();
                }

                public void setWritable(boolean isWritable) {
                    DurationOption.this.setWritable(isWritable);
                    if (!isWritable) {
                        onLockingDuration();
                    }
                }

                public IStatus trySetWritable(boolean isWritable) {
                    return false==isWritable ? Status.OK_STATUS : Status.CANCEL_STATUS;
                }
            });
        }
        @Override
        public void commit() {
            super.commit();
            try {
                TaskLength taskLength = getTask().getManager().createLength(getValue());
                getTimeInterval().setDuration(taskLength.getTimeUnit(), taskLength.getLength());
            }
            catch (RuntimeException e) {
                
            }
        }
        public void setValue(String value) {
            try {
                TaskLength taskLength = getTask().getManager().createLength(value);
                if (taskLength==null) {
                    throw new ValidationException();
                }
            }
            catch(DurationParsingException e) {
                throw new ValidationException(e);
            }
            super.setValue(value);
            onChangingDuration();
        }
        public void reloadValue() {
            setValue(getTask().getManager().encode(getTask().getDuration()), true);
        }
        
    }

    private void onLockingDuration() {
        myStartDateOption.setWritable(true);
        myEndDateOption.setWritable(true);
        apply();
    }

    private void onLockingStartDate() {
        myEndDateOption.setWritable(true);
        myDurationOption.setWritable(true);
        apply();
    }
    private void onLockingEndDate() {
        myStartDateOption.setWritable(true);
        myDurationOption.setWritable(true);
        apply();
    }
    
    private void onChangingStartDate() {
        apply();
        myEndDateOption.reloadValue();
        myDurationOption.reloadValue();
    }
    private void onChangingEndDate() {
        apply();
        myStartDateOption.reloadValue();
        myDurationOption.reloadValue();
    }
    public void onChangingDuration() {
        apply();
        myStartDateOption.reloadValue();
        myEndDateOption.reloadValue();        
    }

    
}
