/**
 * @file Supply.java
 *
 * @author
 *    Christos Choutouridis
 *    <cchoutou@ece.auth.gr>
 *    AEM:8997
 */

package host.labyrinth;

/**
 * @brief
 *    This class is the representation of the supplies in the game
 *
 * Supplies are the game "goodies". They placed randomly in the board.
 * In each tile there can be only one supply. The player has to collect all
 * of them in order to complete the game successfully.
 */
class Supply {

   /** @name Constructors */
   /** @{ */

   /**
    * The main constructor of the Supply constructed from (row,column)
    *
    * @param id      The Id of the created supply
    * @param row     The row coordinate to place the supply
    * @param col     The column coordinate to place the supply
    */
   Supply(int id, int row, int col) {
      // Boundary checks
      assert (row >= 0 && row< Session.boardSize) : "Row coordinate must be in the range [0, Session.boardSize)";
      assert (col >= 0 && col< Session.boardSize) : "Column coordinate must be in the range [0, Session.boardSize)";

      // Initialization
      this.supplyId  =id;
      this.x         =col;
      this.y         =row;
      this.supplyTileId = Position.toID(row, col);
   }

   /**
    * A second constructor of the Supply constructed from supplyTileId
    *
    * @param id      The Id of the created supply
    * @param tileId  The linear combination of (row, column)
    */
   Supply(int id, int tileId) {
      // Boundary check
      assert (id >= 0 && id <= Position.toID(Session.boardSize-1, Session.boardSize-1))
         : "TileId must be in the range of [0, Session.boardSize^2)";

      // Initialization
      this.supplyId     = id;
      this.supplyTileId = tileId;
      this.x            = Position.toCol(tileId);
      this.y            = Position.toRow(tileId);
   }

   /**
    * A deep copy constructor.
    */
   Supply (Supply s) {
      this.supplyId  = s.supplyId;
      this.x         = s.x;
      this.y         = s.y;
      this.supplyTileId = s.supplyTileId;
      // We achieve deep copy as the members are all primitives.
   }
   /** @} */

   /** @name Supply's main application interface */
   /** @{ */

   /** @return the supplyId */
   int supplyId ()        { return supplyId; }
   /**
    * Set the supplyId
    * @param   sID   The Id to set
    * @return  The supplyId
    * @note    This function also returns the supplyId to help in chained expressions.
    */
   int supplyId (int sID) { return supplyId = sID; }

   /**
    * @return the position of the supply as a Position object
    * @see Position
    */
   Position position ()   { return new Position (supplyTileId); }

   /**
    * Set the position of the supply from a (row, column) pair
    * @param row  The row coordinate of the tile
    * @param col  The column coordinate of the tile
    * @return the position of the supply as a Position object
    * @note       This function also returns the Position to help in chained expressions.
    * @see Position
    */
   Position position (int row, int col) {
      // Boundary checks
      assert (row >= 0 && row< Session.boardSize) : "Row coordinate must be in the range [0, Session.boardSize)";
      assert (col >= 0 && col< Session.boardSize) : "Column coordinate must be in the range [0, Session.boardSize)";

      Position p  = new Position (row, col);
      this.x      = p.getCol();  // =col;
      this.y      = p.getRow();  // =row;
      this.supplyTileId = p.getId();
      return p;
   }

   /**
    * Set the position of the supply from a tileId
    * @param tileId  The tileId position
    * @return        The position of the supply as Position object
    * @note          This function also returns the Position to help in chained expressions.
    * @see Position
    */
   Position position (int tileId) {
      // Boundary check
      assert (tileId >= 0 && tileId <= Position.toID(Session.boardSize-1, Session.boardSize-1))
         : "TileId must be in the range of [0, Session.boardSize^2)";

      Position p  = new Position (tileId);
      this.x      = p.getCol();
      this.y      = p.getRow();
      this.supplyTileId = p.getId();   // =tileId;
      return p;
   }

   /**
    * Marks the supply removed. This usually mean the supply is picked up by a user.
    */
   void removeSupply () {  this.supplyId = Const.noSupply; }
   /** @} */

   /**
    * @name Accessor/Mutator interface
    * @note
    *    Please consider not to use mutator interface. Its the abstraction killer :(
    *    We have added a bit of logic however, in order to make it a bit more safe.
    */
   /** @{ */
   int getSupplyId ()   { return supplyId; }
   int getX()           { return x; }
   int getY()           { return y; }
   int getSupplyTileId(){ return supplyTileId; }

   void setSupplyId(int Id)   { supplyId = Id; }
   void setX(int x) {
      assert (x >= 0 && x< Session.boardSize) : "X(column) coordinate must be in the range [0, Session.boardSize)";
      this.x = x;
      this.supplyTileId = Position.toID(this.x, this.y);
   }
   void setY(int y) {
      assert (y >= 0 && y< Session.boardSize) : "Y(row) coordinate must be in the range [0, Session.boardSize)";
      this.y = y;
      this.supplyTileId = Position.toID(this.x, this.y);
   }
   void setSupplyTileId(int tileId) {
      assert (tileId >= 0 && tileId <= Position.toID(Session.boardSize-1, Session.boardSize-1))
      : "TileId must be in the range of [0, Session.boardSize^2)";
      this.supplyTileId = tileId;
      this.x = Position.toCol(tileId);
      this.y = Position.toRow(tileId);

   }
   /** @} */

   /** @name Class data */
   /** @{ */
   private int supplyId;      /**< The unique identifier of the tile. This must not be confused with TileID */
   private int x;             /**< The x coordinate of the tile as if the board lies in the 1st quadrant */
   private int y;             /**< The y coordinate of the tile as if the board lies in the 1st quadrant */
   private int supplyTileId;  /**< The Id of the tile on the board, in witch the supply is located */
   /**
    * @warning
    *    We can calculate tileId from (x,y) so having both (x,y) and tile ID is error
    *    prone and not a good practice. Its easy to get them out of sync (code smell).
    *    We implement it just because its in the requirements of the assignment.
    *    @see Tile.tileId
    */
   /** @} */
}
