#include "SurfaceMask.h"

#include <algorithm>
#include <climits>

namespace
{

// Rotate direction character 90° clockwise.
char rotateDirCharCW(char c)
{
    switch (c)
    {
        case '>': return 'v';
        case 'v': return '<';
        case '<': return '^';
        case '^': return '>';
        default:  return c;
    }
}

// Rotate the character grid 90° clockwise.
// Input grid[row][col].  Returns a new grid with swapped dimensions.
std::vector<std::string> rotateCW(const std::vector<std::string>& grid)
{
    if (grid.empty())
    {
        return {};
    }

    const int srcH = static_cast<int>(grid.size());
    // Pad all rows to the same width.
    int srcW = 0;
    for (const std::string& row : grid)
    {
        const int w = static_cast<int>(row.size());
        if (w > srcW)
        {
            srcW = w;
        }
    }

    // After 90° CW: new width = srcH, new height = srcW.
    const int dstW = srcH;
    const int dstH = srcW;
    std::vector<std::string> dst(dstH, std::string(dstW, ' '));

    for (int row = 0; row < srcH; ++row)
    {
        for (int col = 0; col < srcW; ++col)
        {
            const char ch = (col < static_cast<int>(grid[row].size()))
                                ? grid[row][col]
                                : ' ';
            // 90° CW mapping: (col, row) -> (dstCol = srcH-1-row, dstRow = col)
            const int dstCol = srcH - 1 - row;
            const int dstRow = col;
            dst[dstRow][dstCol] = rotateDirCharCW(ch);
        }
    }

    return dst;
}

}  // namespace

ParsedSurfaceMask parseSurfaceMask(const std::vector<std::string>& rows,
                                   Rotation rotation)
{
    // Number of 90° CW steps to apply.
    int cwSteps = 0;
    switch (rotation)
    {
        case Rotation::East:  cwSteps = 0; break;
        case Rotation::South: cwSteps = 1; break;
        case Rotation::West:  cwSteps = 2; break;
        case Rotation::North: cwSteps = 3; break;
    }

    // Apply rotations.
    std::vector<std::string> grid = rows;
    for (int i = 0; i < cwSteps; ++i)
    {
        grid = rotateCW(grid);
    }

    // Scan grid: collect body cells, ship dock cells, and output port indicators.
    std::vector<QPoint> rawBodyCells;
    std::vector<QPoint> rawShipDockCells;
    struct RawPort
    {
        QPoint tile;
        Rotation direction;
    };
    std::vector<RawPort> rawPorts;

    for (int row = 0; row < static_cast<int>(grid.size()); ++row)
    {
        for (int col = 0; col < static_cast<int>(grid[row].size()); ++col)
        {
            const char ch = grid[row][col];
            if (ch == 'A')
            {
                rawBodyCells.push_back(QPoint(col, row));
            }
            else if (ch == 'S')
            {
                rawBodyCells.push_back(QPoint(col, row));
                rawShipDockCells.push_back(QPoint(col, row));
            }
            else if (ch == '>')
            {
                rawPorts.push_back({QPoint(col, row), Rotation::East});
            }
            else if (ch == '<')
            {
                rawPorts.push_back({QPoint(col, row), Rotation::West});
            }
            else if (ch == '^')
            {
                rawPorts.push_back({QPoint(col, row), Rotation::North});
            }
            else if (ch == 'v')
            {
                rawPorts.push_back({QPoint(col, row), Rotation::South});
            }
        }
    }

    // Compute bounding box of body cells and normalize to (0,0).
    int minCol = INT_MAX;
    int minRow = INT_MAX;
    int maxCol = INT_MIN;
    int maxRow = INT_MIN;

    for (const QPoint& pt : rawBodyCells)
    {
        if (pt.x() < minCol) { minCol = pt.x(); }
        if (pt.x() > maxCol) { maxCol = pt.x(); }
        if (pt.y() < minRow) { minRow = pt.y(); }
        if (pt.y() > maxRow) { maxRow = pt.y(); }
    }

    // If there are no body cells, return an empty mask.
    if (rawBodyCells.empty())
    {
        return ParsedSurfaceMask{};
    }

    const QPoint offset(-minCol, -minRow);

    ParsedSurfaceMask result;
    result.footprint = QSize(maxCol - minCol + 1, maxRow - minRow + 1);

    for (const QPoint& pt : rawBodyCells)
    {
        result.bodyCells.push_back(pt + offset);
    }
    for (const QPoint& pt : rawShipDockCells)
    {
        result.shipDockCells.push_back(pt + offset);
    }
    for (const RawPort& rp : rawPorts)
    {
        Port port;
        port.tile = rp.tile + offset;
        port.direction = rp.direction;
        result.outputPorts.push_back(port);
    }

    return result;
}
