
// Copyright (c) 2004
package uk.co.patrickhaston.mars;


/**
 * A Class class.
 * <P>
 * @author Patrick Haston
 */
public class MarsActivity extends MarsData 
{
  private MarsModel theModel;
  // a pointer to the model - it's needed all the time

  private int settlementId;
  // index to the settlement where this activity is taking place
  private int activityType;
  // index to the ActivityType for this activity
  public static int ACTIVITY_NONE = 0;
  public static int ACTIVITY_CONSTRUCTION = 1;
  public static int ACTIVITY_RESEARCH = 2;
  public static int ACTIVITY_SURVEY = 3;
  public static int ACTIVITY_MINING = 4;
  public static int ACTIVITY_INDUSTRY = 5;
  public static int ACTIVITY_MANUFACTURING = 6;
  public static int ACTIVITY_TRANSPORTATION = 7;
  public static int ACTIVITY_SECURITY = 8;
  public static int ACTIVITY_ADMINISTRATION = 9;
  public static int ACTIVITY_COMMERCE = 10;
  public static int ACTIVITY_FARMING = 11;
  public static int ACTIVITY_ENTERTAINMENT = 12;
  public static int ACTIVITY_MAINTENANCE = 13;
  public static int ACTIVITY_ENERGY = 14;
  public static int ACTIVITY_AQUIFER = 15;
  public static int ACTIVITY_AIR_SUPPLY = 16;

  private int status;
  public static int NOT_STARTED = 1;
  public static int IN_PROGRESS = 2;
  public static int PAUSED = 3;
  public static int COMPLETE = 4;

  private int resourceInputs;
  private int resourceOutputs;
  private long workers;
  private int special;
  private long workforce; // modified to take automation into account
  private long workToDo; // days of work to be done
  private int leader;
  private String description;
  
  private int inputSource; // the id of the entity which supplies the input resources
  private int inputSourceType; // the type of entity which is supplying the input resources
  // the types of resource sources
  public static int RESOURCE_NONE = 0;
  public static int RESOURCE_SETTLEMENT = 1;
  public static int RESOURCE_PERSON = 2;
  public static int RESOURCE_WAYPOINT = 3;
  public static int RESOURCE_ATMOSPHERE = 4;
  
  private int outputDestination; // the id of the entity where the output resources end up
  private int outputDestinationType; // the type of the entity where the output resources end up
  // the types of resource destinations are the same as the sources
  
  private int participants[]; // the id's of the people involved in this activity
  private int equipment[]; // the id's of the constructions involved in this activity

  /**
   * Constructor
   */
  public MarsActivity(int id, MarsModel model) 
  {
    super(id);
    theModel = model;
    settlementId = 0;
    activityType = 0;
    status = 0;
    resourceInputs = 0;
    resourceOutputs = 0;
    workers = 0;
    special = 0;
    workforce = 0;
    workToDo = 0;
    leader = 0;
    description = "";
    inputSource = 0;
    inputSourceType = 0;
    outputDestination = 0;
    outputDestinationType = 0;
  }

  public MarsActivity(MarsActivity a) 
  {
    super(a);
    if (a== null)
    {
      theModel = null;
      settlementId = 0;
      activityType = 0;
      status = 0;
      resourceInputs = 0;
      resourceOutputs = 0;
      workers = 0;
      special = 0;
      workforce = 0;
      workToDo = 0;
      leader = 0;
      description = "";
      inputSource = 0;
      inputSourceType = 0;
      outputDestination = 0;
      outputDestinationType = 0;
    }
    else
    {
      theModel = a.theModel;
      settlementId = a.settlementId;
      activityType = a.activityType;
      status = a.status;
      resourceInputs = a.resourceInputs;
      resourceOutputs = a.resourceOutputs;
      workers = a.workers;
      special = a.special;
      workforce = a.workforce;
      workToDo = a.workToDo;
      leader = a.leader;
      description = a.description;
      inputSource = a.inputSource;
      inputSourceType = a.inputSourceType;
      outputDestination = a.outputDestination;
      outputDestinationType = a.outputDestinationType;
    }
  }
  
