////////////////////////////////////////////////////////////////////////////////
// Match                                                                      //
////////////////////////////////////////////////////////////////////////////////
bool GameCore::checkMatch(const CoreCoord::Coord &coord1,
                          const CoreCoord::Coord &coord2)
{
    //Game is already over.
    if(m_status != Status::Continue)
        return false;

    //Both must be valid coords.
    if(!isValidCoord(coord1) || !isValidCoord(coord2))
        return false;

    //Coords must be different.
    if(coord1 == coord2)
        return false;

    //Get the cards.
    auto &card1 = getCardAt(coord1);
    auto &card2 = getCardAt(coord2);

    //Already matched...
    if(card1.matched || card2.matched)
        return false;

    auto cardsAreEqual = (card1.value == card2.value);

    if(cardsAreEqual)
    {
        ++m_matchedPairsCount;
        const_cast<Card &>(card1).matched = true;
        const_cast<Card &>(card2).matched = true;

        if(m_matchCountAsTry)
            ++m_triesCount;
    }
    else
    {
        //Player miss the cards
        // TryCount is incremented ALWAYS in this situation.
        ++m_triesCount;
    }

    checkStatus();

    return cardsAreEqual;
}
bool GameCore::isValidIndex(int index) const
{
    return isValidCoord(indexToCoord(index));
}
bool Pathfinder::getWalkable(const sf::Vector2i& coord){
	if (isValidCoord(coord)){
		return m_nodes[coord.x + (coord.y * m_mapWidth)]->walkable();
	}
	return false;
}
// Update the routes, this method is called each time
// a new tower is built or a present tower is destroyed.
//
// This method uses a Greedy algorithm to calculate the routes.
//
// To begin with, the target node is pushed into the priority
// queue with distance 0. While the priorit queue is not
// empty, at each iteration, the node with minimal distance will 
// be popped from the queue. Then its adjacent nodes will
// be inspected. For each adjacent node, if it has not been
// visited before, it will be pushed into the queue with the
// new distance and its direction and visited flag will be set.
//
// The Creeps can go horizontally, vertically or diagonally.
// A diagonal direction is valid only when the the diagonal
// grid can be accessed from both the two ways:
// e.g.
//    A | B       A -> D is valid only when both
//    --+--       A -> B -> D is valid and
//    C | D       A -> C -> D is valid
//
// Creeps can can directly go from A to D if Both B and C
// is not occupied by towers.
//
// By introducing the diagonal shortcuts, the routes will
// be more natural.
void GridMap::updateRoute() {

    // horizontal and vertical offsets
    static const int offset_x[] = {-1, 0, 1, 0};
    static const int offset_y[] = {0, 1, 0, -1};
    
    // diagonal offsets
    static const int d_offset_x[] = {-1, -1, 1, 1}; 
    static const int d_offset_y[] = {-1, 1, 1, -1};

    // Correspondencies between index and position
    // 
    //     offset              d_offset:      
    //  +---+---+---+        +---+---+---+
    //  |   | 3 |   |        | 0 |   | 3 | 
    //  +---+---+---+        +---+---+---+
    //  | 0 |   | 2 |        |   |   |   |
    //  +---+---+---+        +---+---+---+
    //  |   | 1 |   |        | 1 |   | 2 |
    //  +---+---+---+        +---+---+---+
    // 
    // if offset[i] is invalid, then
    // d_offset[i] and 
    // d_offset[(i + 1) % 4] is invalid.

    // horizontal and vertical directions
    static const Grid::Direction dirs[] = {
        Grid::RIGHT, Grid::UP, Grid::LEFT, Grid::DOWN
    };

    // diagonal directions
    static const Grid::Direction ddirs[] = {
        Grid::BOTTOMRIGHT, Grid::TOPRIGHT, Grid::TOPLEFT, Grid::BOTTOMLEFT
    };


    // Step1: Clear all grids' direction to be Grid::NONE
    //        and mark all grids as un-visited
    clearGridsFlags();

    // Step2: Begin Greedy Search
    std::priority_queue<GreedyNode> pq;
    pq.push(GreedyNode(_target_x, _target_y, 0));
    _visited[_target_y][_target_x] = true;

    while (not pq.empty()) {

        // indicate whether the diagonal direction is available
        bool diagonal_valid[] = {true, true, true, true};

        // take one node from the queue
        GreedyNode node = pq.top();
        pq.pop();

        int x = node.x, y = node.y;
        int dist = node.dist;

        // Inspect the adjacent nodes
        for (size_t i = 0; i < 4; ++i) {

            // Next node's coordinate
            int next_x = x + offset_x[i];
            int next_y = y + offset_y[i];

            if (isValidCoord(next_x, next_y)) {

                if (not _visited[next_y][next_x]) {
                    // Update the grid's direction
                    _grids[next_y][next_x].setDirection(dirs[i]);
                    
                    // Push the node into the queue
                    pq.push(GreedyNode(next_x, next_y, dist + 10));
                    
                    // Mark the grid as visited
                    _visited[next_y][next_x] = true;
                }

            } else {
                // The coordinate is invalid 
                // Thus the corresponding diagonal coordinate is invalid
                diagonal_valid[i] = false;
                diagonal_valid[(i + 1) % 4] = false;
            }
        }

        // Further inspect the diagonally adjacent nodes
        for (size_t i = 0; i < 4; ++i) {

            if (diagonal_valid[i]) {

                int next_x = x + d_offset_x[i];
                int next_y = y + d_offset_y[i];

                if (not _visited[next_y][next_x]) {
                    // Update the grid's direction
                    _grids[next_y][next_x].setDirection(ddirs[i]);

                    // Push the node into the queue
                    pq.push(GreedyNode(next_x, next_y, dist + 14));

                    // Mark the grid as visited
                    _visited[next_y][next_x] = true;
                }
            } 
        }
    }
}
std::string Pathfinder::getAreaName(const sf::Vector2i& coord){
	if (isValidCoord(coord)){
		return m_nodes[coord.x + (coord.y * m_mapWidth)]->area();
	}
	return "noNode";
}