
// Copyright (c) 2004 
package uk.co.patrickhaston.mars;

import uk.co.patrickhaston.pdharray2d.PdhArray2d;
import uk.co.patrickhaston.pdhdata.PdhError;
import java.awt.Point;

/**
 * A Class class.
 * <P>
 * @author Patrick Haston
 */
public class MarsSettlement extends MarsType
{
  private int waypoint;
  private long population;
  private int storedResourcesId;
  private MarsWaypoint WayPoint = null;
  private int planType;
  private int nextInPlan;
  
  private PdhArray2d grid;
  private int[] buildings;
  private Point gridOffset;

  /**
   * Constructor
   */
  public MarsSettlement(int id)
  {
    super(id);
    population = 0;
    storedResourcesId = 0;
    waypoint = 0;
    planType = 0;
    nextInPlan = 0;
  }

  public MarsSettlement(MarsSettlement s)
  {
    super((MarsType) s);
    population = s.population;
    storedResourcesId = s.storedResourcesId;
    waypoint = s.waypoint;
    WayPoint = s.WayPoint;
    planType = s.planType;
    nextInPlan = s.nextInPlan;
  }
  
  public MarsSettlement(String line)
  {
    super(line);
    waypoint = readInteger(line, 1);
    population = readLong(line, 2);
    storedResourcesId = readInteger(line, 3);
    planType = readInteger(line,4);
    nextInPlan = readInteger(line,5);
  }

  public String toString()
  {
    String s = super.toString();
    
    s = s + " Population: " + population + ",";
    s = s + " Resources: " + storedResourcesId;
    s = s + " Plan Type: " + planType;
    s = s + " Next in Plan: " + nextInPlan;

    return s;
  }

  public String toFile()
  {
    String s = super.toFile();
    
    s = s + "," + waypoint;
    s = s + "," + population + "," + storedResourcesId + "," + planType;
    s = s + "," + nextInPlan;

    return s;
  }
  
  public void subtractResource(MarsModel model, MarsResource r)
  {
    ((MarsResource) model.gameResources.elementAt(storedResourcesId)).subtractResource(r);
  }

  public void addResource(MarsModel model, MarsResource r)
  {
    ((MarsResource) model.gameResources.elementAt(storedResourcesId)).addResource(r);
  }
  
  public void setWaypoint(int wp, MarsWaypoint w)
  {
    waypoint = wp;
    WayPoint = w;
  }
  
  public LatLong getLocation()
  {
    return WayPoint.getLocation();
  }
  
  public int getWaypoint()
  {
    return waypoint;
  }
  
  public int getStoredResources()
  {
    return storedResourcesId;
  }
  
  /**
   * getNextConstruction - get the next item to build from the list
   * @param model
   * @return constructionId of the new building or 0 if nothing more to build
   */
  public int getNextConstruction(MarsModel model)
  {
    System.out.println("getNextInPlan for planType " + planType + " and nextInPlan " + nextInPlan);
    int p = model.Plans.getNextInPlan(planType, nextInPlan);
    System.out.println("Next In Plan = " + p);
    if (p == 0) return 0;
    MarsPlan plan = (MarsPlan) model.Plans.elementAt(p);
    if( plan.getConstructionType() == 0) return 0;
    int conId = model.Constructions.addConstruction(plan.getConstructionType(), getLocation(), this.getId(), this.getId());
    MarsConstruction construction = (MarsConstruction) model.Constructions.elementAt(conId);
    construction.status = MarsConstruction.UNDER_CONSTRUCTION;
    construction.setTile(new Point(plan.getX(), plan.getY()));
    construction.setOrientation(plan.getOrientation());
    nextInPlan++;
    return conId;
  }
  