  MarsActivity(int id, MarsModel model, int settlement, int activity, int stat, int resIn, int resOut, long work, 
              int spec, long workF, long workTD, int lead, String desc,
              int inS, int inST, int outD, int outDT)
  {
    super(id);
    theModel = model;
    settlementId = settlement;
    activityType = activity;
    status = stat;
    resourceInputs = resIn;
    resourceOutputs = resOut;
    workers = work;
    special = spec;
    workforce = workF;
    workToDo = workTD;
    leader = lead;
    description = desc;
    inputSource = inS;
    inputSourceType = inST;
    outputDestination = outD;
    outputDestinationType = outDT;
  }
  
  public MarsActivity(MarsModel model, String line)
  {
    super(line);
    theModel = model;
    settlementId = readInteger(line, 0); // settlementId
    activityType = readInteger(line, 1); // activityType
    status = readInteger(line, 2); // status
    resourceInputs = readInteger(line, 3); // resourceInputs
    resourceOutputs = readInteger(line, 4); // resourceOutputs
    workers = readLong(line, 5); // workers
    special = readInteger(line, 6); // special
    workforce = readLong(line, 7); // workforce
    workToDo = readLong(line, 8); // workToDo
    leader = readInteger(line, 9); // leader
    description = readString(line, 10); // description
    inputSource = readInteger(line, 11); //
    inputSourceType = readInteger(line, 12); //
    outputDestination = readInteger(line, 13); //
    outputDestinationType = readInteger(line, 14); //
  }
  
