/**
 * @file Common.java
 *
 * @author
 *    Christos Choutouridis
 *    <cchoutou@ece.auth.gr>
 *    AEM:8997
 */
package host.labyrinth;

import java.util.ArrayList;
import java.util.Collections;

/**
 * Class to hold constant values for entire application
 */
class Const {
   static final int maxTileWalls = 2;  /**< Number of maximum walls for each tile on the board */
   static final int noSupply =-1;      /**< Number to indicate the absent of supply */
   static final int noTileId =-1;      /**< Number to indicate wrong tileId */
}
/**
 * Application wide object to hold settings like values for the session.
 */
class Session {
   static int  boardSize = 15;         /**< Default board's size (if no one set it via command line) */
   static int  supplySize = 4;         /**< Default board's supply size (if no one set it via command line) */
   static int  maxRounds = 100;        /**< Default number of rounds per game (if no one set it via command line) */
   static boolean loopGuard = false;   /**< When true a wall creation guard is added to prevent closed rooms inside the board */
   static boolean interactive = false; /**< When true each round of the game requires user input */
}

/**
 * Helper C++-like enumerator class to hold direction
 */
class Direction {
   static final int  UP    =1;   /**< North direction */
   static final int  RIGHT =3;   /**< East direction */
   static final int  DOWN  =5;   /**< South direction */
   static final int  LEFT  =7;   /**< West direction */

   /**
    * Utility to get the opposite direction.
    * @param direction  Input direction
    * @return           The opposite direction
    */
   static int opposite (int direction) { return (direction+4)%DirRange.End; }
}

/**
 * Helper C++ like enumerator class for direction ranged loops.
 *
 * We can make use of this in loops like:
 * <pre>
 * for (int i=DirRange.Begin ; i<DirRange.End ; i += DirRange.Step) { }
 * 
 * or
 * 
 * Range directions = new Range(DirRange.Begin, DirRange.End, DirRange.Step);
 * </pre>
 */
class DirRange {
   static final int  Begin =1;   /**< Iterator style begin of range direction (starting north) */
   static final int  End   =8;   /**< Iterator style end of range direction (one place after the last) */
   static final int  Step  =2;   /**< Step for iterator style direction */
}

/**
 * @brief
 *    An Application wide board position implementation holding just the id coordinate.
 *
 * Position is a helper class to enable us cope with the redundant position data (id and coordinates).
 * This class provide both static conversion functionalities between id and coordinates
 * and data representation in the coordinates system.
 * For clarity we adopt a tileId convention.
 */
class Position {

   /**
    * Basic constructor from row-column coordinates
    * @param row     The row coordinate
    * @param col     The column coordinate
    */
   Position(int row, int col) {
      this.id = toID(row, col);
   }

   /**
    * Basic constructor from Id
    * @param tileId  The id of tile
    */
   Position(int tileId) {
      this.id = tileId;
   }

   /**
    * Constructor from row-column coordinates and a direction.
    *
    * This constructor creates a position relative to coordinates.
    *
    * @param row        The row coordinate
    * @param col        The column coordinate
    * @param direction  The direction
    */
   Position(int row, int col, int direction) {
      switch (direction) {
         case Direction.UP:   this.id = toID(row+1, col);   break;
         case Direction.DOWN: this.id = toID(row-1, col);   break;
         case Direction.LEFT: this.id = toID(row, col-1);   break;
         case Direction.RIGHT:this.id = toID(row, col+1);   break;
      }
   }

   /** @name non-static API */
   /** @{ */
   int getRow()  { return toRow(id); }   /**< Read access to virtual row coordinate */
   int getCol()  { return toCol(id); }   /**< Read access to virtual column coordinate */
   int getId()   { return id; }          /**< Read access to id coordinate */
   /** @} */

   /** @name Static convention utilities */
   /** @{ */
   /**
    * Takes row and column coordinates and return the calculated Id coordinate
    * @param row  The row coordinate
    * @param col  The column coordinate
    * @return     The converted value
    */
   static int toID(int row, int col) {
      return row * Session.boardSize + col;
   }

