//
// Created by sebastian on 05.08.26.
//

#include "LudoRules.h"

int ludo::LudoRules::getEntryOffset(PlayerColor color) {
    return static_cast<int>(color) * 10;
}

std::optional<ludo::Destination> ludo::LudoRules::computeDestination(const LudoGameState &state, const Piece &piece, int diceValue) {
    switch (piece.state) {
        case PieceState::AtHome:
            return Destination{PieceState::OnTrack, getEntryOffset(piece.color)};

        case PieceState::OnTrack: {
            int distanceTraveled = (piece.position - getEntryOffset(piece.color) + kTrackLength) % kTrackLength;
            int newDistance = distanceTraveled + diceValue;
            if (newDistance <= kTrackLength - 1) {
                return Destination{PieceState::OnTrack, (piece.position + diceValue) % kTrackLength};
            }
            int homeIndex = newDistance - kTrackLength;
            if (homeIndex > kHomeStretchLength - 1) return std::nullopt; // überzählig
            return Destination{homeIndex == kHomeStretchLength - 1 ? PieceState::Finished : PieceState::InHomeStretch, homeIndex};
        }

        case PieceState::InHomeStretch: {
            int newIndex = piece.position + diceValue;
            if (newIndex > kHomeStretchLength - 1) return std::nullopt; // überzählig
            return Destination{newIndex == kHomeStretchLength - 1 ? PieceState::Finished : PieceState::InHomeStretch, newIndex};
        }

        case PieceState::Finished:
            return std::nullopt; // nie bewegbar
    }
    return std::nullopt;
}

bool ludo::LudoRules::isBlockedByOwnPiece(const LudoGameState &state, PlayerColor owner, PieceState pieceState, int position) {
    for (const auto& piece : state.getPieces()) {
        if (piece.color == owner && piece.state == pieceState && piece.position == position) {
            return true;
        }
    }
    return false;
}

std::optional<int> ludo::LudoRules::wouldCapture(const LudoGameState &state, int pieceIndex, int diceValue) {
    const auto& piece = state.getPieces()[pieceIndex];
    auto dest = LudoRules::computeDestination(state, piece, diceValue);
    if (dest.value().state != PieceState::OnTrack) return std::nullopt;

    for (int i = 0; i < static_cast<int>(state.getPieces().size()); ++i) {
        if (i == pieceIndex) continue;
        const auto& other = state.getPieces()[i];
        if (other.color != piece.color && other.state == PieceState::OnTrack && other.position == dest.value().position) {
            return i;
        }
    }
    return std::nullopt;
}

std::vector<int> ludo::LudoRules::getMoveablePieceIndices(const LudoGameState &state, PlayerColor color, int diceValue) {
    std::vector<int> moveablePieceIndices;
    for (int i = 0; i < state.getPieces().size(); ++i) {
        const Piece& piece = state.getPieces()[i];
        if (piece.color != color) continue;

        Destination dest = LudoRules::computeDestination(state, piece, diceValue).value();

        // Überschuss in der Zielgeraden abfangen: computeDestination liefert bei Überschuss
        // aktuell denselben Zustand zurück, wie unten in computeDestination definiert
        if (piece.state == PieceState::InHomeStretch && dest.position > kHomeStretchLength - 1) continue;

        if (piece.state == PieceState::AtHome && diceValue != 6) {
            continue;
        }

        if (!LudoRules::isBlockedByOwnPiece(state, color, dest.state, dest.position)) {
            moveablePieceIndices.push_back(i);
        }
    }
    return moveablePieceIndices;
}

std::optional<int> ludo::LudoRules::findLowestHomeSlotPiece(const LudoGameState &state, PlayerColor color) {
    for (int i = 0; i < static_cast<int>(state.getPieces().size()); ++i) {
        const auto& piece = state.getPieces()[i];
        if (piece.color == color && piece.state == PieceState::AtHome) {
            return i; // erste Übereinstimmung = kleinster Home-Slot, dank Erzeugungsreihenfolge
        }
    }
    return std::nullopt;
}

ludo::LudoRules::MoveResult ludo::LudoRules::computeMoveResult(const LudoGameState &state, int pieceIndex, int diceValue) {
    const Piece& piece = state.getPieces()[pieceIndex];
    int fromPosition = piece.position;

    Destination destination = computeDestination(state, piece, diceValue).value();

    PieceState startState = piece.state;
    PieceState targetState = destination.state;

    MoveResult result{fromPosition, destination.position, false, -1, targetState, startState};

    if (targetState == PieceState::OnTrack) {
        for (int i = 0; i < static_cast<int>(state.getPieces().size()); ++i) {
            if (i == pieceIndex) continue;
            const Piece& other = state.getPieces()[i];
            if (other.color != piece.color && other.state == PieceState::OnTrack  && other.position == destination.position) {
                result.captured = true;
                result.capturedPieceIndex = i;
                break; // dank Blockade-Regel max. eine gegnerische Figur pro Feld
            }
        }
    }

    return result;
}