  /**
   * This is the constructor chosen when a person needs to start a new activity
   * The values for the parameters are mostly selected from the Goals for this settlement
   * @param id - default value of 0 is normally passed from MarsActivities (to be updated later)
   * @param activity - the activity type
   * @param spec - the special value
   * @param lead - the leader of the activity - the person id
   * @param settlement - the id of the settlement
   * @param model - pointer to the model
   */
  public MarsActivity(int id, int activity, int spec, int lead, int settlement, MarsModel model)
  {
    super(id);
    theModel = model;
    settlementId = settlement;
    activityType = activity;
    status = NOT_STARTED;
    // set up the resources that this activity requires and produces
    //System.out.println("define new activity inputs: id=" + id +
    //  " activity=" + activity + " special=" + spec + " leader=" + lead +
    //  " settlement=" + settlement);
    resourceInputs = model.gameResources.addResource();
    int actRes = model.ActivityTypes.getInputResources(activityType);
    MarsResource res = model.referenceResources.getResource(actRes);
    model.gameResources.addResources(resourceInputs, res);
    // now set up the outputs
    //System.out.println("define new activity outputs.");
    resourceOutputs = model.gameResources.addResource();
    actRes = model.ActivityTypes.getOutputResources(activityType);
    res = model.referenceResources.getResource(actRes);
    model.gameResources.addResources(resourceOutputs, res);
    // the inputs and outputs will be affected by additional workers and equipment
    //System.out.println("set activity attributes.");
    workers = 0;
    special = spec;
    workforce = 0;
    workToDo = 10;
    leader = lead;
    inputSource = settlement; // default to settlement
    inputSourceType = RESOURCE_SETTLEMENT; // default to settlement
    outputDestination = settlement;
    outputDestinationType = RESOURCE_SETTLEMENT;
    description = "New Activity";//((MarsType) theModel.ActivityTypes.elementAt(activityType)).name;
    addWorker(leader);
    calculateWorkForce(model);// 1 hour per day
    //
    // Now work out how much work there is to do
    // this depends on the activity type
    //System.out.println("calculate work to do.");
    switch (activityType)
    {
      case 0: // there must be some mistake...
        if( model.logEnabled ) model.logEvent("Invalid activity type selected.");
        description = "Inactivity";
        break;
      case 1: // construction
        startConstruction();
        //System.out.println("construction started.");
        break;
      case 2: // research
        if(special > 0)
        {
          MarsTechnology tech = 
            new MarsTechnology( (MarsTechnology)
            model.Technologies.get(special)) ;
          workToDo = tech.researchDays;
          description = "Research " + tech.name + ".";
        }
        break;
      case 3: // Survey
        // a survey of a waypoint takes 10 days
        workToDo = 10;
        description = "Survey";
        int wp = model.Settlements.getWaypoint(settlement);
        special = model.Waypoints.findNearestUnsurveyed(wp);
        if (special > 0)
        {
          // special indicates the waypoint to be surveyed
          description += " at (" + model.Waypoints.getLocation(special) + ")";
        }
        break;
      case 4: // Mining
        // A mining "shift" is for 10 days
        workToDo = 10;
        description = "Mining";
        inputSource = model.Settlements.getWaypoint(settlement); // override default of settlement
        inputSourceType = this.RESOURCE_WAYPOINT; // override default of settlement
        break;
      case 5: // Industry
        // An industry shift is for 10 days
        workToDo = 10;
        description = "Industry";
        break;
      case 6: // Manufacturing
        // A  manufacturing shift is for 10 days
        workToDo = 10;
        description = "Manufacturing";
        break;
      case 7: // Transportation
        // don't know how to do this yet
        // A  typical shift is for 10 days
        workToDo = 10;
        description = "Transportation";
        break;
      case 8: // Security
        // don't know how to do this yet
        // A  typical shift is for 10 days
        workToDo = 10;
        description = "Security";
        break;
      case 9: // Administration
        // don't know how to do this yet
        // A  typical shift is for 10 days
        workToDo = 10;
        description = "Administration";
        break;
      case 10: // Commerce
        // don't know how to do this yet
        // A  typical shift is for 10 days
        workToDo = 10;
        description = "Commerce";
        break;
      case 11: // Farming
        // A  farming shift is for 10 days
        workToDo = 10;
        description = "Farming";
        break;
      case 12: // Entertainment
        // don't know how to do this yet
        // A  typical shift is for 10 days
        workToDo = 10;
        description = "Entertainment";
        break;
      case 13: // Maintenance
        // A  typical shift is for 10 days
        workToDo = 10;
        description = "Maintenance";
        break;
      case 14: // Energy
        // A  typical shift is for 10 days
        workToDo = 10;
        description = "Energy";
        break;
      case 15: // Aquifer
        // A  typical shift is for 10 days
        workToDo = 10;
        description = "Aquifer";
        inputSource = model.Settlements.getWaypoint(settlement); // override default of settlement
        inputSourceType = this.RESOURCE_WAYPOINT; // override default of settlement
        break;
      case 16: // Air Supply
        // A  typical shift is for 10 days
        workToDo = 10;
        description = "Air Supply";
        break;
    } // end of switch activityType
    
  }

  public String toString()
  {
    String s = new String();
    
    s = "Settlement " + settlementId + ",";
    s = s + " type " + activityType + ",";
    s = s + " status " + status + ",";
    s = s + " inputs " + resourceInputs + "," + " outputs " + resourceOutputs + ",";
    s = s + " workers " + workers + ",";
    s = s + " special " + special + ",";
    s = s + " workforce " + workforce + ",";
    s = s + " to do " + workToDo + ",";
    s = s + " leader " + leader + ",";
    s = s + description;

    return s;
  }

  public String toFile()
  {
    String s = new String();
    
    s = s + new Integer(settlementId).toString() + ",";
    s = s + new Integer(activityType).toString() + ",";
    s = s + new Integer(status).toString() + ",";
    s = s + resourceInputs + "," + resourceOutputs + ",";
    s = s + new Long(workers).toString() + ",";
    s = s + new Integer(special).toString() + ",";
    s = s + new Long(workforce).toString() + ",";
    s = s + new Long(workToDo).toString() + ",";
    s = s + new Integer(leader).toString() + ",";
    s = s + description + ",";
    s = s + inputSource + "," + inputSourceType;
    s = s + "," + outputDestination + "," + outputDestinationType;

    return s;
  }
  
  public int getSettlementId()
  {
    return settlementId;
  }
  
  public void setSettlementId(int s)
  {
    settlementId = s;
  }
  
  public int getActivityType()
  {
    return activityType;
  }
  