   /**
    * Takes Id coordinate and return the corresponding row coordinate
    * @param id   The id coordinate
    * @return     The row coordinate
    */
   static int toRow(int id){
      return id / Session.boardSize;
   }
   /**
    * Takes Id coordinate and return the corresponding column coordinate
    * @param id   The id coordinate
    * @return     The column coordinate
    */
   static int toCol(int id) {
      return id % Session.boardSize;
   }
   /** @} */

   /** @name private data types */
   /** @{ */
   private int id;    /**< The id coordinate of the constructed Position object */
   /** @} */
}

/**
 * Class to create ranges of numbers
 */
class Range {
   /**
    * Create the range [begin, end)
    * @param begin   The first item on the range
    * @param end     The item after the last on the range
    */
   Range (int begin, int end) {
      numbers = new ArrayList<Integer>();
      init (begin, end, 1);      
   }
   /**
    * Create the range [begin, end) using step as interval between items.
    * @param begin   The first item on the range
    * @param end     The item after the last on the range
    * @param step    The interval between items
    */
   Range(int begin, int end, int step) {
      numbers = new ArrayList<Integer>();
      init (begin, end, step);
   }

   /**
    * Common utility to create the range for all constructors
    */
   private void init (int begin, int end, int step) {
      numbers.clear();
      for (int i=begin ; i<end ; i+=step)
         numbers.add(i);
   }
   /**
    * Extract and return the first item from the range.
    * @return  The first item of the range or Const.noTileId if there is none.
    */
   int get () {
      if (!numbers.isEmpty())
         return numbers.remove(0);
      return Const.noTileId;
   }

   /** @name protected data types */
   /** @{ */
   protected ArrayList<Integer> numbers;  /**< handle to range */
   /** @} */
}

/**
 * Class to create shuffled ranges of numbers
 */
class ShuffledRange extends Range {
   /**
    * Create a shuffled version of range [begin, end)
    * @param begin   The first item on the range
    * @param end     The item after the last on the range
    */
   ShuffledRange(int begin, int end) {
      super(begin, end);         // Delegate
      Collections.shuffle(numbers);
   }
   /**
    * Create a shuffled version of the range [begin, end)
    * using step as interval between items.
    *
    * @param begin   The first item on the range
    * @param end     The item after the last on the range
    * @param step    The interval between items
    */
   ShuffledRange(int begin, int end, int step) {
      super(begin, end, step);   // Delegate
      Collections.shuffle(numbers);
   }
}

/**
 * @brief
 *    A utility class used for room prevent algorithm.
 *
 * This class is the wall representation we use in the room preventing algorithm.
 * In this algorithm we represent the crosses between tiles as nodes (V) of a graph and the
 * walls as edges. So for example:
 * <pre>
 *   12--13--14---15
 *    |           |
 *    8   9--10   11
 *    |       |   |
 *    4   5   6   7
 *    |   |       |
 *    0   1---2---3
 * </pre>
 * In this example we have a 4x4=16 vertices board(nodes) and 14 edges(walls).
 * To represent the vertices on the board we use the same trick as the tileId
 *
 * V = Row*(N+1) + Column, where N is the board's tile size.
 *
 * The edges are represented as vertices pairs. For example (0, 4) or (13, 14).
 *
 * @note
 *    Beside the fact that we prefer this kind of representation of the walls in
 *    the application we use the one that is suggested from the assignment. This is
 *    used only in room preventing algorithm.
 * @note
 *    Using this kind of representation we don't have any more the "problem"
 *    of setting the wall in both neighbor tiles.
 */