  /**
   * 
   * @param model = the model of the game
   * @param activityId = the activity which is building the construction
   * @param constructionId = the construction being built
   * @return true = success
   */
  public boolean setTileForNewBuilding(MarsModel model, int activityId, int constructionId)
  {
    Point tile = new Point();
    // find the start point of the settlement, which should be an airlock at 0,0
    int b = 0;  // the move through the buildings array to find the first airlock
    MarsConstruction c = null;
    do
    {
      c = (MarsConstruction) model.Constructions.get(buildings[b]);
      b++;
    } while (c.getType() != 26 && b<buildings.length);
    if(b == buildings.length)
    {
      // couldn't find an airlock - put this building at the start
      tile.x = 0;
      tile.y = 0;
      model.Constructions.setTile(constructionId, tile);
      model.Constructions.setOrientation(constructionId, 0);
      return false;
    }
    // ascertain the orientation of the airlock
    int dir = c.getOrientation();
    // move inwards from the airlock to find the first available "slot"
    
    // is a new length of corridor required?  if so, add it to the activity (set the location of the corridor)
    // return the location of the constructions in the activity
    // return 0,0 to begin with
    tile.x = 0;
    tile.y = 0;
    model.Constructions.setTile(constructionId, tile);
    model.Constructions.setOrientation(constructionId, 0);
    return true;
  }
  
  /**
   * 
   * @param startAt = the building to start searching from
   * @param direction = the direction to search in
   * @param depth = the length of corridor to search to before returning
   * @param max = the maximum corridor length before branching
   * @param model = the model of the game
   * @return = the point of the free slot (or null for nothing found)
   */
  private Point getFreeTile(int startAt, int direction, int depth, int max, int constructionId, MarsModel model)
  {
    int currentBuildingId = startAt;
    MarsConstruction currentBuilding = null;
    int nextBuilding = 0;
    int corridorX = 0;
    int corridorY = 0;
    int buildingX = 0;
    int buildingY = 0;

    for (int currentDepth=0; currentDepth < depth; currentDepth++)
    {
      currentBuilding = (MarsConstruction) model.Constructions.get(currentBuildingId);
      if(currentBuilding == null) return null;
      nextBuilding = currentBuilding.getLinked(direction);
      if( nextBuilding == 0 )
      {
        // there is nothing linked in this direction
        // check to see if there is space going forwards and to the left and right
        corridorX = currentBuilding.getLocalX();
        corridorY = currentBuilding.getLocalY();
        buildingX = currentBuilding.getLocalX();
        buildingY = currentBuilding.getLocalY();
        switch(direction)
        {
          case 0: corridorY += 1; break;
          case 1: corridorX += 1; break;
          case 2: corridorY -= 1; break;
          case 3: corridorX -= 1;  break;
        }
        if(grid.elementAt(corridorX,corridorY) != null) return null; // there's something in the way
        // check to the left
        switch(direction)
        {
          case 0: buildingX -= 1; buildingY += 1; break;
          case 1: buildingX += 1; buildingY += 1; break;
          case 2: buildingX += 1; buildingY -= 1; break;
          case 3: buildingX -= 1; buildingY -= 1;  break;
        }
        if(grid.elementAt(buildingX,buildingY) == null)
        {
          // success! we've found what we need, a free space and a way to get there
          // we need to create a new corridor construction
          int corridorId = model.Constructions.addConstruction(28, this.getLocation(), this.getId(), this.getId());
          // link the current building to this new building
          currentBuilding.linkTo(corridorId, direction);
          // we need to add this to the build list
          MarsConstruction corridor = (MarsConstruction) model.Constructions.get(corridorId);
          corridor.setOrientation(direction);
          corridor.setTile(new Point(corridorX,corridorY));
          MarsConstruction building = (MarsConstruction) model.Constructions.get(constructionId);
          int dir = direction - 1;
          if(dir < 0) dir += 4;
          building.setOrientation(dir);
          corridor.linkTo(constructionId, dir);
          Point p = new Point(buildingX, buildingY);
          building.setTile(p);
          return p;
        }
        // check to the right
        switch(direction)
        {
          case 0: buildingX += 1; buildingY += 1; break;
          case 1: buildingX += 1; buildingY -= 1; break;
          case 2: buildingX -= 1; buildingY -= 1; break;
          case 3: buildingX -= 1; buildingY += 1;  break;
        }
        if(grid.elementAt(buildingX,buildingY) == null)
        {
          // success! we've found what we need, a free space and a way to get there
          // we need to create a new corridor construction
          int corridorId = model.Constructions.addConstruction(28, this.getLocation(), this.getId(), this.getId());
          // we need to add this to the build list
          MarsConstruction corridor = (MarsConstruction) model.Constructions.get(corridorId);
          corridor.setOrientation(direction);
          corridor.setTile(new Point(corridorX,corridorY));
          MarsConstruction building = (MarsConstruction) model.Constructions.get(constructionId);
          direction += 1;
          if(direction > 3) direction -= 4;
          building.setOrientation(direction);
          Point p = new Point(buildingX, buildingY);
          building.setTile(p);
          return p;
        }
      }
      // there is a linked building in this direction.
      // it must be a corridor because we are creating a grid
      // find out it's id and make loop back to the beginning
      if(depth < max) currentBuildingId = nextBuilding;
    }
    // we reached the end of depth
    // is this the end of this corridor
    if(depth == max)
    {
      // choose a random direction to search in child corridors
      int childDirection = (int) (Math.random() * 3) + 1;
      if(nextBuilding > 0)
      {
        currentBuildingId = nextBuilding;
        Point p = null;
        for (int branch=-1; branch==1; branch++)
        {
          int d = direction + childDirection;
          if(d>3) d-=4;
          p = getFreeTile(currentBuildingId,d,5,5,constructionId,model);  
          if(p != null) return p;
        }
        return null;
      }
      // the next building hasn't been built yet.
      // this is going to be a cross roads, so make it one from the start.
      nextBuilding = model.Constructions.addConstruction(30, this.getLocation(), this.getId(), this.getId());
      // connect to the new corridor
      currentBuilding.linkTo(nextBuilding, direction);
      MarsConstruction corridor = (MarsConstruction) model.Constructions.get(nextBuilding);
    }
    return null;
  }
    