  public void setActivityType(int a)
  {
    activityType = a;
  }
  
  public int getStatus()
  {
    return status;
  }
  
  public void setStatus(int s)
  {
    status = s;
  }
  
  public int getResourceInputs()
  {
    return resourceInputs;
  }
  
  public MarsResource getResourceUse(MarsModel model)
  {
    if(resourceInputs == 0) return null;
    return model.gameResources.getResource(resourceInputs);
  }
  
  public void setResourceInputs(int r)
  {
    resourceInputs = r;
  }
  
  public long getWorkers()
  {
    return workers;
  }
  
  public void setWorkers(long w)
  {
    workers = w;
  }
  
  public int getSpecial()
  {
    return special;
  }
  
  public void setSpecial(int s)
  {
    special = s;
  }
  
  public long getWorkforce()
  {
    return workforce;
  }
  
  public void setWorkforce(long w)
  {
    workforce = w;
  }
  
  public long getWorkToDo()
  {
    return workToDo;
  }
  
  public void setWorkToDo(long w)
  {
    workToDo = w;
  }
  
  public int getLeader()
  {
    return leader;
  }
  
  public void setLeader(int l)
  {
    leader = l;
    // a leader doesn't have to be a particpant, so doesn't count as a worker
  }
  
  public String getDescription()
  {
    return description;
  }
  
  public void setDescription(String s)
  {
    description = s;
  }
  
  public void addWorker(int p)
  {
    if( participants == null )
    {
      participants = new int[1];
      participants[0] = p;
    }
    else
    {
      int party[] = new int[participants.length + 1];
      int i;
      for (i=1; i<=participants.length; i++)
      {
        party[i] = participants[i];
      }
      party[i] = p;
      participants = (int[]) party.clone();
    }
    workers++;
  }
  
  public int[] getPeopleId()
  {
    return participants;
  }
  
  public int[] getEquipment()
  {
    return equipment;
  }
  
  public void calculateWorkForce(MarsModel model)
  {
    // simple calculation just now needs to be updated later
    workforce = workers;
  }
  
  public int updateActivity(MarsModel model)
  {
    if (status == IN_PROGRESS)
    {
      MarsResource r = null;
      float fraction = 1;
      long workDone;
      
      if(resourceInputs == 0)
      {
        resourceInputs = model.gameResources.addResource();
      }
      r = new MarsResource( (MarsResource) model.gameResources.get(resourceInputs) );
      
      // multiply the basic resource inputs by the workforce to get a total
      r.multiplyResource((float) workforce);

      if(workToDo <= workforce)
      {
        // deduct the amount of resources needed to finish the job
        fraction = ((float) workToDo) / workforce;
      }
      
      int sourceResourcesId = 0;
      if(inputSourceType == RESOURCE_SETTLEMENT)
      {
        sourceResourcesId = model.Settlements.getStoredResources(inputSource);
      }
      if(inputSourceType == RESOURCE_PERSON)
      {
        sourceResourcesId = model.People.getStoredResources(inputSource);
      }
      if(inputSourceType == RESOURCE_WAYPOINT)
      {
        sourceResourcesId = model.Waypoints.getStoredResources(inputSource);
      }
      if(inputSourceType == RESOURCE_ATMOSPHERE)
      {
        sourceResourcesId = model.AtmosphereResources;
      }
      // find out if there are enough resources available to finish the job
      if( !model.gameResources.sufficientResources(sourceResourcesId, r) )
      {
        float resFraction = model.gameResources.resourceFraction(sourceResourcesId, r);
        fraction = fraction * resFraction;
      }
      
      workDone = (long) (workforce * fraction);
      
      // subtract the inputs from the source
      r.multiplyResource(fraction);
      model.gameResources.subtractResources(sourceResourcesId, r);
      
      // calculate the outputs
      if(resourceOutputs == 0)
      {
        resourceOutputs = model.gameResources.addResource();
      }
      r = new MarsResource( (MarsResource) model.gameResources.get(resourceOutputs) );
      
      // multiply the basic resource outputs by the workforce to get a total
      r.multiplyResource((float) workforce);

      // add the outputs to the destination
      int destinationResourcesId = 0;
      if(outputDestinationType == RESOURCE_SETTLEMENT)
      {
        destinationResourcesId = model.Settlements.getStoredResources(outputDestination);
      }
      if(outputDestinationType == RESOURCE_PERSON)
      {
        destinationResourcesId = model.People.getStoredResources(outputDestination);
      }
      if(outputDestinationType == RESOURCE_WAYPOINT)
      {
        destinationResourcesId = model.Waypoints.getStoredResources(outputDestination);
      }
      if(outputDestinationType == RESOURCE_ATMOSPHERE)
      {
        destinationResourcesId = model.AtmosphereResources;
      }
      model.gameResources.addResources(destinationResourcesId, r);
      
      // update the amount of work left to do
      workToDo = workToDo - workDone;
      
      // if it's all done the activity is complete
      if (workToDo <=0.1 )
      {
        status = COMPLETE;
        this.activityComplete(model);
      }
      
    }
    return status;
  }

