/* A Task is an activity that requires time and resources to
   complete.  Tasks should only have a single input and output.
   Tasks can connect to other tasks or to Milestones.  When
   a path must split or join, a Milestone is required.  

   Tasks descend from Activity and inherit all of
   their methods and instance variables.
*/!!

inherit(Activity, #Task, #(resources   /* a collection */
time        /* required by task */
cost        /* total task cost */
fixedCost 
), 2, nil)!!

now(TaskClass)!!

now(Task)!!

/* Return the class name.  Uses resources for translation. */
Def className(self)
{
  ^loadString(PW_TASK);
}!!


/* Return true if the task is "complete", e.g.
   connected, time set etc.  
   This is for experimental use only.   */
Def  complete(self)
{ 
  ^complete(self:Activity)
     cand time <> 0
     cand size(resources) > 0;
}!!

/* Draw a Task at a location in a window.
   The window manages the details of what the
   Task will look like, since it could be
   a PERT chart or a Gantt chart.    */
Def  draw(self, window, x, y, hDC)
{ 
  drawTask(window, self, x, y, hDC);
}!!

/* Tasks can have only a single connection at
   inputs and outputs.  If you attempt to connect
   multiple inputs or outputs, an error message
   will be displayed.  */
Def  maxConnectionNames(self, names | connected)
{   
  if size(names) > 1
    beep();
    errorBox(loadString(PW_ERRCONNECT1),
      loadString(PW_ERRCONNECT2) + CR_LF +
      loadString(PW_ERRCONNECT3));
    names := copyFrom(names, 0, 1);
  endif;
  ^names;
}!!

/* Check to see if the resources have changed.
   If so, remove the old ones add the new ones. */
Def  checkResources(self, newResNames | oldResNames, res)
{
  oldResNames := getResourceNames(self);
  
  if newResNames <> oldResNames       /* resources changed */
    do(resources,
      {using(oldRes) 
       removeResource(self, oldRes);
     });
    res := parseResNames(network, newResNames);
    
    do(res,
      {using(newRes)
       addResource(self, newRes);       
    });    
    calcCost(self);   /* update cost */
  endif;  
}!!

/* Return a string of resource names. */
Def  getResourceNames(self | str)
{
  str := "";
  do(resources,
    {using(res) str := str + getName(res) + " ";
  });
  ^str;  
}!!

/* Return the appropriate dialog class to be used
   by editInfo(). */
Def  dialogClass(self)
{
  ^TaskDialog;
}!!

/* Set the values of an activity. 
   Values is an array of name, desc,
   userEarlyStart, userLateFinish, time, 
   fixedCost, resources. */
Def  setValues(self, values | oldUES, oldULF, oldTime)
{
  oldUES := userEarlyStart;
  oldULF := userLateFinish;
  oldTime := time;
  
  name := values[0];
  desc := values[1];
  userEarlyStart := values[2];
  userLateFinish := values[3];
  time := values[4];
  
  setFixedCost(self, values[5]);
  checkResources(self, values[6]);

  if network cand autoCalc(network) cand 
     (oldUES <> userEarlyStart cor
      oldULF <> userLateFinish cor
      oldTime <> time)
    recalc(self);
  endif;
}!!

/* Get the fixed cost. */
Def  getFixedCost(self)
{ ^fixedCost;
}!!

/* Get the cost. */
Def  getCost(self)
{ ^cost;
}!!

/* Calculate the cost of a Task and update the project cost. */
Def  calcCost(self | oldCost)
{ 
  oldCost := cost;
  cost := fixedCost;
  do(resources,
    {using(elem)
    cost := cost + getFixedCost(elem)
                 + getTime(self) * getVariableCost(elem);
  });
  updateCost(network, cost - oldCost);
  ^cost;
}!!

/* Update the cost based on a change to a resource.
   Pass the change in cost to the network also. */
Def  updateCost(self, change)
{ 
  cost := cost + change;
  updateCost(network, change);
}!!

/* Remove the resource for a task.  Also remove
   the resource's reference to the task.  
   Recalculate the cost if necessary. */
Def  removeResource(self, res)
{ 
  remove(resources, res);
  removeReference(res, self);
  if (getVariableCost(res) <> 0 
      cor getFixedCost(res) <> 0)
    calcCost(self);
  endif;
}!!

/* Add to the resources for a task creating the resource is
   necessary.  The resource should also know where it is
   referenced, in case it changes. Update cost if necessary. */
Def  addResource(self, res)
{ 
  add(resources, res);
  addReference(res,self);     
  if (getVariableCost(res) <> 0)
    calcCost(self);
  endif;
}!!

/* Set the fixed cost for a task. */
Def  setFixedCost(self, aCost | change)
{ 
  change := aCost - fixedCost;
  if (change <> 0)
    fixedCost := aCost;
    cost := cost + change;
    updateCost(network, change);
  endif;
}!!

/* Initialize a new Task. */
Def  init(self)
{
  init(self:Activity);      /* use ancestor init */
  resources := new(Set, 5);
  time := slack := cost := fixedCost := 0;
} !!

/* Get the time. */
Def  getTime(self)
{ ^time;
}!!