  public void setupSettlement(MarsModel model)
  {
    buildings = model.Constructions.getConstructionsAt(waypoint);
    int minX = 0;
    int minY = 0;
    int maxX = 0;
    int maxY = 0;
    int i;
    for(i=0; i<buildings.length; i++)
    {
      MarsConstruction c = (MarsConstruction) model.Constructions.get(buildings[i]);
      int x1 = c.getLocalX();
      int y1 = c.getLocalY();
      int x2 = x1;
      int y2 = y1;
      int t = c.getType();
      MarsConstructionType ct = (MarsConstructionType) model.ConstructionTypes.get(t);
      int dir = c.getOrientation();
      switch(dir)
      {
        case 0: // north
          y2 += ct.length/10 - 1;
          x2 += ct.width/10 - 1;
          break;
        case 1: // east
          y1 -= ct.width/10 -1;
          x2 += ct.length/10 - 1;
          break;
        case 2: // south
          y1 -= ct.length/10 - 1;
          x1 -= ct.width/10 - 1;
          break;
        case 3: // east
          y2 += ct.width/10 -1;
          x1 -= ct.length/10 - 1;
          break;
      }
      if(x1<minX) minX = x1;
      if(x2>maxX) maxX = x2;
      if(y1<minY) minY = x1;
      if(y2>maxY) maxY = x2;
    }
    gridOffset = new Point(minX, minY);
    grid = new PdhArray2d(maxX - minX, maxY - minY);
    // now populate the grid
    for(i=0; i<buildings.length; i++)
    {
      MarsConstruction c = (MarsConstruction) model.Constructions.get(buildings[i]);
      int x1 = c.getLocalX();
      int y1 = c.getLocalY();
      int x2 = x1;
      int y2 = y1;
      int t = c.getType();
      MarsConstructionType ct = (MarsConstructionType) model.ConstructionTypes.get(t);
      int dir = c.getOrientation();
      switch(dir)
      {
        case 0: // north
          y2 += ct.length/10 - 1;
          x2 += ct.width/10 - 1;
          break;
        case 1: // east
          y1 -= ct.width/10 -1;
          x2 += ct.length/10 - 1;
          break;
        case 2: // south
          y1 -= ct.length/10 - 1;
          x1 -= ct.width/10 - 1;
          break;
        case 3: // east
          y2 += ct.width/10 -1;
          x1 -= ct.length/10 - 1;
          break;
      }
      for(int j=x1; j<=x2; j++)
      {
        for(int k=y1; k<=y2; k++)
        {
          grid.setElementAt(new Integer(buildings[i]),j,k);
        }
      }
    }
  }
  
  public int getConstructionAt(Point tile)
  {
    if (grid == null) 
    {
      return -5; //PdhError.OUT_OF_RANGE;
    }
    if (grid.elementAt(tile.x,tile.y) == null)
    {
      return -6; //PdhError.OUT_OF_RANGE;
    }
    return ((Integer) grid.elementAt(tile.x,tile.y)).intValue();
  }
}

 
