#include <Side/Side.hpp>

Side Side::West(0);
Side Side::East(1);
Side Side::South(2);
Side Side::North(3);
Side Side::Bottom(4);
Side Side::Top(5);

static const char* sideNames[] = {
    "West",
    "East",
    "South",
    "North",
    "Bottom",
    "Top",
};

std::array<Side, 6> Side::all{
    Side(0),
    Side(1),
    Side(2),
    Side(3),
    Side(4),
    Side(5),
};

std::array<Side, 2> Side::allX{Side(0), Side(1)};
std::array<Side, 2> Side::allY{Side(2), Side(3)};
std::array<Side, 2> Side::allZ{Side(4), Side(5)};

const char* Side::name() const
{
    return sideNames[asNumber()];
}

Side Side::rotate(Side axis, int turns) const
{
    if (isX() && axis.isX())
    {
        return *this;
    }
    if (isY() && axis.isY())
    {
        return *this;
    }
    if (isZ() && axis.isZ())
    {
        return *this;
    }

    turns = (turns % 4 + 4) % 4;

    static Side rotationTable[6][4] = {
        {Side::Top, Side::Side::South, Side::Bottom, Side::North},
        {Side::Top, Side::North, Side::Bottom, Side::South},
        {Side::Top, Side::East, Side::Bottom, Side::West},
        {Side::Top, Side::West, Side::Bottom, Side::East},
        {Side::North, Side::West, Side::South, Side::East},
        {Side::North, Side::East, Side::South, Side::West},
    };

    Side *row = rotationTable[axis.sidenum];
    auto it = std::find(row, row + 4, *this);

    if (it == row + 4)
    {
        return *this;
    }

    int index = it - row;
    return row[(index + turns) % 4];
}

std::ostream& operator<<(std::ostream &os, Side side)
{
    os << sideNames[side.asNumber()];
    return os;
}