#include "ShipLayoutPreview.h"

#include <QPainter>
#include <QPaintEvent>

namespace
{

const int kCellSize = 8;

// Size of the empty placeholder box shown when no schematic is selected.
const int kPlaceholderWidth  = 64;
const int kPlaceholderHeight = 40;

std::vector<std::string> rotateMaskCW(const std::vector<std::string>& grid)
{
    if (grid.empty())
    {
        return {};
    }
    const int srcH = static_cast<int>(grid.size());
    int srcW = 0;
    for (const std::string& row : grid)
    {
        const int w = static_cast<int>(row.size());
        if (w > srcW)
        {
            srcW = w;
        }
    }
    const int dstW = srcH;
    const int dstH = srcW;
    std::vector<std::string> dst(dstH, std::string(dstW, 'X'));
    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]
                                : 'X';
            dst[col][srcH - 1 - row] = ch;
        }
    }
    return dst;
}

std::vector<std::string> rotateMask(const std::vector<std::string>& mask,
                                    Rotation rotation)
{
    int steps = 0;
    switch (rotation)
    {
        case Rotation::East:  steps = 0; break;
        case Rotation::South: steps = 1; break;
        case Rotation::West:  steps = 2; break;
        case Rotation::North: steps = 3; break;
    }
    std::vector<std::string> result = mask;
    for (int i = 0; i < steps; ++i)
    {
        result = rotateMaskCW(result);
    }
    return result;
}

}  // namespace


ShipLayoutPreview::ShipLayoutPreview(QWidget* parent)
    : QWidget(parent)
    , m_modules(nullptr)
    , m_rows(0)
    , m_cols(0)
{
}

void ShipLayoutPreview::clear()
{
    m_grid.clear();
    m_placedModules.clear();
    m_modules = nullptr;
    m_rows = 0;
    m_cols = 0;
    m_placeholder = false;
    setFixedSize(0, 0);
    update();
}

void ShipLayoutPreview::showPlaceholder()
{
    m_grid.clear();
    m_placedModules.clear();
    m_modules = nullptr;
    m_rows = 0;
    m_cols = 0;
    m_placeholder = true;
    setFixedSize(kPlaceholderWidth, kPlaceholderHeight);
    update();
}

void ShipLayoutPreview::setShipAndLayout(const std::vector<std::string>& shipLayout,
                                         const ShipLayoutConfig& layout,
                                         const ModulesConfig* modules)
{
    m_placeholder = false;
    m_modules = modules;
    m_placedModules = layout.placedModules;
    m_rows = static_cast<int>(shipLayout.size());
    m_cols = 0;
    for (const std::string& row : shipLayout)
    {
        const int w = static_cast<int>(row.size());
        if (w > m_cols)
        {
            m_cols = w;
        }
    }

    m_grid.assign(m_rows, std::vector<CellInfo>(m_cols, {false, std::nullopt}));
    for (int r = 0; r < m_rows; ++r)
    {
        for (int c = 0; c < static_cast<int>(shipLayout[r].size()); ++c)
        {
            if (shipLayout[r][c] == 'O')
            {
                m_grid[r][c].buildable = true;
            }
        }
    }

    for (int i = 0; i < static_cast<int>(m_placedModules.size()); ++i)
    {
        const PlacedModule& pm = m_placedModules[i];
        const ModuleDef* def = m_modules->findModuleDef(pm.moduleId);
        if (!def)
        {
            continue;
        }
        const std::vector<std::string> rotated = rotateMask(def->surfaceMask, pm.rotation);
        for (int mr = 0; mr < static_cast<int>(rotated.size()); ++mr)
        {
            for (int mc = 0; mc < static_cast<int>(rotated[mr].size()); ++mc)
            {
                if (rotated[mr][mc] != 'O')
                {
                    continue;
                }
                const int gr = pm.position.y() + mr;
                const int gc = pm.position.x() + mc;
                if (gr >= 0 && gr < m_rows && gc >= 0 && gc < m_cols)
                {
                    m_grid[gr][gc].moduleIndex = i;
                }
            }
        }
    }

    setFixedSize(m_cols * kCellSize, m_rows * kCellSize);
    update();
}

void ShipLayoutPreview::paintEvent(QPaintEvent* /*event*/)
{
    if (m_placeholder)
    {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing, false);
        const QRect box = rect().adjusted(0, 0, -1, -1);
        painter.fillRect(box, QColor(40, 40, 40));
        painter.setPen(QColor(128, 128, 128));
        painter.drawRect(box);
        painter.drawText(box, Qt::AlignCenter, tr("No schematic"));
        return;
    }

    if (m_rows == 0 || m_cols == 0)
    {
        return;
    }

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, false);

    for (int r = 0; r < m_rows; ++r)
    {
        for (int c = 0; c < m_cols; ++c)
        {
            const QRect cellRect(c * kCellSize, r * kCellSize, kCellSize, kCellSize);
            const CellInfo& cell = m_grid[r][c];

            if (!cell.buildable)
            {
                painter.fillRect(cellRect, Qt::black);
            }
            else if (cell.moduleIndex.has_value())
            {
                const PlacedModule& pm = m_placedModules[*cell.moduleIndex];
                const ModuleDef* def = m_modules->findModuleDef(pm.moduleId);
                QColor color(Qt::gray);
                if (def)
                {
                    color = QColor(QString::fromStdString(def->fillColor));
                }
                painter.fillRect(cellRect, color);
            }
            else
            {
                painter.fillRect(cellRect, Qt::white);
            }

            painter.setPen(QColor(128, 128, 128));
            painter.drawRect(cellRect);
        }
    }
}
