#include "catch.hpp"

#include <algorithm>
#include <vector>

#include <QPoint>

#include "Port.h"
#include "Rotation.h"
#include "SurfaceMask.h"

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

static bool hasBodyCell(const ParsedSurfaceMask& mask, int x, int y)
{
    const QPoint pt(x, y);
    return std::find(mask.bodyCells.begin(), mask.bodyCells.end(), pt)
           != mask.bodyCells.end();
}

static bool hasOutputPort(const ParsedSurfaceMask& mask, int x, int y, Rotation dir)
{
    for (const Port& port : mask.outputPorts)
    {
        if (port.tile == QPoint(x, y) && port.direction == dir)
        {
            return true;
        }
    }
    return false;
}

static bool hasShipDockCell(const ParsedSurfaceMask& mask, int x, int y)
{
    const QPoint pt(x, y);
    return std::find(mask.shipDockCells.begin(), mask.shipDockCells.end(), pt)
           != mask.shipDockCells.end();
}

// ---------------------------------------------------------------------------
// Belt  ["A>"]  — 1×1 body, East output
// ---------------------------------------------------------------------------

TEST_CASE("SurfaceMask: belt East — 1 body cell, port at (1,0) East", "[surface_mask]")
{
    const ParsedSurfaceMask mask = parseSurfaceMask({"A>"}, Rotation::East);

    REQUIRE(mask.footprint.width()  == 1);
    REQUIRE(mask.footprint.height() == 1);
    REQUIRE(mask.bodyCells.size()   == 1);
    REQUIRE(hasBodyCell(mask, 0, 0));
    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(hasOutputPort(mask, 1, 0, Rotation::East));
}

TEST_CASE("SurfaceMask: belt South — body at (0,0), port at (0,1) South", "[surface_mask]")
{
    const ParsedSurfaceMask mask = parseSurfaceMask({"A>"}, Rotation::South);

    REQUIRE(mask.bodyCells.size()   == 1);
    REQUIRE(hasBodyCell(mask, 0, 0));
    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(hasOutputPort(mask, 0, 1, Rotation::South));
}

TEST_CASE("SurfaceMask: belt West — body at (0,0), port at (-1,0) West", "[surface_mask]")
{
    const ParsedSurfaceMask mask = parseSurfaceMask({"A>"}, Rotation::West);

    REQUIRE(mask.bodyCells.size()   == 1);
    REQUIRE(hasBodyCell(mask, 0, 0));
    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(hasOutputPort(mask, -1, 0, Rotation::West));
}

TEST_CASE("SurfaceMask: belt North — body at (0,0), port at (0,-1) North", "[surface_mask]")
{
    const ParsedSurfaceMask mask = parseSurfaceMask({"A>"}, Rotation::North);

    REQUIRE(mask.bodyCells.size()   == 1);
    REQUIRE(hasBodyCell(mask, 0, 0));
    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(hasOutputPort(mask, 0, -1, Rotation::North));
}

// ---------------------------------------------------------------------------
// Miner  ["AA", "A>"]  — 3 body cells, East output
// ---------------------------------------------------------------------------

TEST_CASE("SurfaceMask: miner East — 3 body cells, port at (1,1) East, footprint 2×2",
          "[surface_mask]")
{
    const ParsedSurfaceMask mask = parseSurfaceMask({"AA", "A>"}, Rotation::East);

    REQUIRE(mask.footprint.width()  == 2);
    REQUIRE(mask.footprint.height() == 2);
    REQUIRE(mask.bodyCells.size()   == 3);
    REQUIRE(hasBodyCell(mask, 0, 0));
    REQUIRE(hasBodyCell(mask, 1, 0));
    REQUIRE(hasBodyCell(mask, 0, 1));
    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(hasOutputPort(mask, 1, 1, Rotation::East));
}

TEST_CASE("SurfaceMask: miner South — port faces South", "[surface_mask]")
{
    const ParsedSurfaceMask mask = parseSurfaceMask({"AA", "A>"}, Rotation::South);

    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(mask.outputPorts[0].direction == Rotation::South);
}

TEST_CASE("SurfaceMask: miner West — port faces West", "[surface_mask]")
{
    const ParsedSurfaceMask mask = parseSurfaceMask({"AA", "A>"}, Rotation::West);

    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(mask.outputPorts[0].direction == Rotation::West);
}

TEST_CASE("SurfaceMask: miner North — port faces North", "[surface_mask]")
{
    const ParsedSurfaceMask mask = parseSurfaceMask({"AA", "A>"}, Rotation::North);

    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(mask.outputPorts[0].direction == Rotation::North);
}

// ---------------------------------------------------------------------------
// Splitter  ["<A>"]  — 1 body cell, West + East outputs
// ---------------------------------------------------------------------------

TEST_CASE("SurfaceMask: splitter East — 1 body cell, ports at (-1,0) West and (1,0) East",
          "[surface_mask]")
{
    const ParsedSurfaceMask mask = parseSurfaceMask({"<A>"}, Rotation::East);

    REQUIRE(mask.footprint.width()  == 1);
    REQUIRE(mask.footprint.height() == 1);
    REQUIRE(mask.bodyCells.size()   == 1);
    REQUIRE(hasBodyCell(mask, 0, 0));
    REQUIRE(mask.outputPorts.size() == 2);
    REQUIRE(hasOutputPort(mask, -1, 0, Rotation::West));
    REQUIRE(hasOutputPort(mask,  1, 0, Rotation::East));
}

// ---------------------------------------------------------------------------
// Shipyard  ["AAAS>", "AAAS "]  — 6 A + 2 S body cells, 1 port
// ---------------------------------------------------------------------------

TEST_CASE("SurfaceMask: shipyard East — 6 A bodyCells, 2 S shipDockCells, port at (4,0) East",
          "[surface_mask]")
{
    const std::vector<std::string> rows = {"AAAS>", "AAAS "};
    const ParsedSurfaceMask mask = parseSurfaceMask(rows, Rotation::East);

    REQUIRE(mask.footprint.width()  == 4);
    REQUIRE(mask.footprint.height() == 2);
    REQUIRE(mask.bodyCells.size()   == 8);  // 6 A + 2 S
    REQUIRE(mask.shipDockCells.size() == 2);
    REQUIRE(hasShipDockCell(mask, 3, 0));
    REQUIRE(hasShipDockCell(mask, 3, 1));
    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(hasOutputPort(mask, 4, 0, Rotation::East));
}

// ---------------------------------------------------------------------------
// Assembler  ["AAA ", "AAA>", "AAA "]  — 9 body cells, East output
// ---------------------------------------------------------------------------

TEST_CASE("SurfaceMask: assembler East — 9 body cells, port at (3,1) East, footprint 3×3",
          "[surface_mask]")
{
    const std::vector<std::string> rows = {"AAA ", "AAA>", "AAA "};
    const ParsedSurfaceMask mask = parseSurfaceMask(rows, Rotation::East);

    REQUIRE(mask.footprint.width()  == 3);
    REQUIRE(mask.footprint.height() == 3);
    REQUIRE(mask.bodyCells.size()   == 9);
    REQUIRE(mask.outputPorts.size() == 1);
    REQUIRE(hasOutputPort(mask, 3, 1, Rotation::East));
}
