#include "BeltDragPath.h"

#include <cstddef>

namespace
{
    int signOf(int value)
    {
        if (value > 0) { return 1; }
        if (value < 0) { return -1; }
        return 0;
    }

    // Direction stepping from one tile to an orthogonally adjacent tile.
    Rotation directionBetween(QPoint from, QPoint to)
    {
        const QPoint delta = to - from;
        if (delta.x() > 0) { return Rotation::East; }
        if (delta.x() < 0) { return Rotation::West; }
        if (delta.y() > 0) { return Rotation::South; }
        return Rotation::North;
    }
}

std::vector<BeltPathTile> computeBeltDragPath(QPoint anchor, QPoint cursor,
                                              Rotation orientation)
{
    const bool horizontalFirst =
        (orientation == Rotation::East || orientation == Rotation::West);

    // Build the ordered tile coordinates: first leg along the primary axis to the
    // corner, then the orthogonal leg to the cursor (no duplicated corner tile).
    std::vector<QPoint> coords;
    if (horizontalFirst)
    {
        const int stepX = signOf(cursor.x() - anchor.x());
        for (int x = anchor.x(); ; x += stepX)
        {
            coords.push_back(QPoint(x, anchor.y()));
            if (x == cursor.x() || stepX == 0) { break; }
        }
        const int stepY = signOf(cursor.y() - anchor.y());
        if (stepY != 0)
        {
            for (int y = anchor.y() + stepY; ; y += stepY)
            {
                coords.push_back(QPoint(cursor.x(), y));
                if (y == cursor.y()) { break; }
            }
        }
    }
    else
    {
        const int stepY = signOf(cursor.y() - anchor.y());
        for (int y = anchor.y(); ; y += stepY)
        {
            coords.push_back(QPoint(anchor.x(), y));
            if (y == cursor.y() || stepY == 0) { break; }
        }
        const int stepX = signOf(cursor.x() - anchor.x());
        if (stepX != 0)
        {
            for (int x = anchor.x() + stepX; ; x += stepX)
            {
                coords.push_back(QPoint(x, cursor.y()));
                if (x == cursor.x()) { break; }
            }
        }
    }

    // Assign each tile the direction toward the next tile; the last tile keeps its
    // incoming step direction, and a single-tile path keeps the belt orientation.
    std::vector<BeltPathTile> path;
    path.reserve(coords.size());
    const std::size_t count = coords.size();
    for (std::size_t index = 0; index < count; ++index)
    {
        Rotation rotation;
        if (count == 1)
        {
            rotation = orientation;
        }
        else if (index + 1 < count)
        {
            rotation = directionBetween(coords[index], coords[index + 1]);
        }
        else
        {
            rotation = directionBetween(coords[index - 1], coords[index]);
        }
        path.push_back(BeltPathTile{ coords[index], rotation });
    }
    return path;
}