class Edge {
   /**
    * This constructor acts as the interface between the application's wall
    * representation and the one based on graph.
    * @param tileId     The tile id of the wall.
    * @param direction  The direction of the tile where the wall should be.
    */
   Edge(int tileId, int direction) {
      int N = Session.boardSize +1;
      switch (direction) {
         case Direction.UP:
            v1= (Position.toRow(tileId) + 1)*N + Position.toCol(tileId);
            v2= (Position.toRow(tileId) + 1)*N + Position.toCol(tileId) + 1;
            break;
         case Direction.DOWN:
            v1= (Position.toRow(tileId))*N + Position.toCol(tileId);
            v2= (Position.toRow(tileId))*N + Position.toCol(tileId) + 1;
            break;
         case Direction.LEFT:
            v1= (Position.toRow(tileId))*N + Position.toCol(tileId);
            v2= (Position.toRow(tileId) + 1)*N + Position.toCol(tileId);
            break;
         case Direction.RIGHT:
            v1= (Position.toRow(tileId))*N + Position.toCol(tileId) + 1;
            v2= (Position.toRow(tileId) + 1)*N + Position.toCol(tileId) +1;
            break;
      }
   }
   /** A deep copy contructor */
   Edge(Edge e) {
      v1 = e.getV1();
      v2 = e.getV2();
   }
   /** Access of the first node of the edge */
   int getV1() { return v1; }
   /** Access of the second node of the edge */
   int getV2() { return v2; }

   private int v1;   /**< First vertex of the edge */
   private int v2;   /**< Second vertex of the edge */
}

/**
 * @brief
 *    Provides a graph functionality for the room preventing algorithm.
 * We use a graph to represent the wall structure of the walls. This way
 * its easy to find any closed loops. Using graph we transform the problem
 * of the closed room into the problem of finding a non simple graph.
 *
 * If the board has non connected wall structure then we would need a non
 * coherent graph to represent it. This class provides constroctors and
 * methods to create coherent graphs
 * 
 * An example of the biggest coherent graph we can create from the board bellow,
 * starting from V=1 is:
 * <pre>
 *    6---7   8                (1)
 *    |       |               /  \
 *    3   4   5             (4)  (2)
 *    |   |   |                    \
 *    0   1---2                    (5)--(8)
 * </pre>
 */
class Graph {
   /**
    * Constructs a node of the graph using the value of a vertex(node).
    * @param v    The verteg to attach.
    */
   Graph (int v) {
      V = v;
      E = new ArrayList<Graph>();
   }
   /**
    * Constructor that transform an edge into graph.
    * @param e    The edge to transform.
    */
   Graph (Edge e) {
      V = e.getV1();
      E = new ArrayList<Graph>();
      E.add(new Graph(e.getV2()));
   }

   /** Access to the current vertex */
   int getV()  { return V; }
   /** Access to the links of the current vertex */
   ArrayList<Graph> getE() { return E; }

   /**
    * Attach an edge into a graph IFF the graph already has a vertex
    * with the same value as one of the vertices of the edge.
    * @param e    The edge to attach.
    * @return     The status of the operation.
    *    @arg     True on success
    *    @arg     False on failure
    */
   boolean attach (Edge e) { 
      return tryAttach(e, 0) > 0;
   }

   /**
    * Counts the number of vertices on the graph with the value of `v`
    * @param v    The vertex to count
    * @return     The number of vertices with value `v`
    */
   int count (int v) {
      return tryCount (v, 0);
   }

   /**
    * Recursive algorithm that tries to attach an edge into a graph
    * IFF the graph already has a vertex with the same value as one
    * of the vertices of the edge.
    *
    * @param e       The edge to attach.
    * @param count   An initial count value to feed the algorithm.
    * @return     The status of the operation.
    *    @arg     True on success
    *    @arg     False on failure
    */
   private int tryAttach (Edge e, int count) {
      for (Graph n: E)
         count = n.tryAttach (e, count);
      if (V == e.getV1()) {
         E.add(new Graph(e.getV2()));
         ++count;
         }
      if (V == e.getV2()) {
         E.add(new Graph(e.getV1()));
         ++count;
      }
      return count;
   }

   /**
    * Recursive algorithm that tries to count the number of vertices
    * on the graph with the same value as `v`.
    *
    * @param v       The vertex to count
    * @param count   An initial count value to feed to the algorithm.
    * @return     The number of vertices with value `v`
    */
   private int tryCount (int v, int count) {
      for (Graph n: E)
         count = n.tryCount (v, count);
      if (V == v)
         return ++count;
      return count;
   }

   private int V;                /**< The value of the current vertex/node */
   private ArrayList<Graph> E;   /**< A list of all the child nodes */
}
