/* Copyright (c) 2008 Charles Rich and Worcester Polytechnic Institute.
 * All Rights Reserved.  Use is subject to license terms.  See the file
 * "license.terms" for information on usage and redistribution of this
 * file and for a DISCLAIMER OF ALL WARRANTIES.
 */
package edu.wpi.cetask;

import org.w3c.dom.*;
import javax.script.*;
import java.util.*;

public class TaskClass extends TaskModel.Member {
   
   // TODO check for duplicate slot names
   
   final String precondition, postcondition; 
   final CompiledScript compiledPrecondition, compiledPostcondition;

   final private List<String> inputNames, outputNames, declaredInputNames, declaredOutputNames;
   
   // will not retrieve init scripts, since schema doesn't allow task with init
   final List<Taskable> scripts = Collections.unmodifiableList(
         getDescriptions("script"));
   
   TaskClass (Node node, TaskModel model) { 
      model.super(node);
      String pre = xpath("./precondition"), post = xpath("./postcondition");
      if ( TaskEngine.COMPILABLE ) {
         compiledPrecondition = engine.compile(pre, this+" precondition");
         compiledPostcondition = engine.compile(post, this+" postcondition");
         precondition = postcondition = null;
      } else {
         precondition = pre; postcondition = post;
         compiledPrecondition = compiledPostcondition = null;
      }
      declaredInputNames = xpathValues("./input/@name");
      inputNames = new ArrayList<String>(declaredInputNames);
      inputNames.add("external");
      declaredOutputNames = xpathValues("./output/@name");
      outputNames =  new ArrayList<String>(declaredOutputNames);
      outputNames.add("success"); outputNames.add("when");
   }
  
   public Task newInstance () { return new Task(this, engine); }
   
   /**
    * Test whether this task class is primitive (relative to models in current
    * task engine). A task class is primitive iff there are no known
    * decomposition types with this task as goal.
    */
   public boolean isPrimitive () {
      return getDecompositions().isEmpty();
   }
   
   /**
    * Return list of input slot names, including "external".  Note "device"
    * has predefined meaning, but is not automatically an input to every task.
    * 
    * @see #getDeclaredInputNames()
    */
   public List<String> getInputNames () { return inputNames; }
   
   /**
    * Return list of output slot names, including "success" and "when".
    * 
    * @see #getDeclaredOutputNames()
    */
   public List<String> getOutputNames () { return outputNames; } 

   /**
    * Return list of declared input slot names.
    * 
    * @see #getInputNames()
    */
   public List<String> getDeclaredInputNames () { return declaredInputNames; }
      
   /**
    * Return list of declared output slot names.
    * 
    * @see #getInputNames()
    */
   public List<String> getDeclaredOutputNames () { return declaredOutputNames; } 

   public String getSlotType (String name) {
      if ( name.equals("success") || name.equals("external") )
         return "boolean";
      else if ( name.equals("when") ) return "Date";
      String path = "[@name=\""+name+"\"]/@type";
      String type = xpath("./input"+path+" | "+"./output"+path);
      if ( type.length() == 0 ) 
         throw new IllegalArgumentException(name+" is not slot of "+this);
      return type;
   }
   
   public List<DecompositionClass> getDecompositions () {
      return getDescriptions("subtasks");
   }
   
   /**
    * Return decomposition class with given id for this task class or null if none.
    */
   public DecompositionClass getDecomposition (String id) {
      for (DecompositionClass decomp : getDecompositions())
         if ( decomp.getId().equals(id) ) return decomp;
      return null;
   }
 
   @Override
   public String toString () {
      String id = getId();
      try { 
         // for readability, suppress namespace for unambiguous id's
         engine.getTaskClass(id);
         return id;
      } catch (TaskEngine.AmbiguousIdException e) { return getNamespace()+'#'+id; }
   }

   @SuppressWarnings("unchecked")
   private <T extends Description.Taskable> 
             List<T> getDescriptions (String element) {
      List<T> descriptions = new ArrayList<T>();
      String path = "./"+element;
      List<Node> nodes = engine.xpathAll(path);
      for (Node node : nodes) {
         Description.Taskable description = newTaskable(element, node, null);
         if ( equals(description.getTask()) ) descriptions.add((T) description);
      }
      // add nested forms 
      for (Node node : xpathNodes(path)) 
         descriptions.add((T) newTaskable(element, node, this));
      return descriptions;
   }
}