  private void activityComplete(MarsModel model)
  {
    model.messageFrom("Finished " + this.description ,leader);
    switch( activityType )
    {
      case 1: //Construction
        if(special > 0)
        ((MarsConstruction) theModel.Constructions.get(special) ).setStatus(MarsConstruction.IN_USE);
         // Add new construction to settlement
/*
          MarsConstruction c = new MarsConstruction();
          c.constructionType = a.special;
          c.status = c.NOT_IN_USE;
          c.storedResources = 0;
          c.usedInActivity = 0;
          c.location = new LatLong( ((MarsSettlement) 
            theModel.Settlements.elementAt(a.settlementId)).location );
          c.settlement = a.settlementId;
          c.efficiency = 100;
          c.workers = 0;
          theModel.Constructions.addElement(c);
*/
          break;
        case 2: //Research
          // record new discovery
          model.Discoveries.newDiscovery(special, leader, settlementId);
          break;
        case 3: //Survey
          // has anything been found?
          // mark this area as surveyed.
          model.Waypoints.setSurveyed(special, settlementId);
          break;
        case 4: //Mining
          // nothing special to do
          break;
        case 5: //Industry
          // nothing special to do
          break;
        case 6: //Manufacturing
          // nothing special to do
          break;
        case 7: //Travel
          // nothing special to do
          break;
        case 8: //Security
          // nothing special to do
          break;
        case 9: //Administration
          // nothing special to do
          break;
        case 10: //Commerce
          // nothing special to do
          break;
        case 11: //Farming
          // nothing special to do
          break;
        case 12: //Entertainment 
          // nothing special to do
          break;
    }
    status = COMPLETE;
    // delete all things owned by this activity
    model.gameResources.deleteItem(resourceInputs);
    model.gameResources.deleteItem(resourceOutputs);
    
 }
  
/*
  public void destroy(MarsModel model)
  {
    // alert people who are involved in this activity
    if (peopleId.length > 0)
    {
      // there are people to contact
      for (int i=1; i<peopleId.length; i++)
      {
        model.People.ActivityStopped(
      }
    }
  }
*/
  public void setInputs(int inS, int inST)
  {
    inputSource = inS;
    inputSourceType = inST;
  }
  
  public void setOutputs(int outD, int outDT)
  {
    outputDestination = outD;
    outputDestinationType = outDT;
  }
  
  protected void startConstruction()
  {
    // Get the next construction to be built from the Plan - the settlement knows what is next
    System.out.println("get construction");
    special = ((MarsSettlement) theModel.Settlements.get(settlementId)).getNextConstruction(theModel);
    System.out.println("construction = " + special);
    if (special > 0)
    {
      // special = the id of the construction
      System.out.println("get construction type");
      int conType = ((MarsConstruction) theModel.Constructions.get(special) ).getType();
      System.out.println("construction type = " + conType);
      MarsConstructionType cType = 
        (MarsConstructionType)
        theModel.ConstructionTypes.get(conType) ;
      workToDo = cType.components / 10; // assemble 10 components per day
      description = "Construct a " + cType.getName() + ".";
    }
 }
  
}

 
