/*
 * Copyright (c) 2005, Stanford University. All rights reserved. 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions
 * are met: 
 *  - Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 *  - Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution. 
 *  - Neither the name of the Stanford University nor the names of its 
 *    contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission. 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE. 
 */

/*
 * Created on Sep 14, 2005
 * Original author: Christopher Bruns
 */
package org.simtk.isimsu;

import java.util.*;

/**
 *  
  * @author Christopher Bruns
  * 
  * Manage Dependencies between tasks that must be completed in order.
  * In Model-View-Controller terms, this is a Model, Viewed by ISIMStepRow
  * Hopefully this class will be more general than just ISIM
 */
public class DependentTask extends Observable implements Observer
    // implements ChangeListener 
{
    // public ChangeBroadcaster changeBroadcaster = new ChangeBroadcaster();

    HashSet prerequisites = new HashSet();
    private TaskStatus mTaskStatus = STATUS_UNKNOWN;
    long lastUpdateStartTime = 0L;
    long lastUpdateEndTime = 0L;
    private String statusDescription = "";

    static TaskStatus FAILED = new TaskStatus();
    static TaskStatus CURRENT_AND_COMPLETE = new TaskStatus();
    static TaskStatus ACTIVELY_UPDATING = new TaskStatus();
    static TaskStatus READY_FOR_UPDATE = new TaskStatus();
    static TaskStatus WAITING_FOR_PREREQUISITES = new TaskStatus();
    static TaskStatus STATUS_UNKNOWN = new TaskStatus();

    public boolean isRunning() {
        // Don't do anything if it appears that someone else is already doing this task
        if ( DependentTask.ACTIVELY_UPDATING == this.getStatus() )
                return true;
        return false;
    }
    
    /**
     * Are all prerequisite tasks complete?
     * @return
     */
    private void updateStatus() {        
        TaskStatus startStatus = mTaskStatus;
        
        if ( (CURRENT_AND_COMPLETE != mTaskStatus)
             && (ACTIVELY_UPDATING != mTaskStatus) )
            mTaskStatus = READY_FOR_UPDATE; // start optimistic
        
        // Check prerequisites to see if we are WAITING_FOR_PREREQUISITES
        long newestPrerequisite = 0l;
        for (Iterator i = prerequisites.iterator(); i.hasNext();) {
            DependentTask prerequisite = (DependentTask) i.next();
            if ( prerequisite.getStatus() != CURRENT_AND_COMPLETE ) {
                mTaskStatus = WAITING_FOR_PREREQUISITES;
            }
            if (prerequisite.lastUpdateEndTime > newestPrerequisite) 
                newestPrerequisite = prerequisite.lastUpdateEndTime;
        }

        // Are there newer prerequisites?
        if (  (lastUpdateStartTime < newestPrerequisite)
           && (WAITING_FOR_PREREQUISITES != mTaskStatus) ) {
            mTaskStatus = READY_FOR_UPDATE;
        }
        
        if (mTaskStatus != startStatus) {
            setChanged();
            notifyObservers();
        }
    }
    
    public TaskStatus getStatus() {
        updateStatus();
        return mTaskStatus;
    }

    public void setStatus(TaskStatus status, String statusDescription) {
        // TaskStatus startStatus = mTaskStatus;

        this.statusDescription = statusDescription;
        
        mTaskStatus = status;

        // Just finished an update
        if (CURRENT_AND_COMPLETE == status)
            lastUpdateEndTime = (new Date()).getTime();

        // Just started an update
        if (ACTIVELY_UPDATING == status)
            lastUpdateStartTime = (new Date()).getTime();
        
        // Make sure proposed change is still current
        updateStatus();

        setChanged();
        notifyObservers();
    }
    
    public void addPrerequisite(DependentTask prerequisite) {
        // TODO make sure no circular dependencies are created
        prerequisites.add(prerequisite);
        
        // Ask to be informed of changes to prerequisite
        prerequisite.addObserver(this);
    }

    // Information about modified dependencies comes through here
    public void update(Observable observable, Object object) {
        if (observable instanceof DependentTask) {
            DependentTask changedTask = (DependentTask) observable;
            if (prerequisites.contains(changedTask)) {
                updateStatus();
            }
        }
    }
    
    public String getStatusDescription() {return this.statusDescription;}
}

//  Enumeration
class TaskStatus {
    TaskStatus() {}
}

