package net.sourceforge.ganttproject.delay;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.swing.event.UndoableEditEvent;

import net.sourceforge.ganttproject.GanttTree2;
import net.sourceforge.ganttproject.Mediator;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.TaskManager;
import net.sourceforge.ganttproject.task.TaskNode;
import net.sourceforge.ganttproject.task.event.TaskDependencyEvent;
import net.sourceforge.ganttproject.task.event.TaskHierarchyEvent;
import net.sourceforge.ganttproject.task.event.TaskListener;
import net.sourceforge.ganttproject.task.event.TaskPropertyEvent;
import net.sourceforge.ganttproject.task.event.TaskScheduleEvent;
import net.sourceforge.ganttproject.undo.GPUndoListener;

/**
 * The DelayManager manages delays. It has all DelayObservers and notify each of
 * them when a delay has be calculated.
 * 
 * @author bbaranne
 * 
 */
public class DelayManager implements TaskListener, GPUndoListener {

    private static boolean ourCriticProcess = false;

    private Date myToday;

    private List myObservers;

    private TaskManager myTaskManager;

    private TaskNode myRoot;
	
	private GanttTree2 myTree;

    public DelayManager(TaskManager taskManager, GanttTree2 tree) {
        myObservers = new ArrayList();
        myTaskManager = taskManager;
        myRoot = (TaskNode) tree.getRoot();
		myTree = tree;
        Mediator.getUndoManager().addUndoableEditListener(this);
    }

    public void addObserver(DelayObserver observer) {
        myObservers.add(observer);
    }

    public void removeObserver(DelayObserver observer) {
        myObservers.remove(observer);
    }

    public void fireDelayObservation() {
        // System.err.println("fireDelayObservation");
        myToday = new Date();
        if (ourCriticProcess) {
			myTaskManager.processCriticalPath(myRoot);
			ArrayList projectTasks = myTree.getProjectTasks();
	        if (projectTasks.size() != 0)
				for (int i = 0 ; i < projectTasks.size() ; i++)
					myTaskManager.processCriticalPath((TaskNode) projectTasks.get(i));
            
            ourCriticProcess = false;
//            System.out.println("critical path processed");
        }
        Iterator itTasks = Arrays.asList(myTaskManager.getTasks()).iterator();
        while (itTasks.hasNext()) {
            Task task = (Task) itTasks.next();
            Delay delay = calculateDelay(task);
            Iterator itObservers = myObservers.iterator();
            while (itObservers.hasNext()) {
                DelayObserver observer = (DelayObserver) itObservers.next();
                observer.setDelay(task, delay);
//                System.out.println("delay " + delay.getType() + " (critical = "+delay.CRITICAL+")");
            }
        }
    }

    /**
     * The delay is calculated as follow : The reference date is today. The
     * check is performed on the end date of the task. There could be a delay
     * only if percentage completion is not equal to 100%. If task end date <
     * today && completion < 100 there is a delay. The delay is critical is the
     * task is critical.
     * 
     * @param t
     *            The task.
     * @return The calculated delay
     */
    private Delay calculateDelay(Task t) {
        Delay res = Delay.getDelay(Delay.NONE);
        int completionPercentage = t.getCompletionPercentage();
        if (t.isMilestone() || completionPercentage == 100)
            return res;
        Date endDate = t.getEnd().getTime();
        if (endDate.before(myToday))
            if (t.isCritical())
                res.setType(Delay.CRITICAL);
            else
                res.setType(Delay.NORMAL);
        return res;
    }

    public void taskScheduleChanged(TaskScheduleEvent e) {
        if (!e.getNewFinishDate().equals(e.getOldFinishDate())) {
            // ourCriticProcess = false;
            fireDelayObservation();
        }
    }

    public void dependencyAdded(TaskDependencyEvent e) {
        // ourCriticProcess = true;
        fireDelayObservation();
    }

    public void dependencyRemoved(TaskDependencyEvent e) {
        // ourCriticProcess = true;
        fireDelayObservation();
    }

    public void taskAdded(TaskHierarchyEvent e) {
        // ourCriticProcess = true;
        fireDelayObservation();
    }

    public void taskRemoved(TaskHierarchyEvent e) {
        // ourCriticProcess = true;
        fireDelayObservation();
    }

    public void taskMoved(TaskHierarchyEvent e) {

    }

    public void taskPropertiesChanged(TaskPropertyEvent e) {

    }

    public void taskProgressChanged(TaskPropertyEvent e) {
        fireDelayObservation();
    }

    public void undoOrRedoHappened() {
//        System.out.println("undoOrRedoHappened");
        ourCriticProcess = true;
        fireDelayObservation();
        
    }

    public void undoableEditHappened(UndoableEditEvent arg0) {
        // TODO Auto-generated method stub
    }

}
