#pragma once

#include <array>
#include <cstdint>
#include <utility>

#include "movelist.hpp"

namespace chess {
enum PieceGenType {
    PAWN   = 1,
    KNIGHT = 2,
    BISHOP = 4,
    ROOK   = 8,
    QUEEN  = 16,
    KING   = 32,
};

class Board;

class movegen {
   public:
    enum class MoveGenType : std::uint8_t { ALL, CAPTURE, QUIET };
    enum class GenMode : std::uint8_t { ALL_MOVES, ONE_MOVE_ONLY };

    /**
     * @brief Generates all legal moves for a position.
     * @tparam mt
     * @param movelist
     * @param board
     * @param pieces
     */
    template <MoveGenType mt = MoveGenType::ALL>
    void static legalmoves(Movelist& movelist, const Board& board,
                           int pieces = PieceGenType::PAWN | PieceGenType::KNIGHT | PieceGenType::BISHOP |
                                        PieceGenType::ROOK | PieceGenType::QUEEN | PieceGenType::KING);

    /**
     * @brief Returns true if there are any legal moves for a position.
     * @tparam mt
     * @param board
     * @param pieces
     */
    template <MoveGenType mt = MoveGenType::ALL>
    bool static anylegalmoves(const Board& board, int pieces = PieceGenType::PAWN | PieceGenType::KNIGHT |
                                                               PieceGenType::BISHOP | PieceGenType::ROOK |
                                                               PieceGenType::QUEEN | PieceGenType::KING);

    /**
     * @brief Check if a move is legal from the given position.
     * @param board
     * @param move
     */
    [[nodiscard]] static bool isLegal(const Board& board, const Move move);

   private:
    static auto init_squares_between();
    static const std::array<std::array<Bitboard, 64>, 64> SQUARES_BETWEEN_BB;

    // Generate the checkmask. Returns a bitboard where the attacker path between the king and enemy piece is set.
    template <Color::underlying c>
    [[nodiscard]] static std::pair<Bitboard, int> checkMask(const Board& board, Square sq);

    // Generate the pin mask for horizontal and vertical pins -> PieceType::ROOK
    // Generate the pin mask for diagonal pins. -> PieceType::BISHOP
    // Returns a bitboard where the ray between the king and the pinner is set.
    template <Color::underlying c, PieceType::underlying pt>
    [[nodiscard]] static Bitboard pinMask(const Board& board, Square sq, Bitboard occ_enemy, Bitboard occ_us) noexcept;

    // Returns the squares that are attacked by the enemy
    template <Color::underlying c>
    [[nodiscard]] static Bitboard seenSquares(const Board& board, Bitboard enemy_empty);

    // Generate pawn moves.
    //
    // If gm == GenMode::ONE_MOVE_ONLY, the function only adds onemove to the movelist.
    //
    // Returns true iff gm == GenMode::ONE_MOVE_ONLY and at least one move was added to the movelist.
    //
    // Note that for gm == GenMode::ALL_MOVES, the return value is always false.
    template <Color::underlying c, MoveGenType mt, GenMode gm>
    static bool generatePawnMoves(const Board& board, Movelist& moves, Bitboard pin_d, Bitboard pin_hv,
                                  Bitboard checkmask, Bitboard occ_enemy);

    [[nodiscard]] static std::array<Move, 2> generateEPMove(const Board& board, Bitboard checkmask, Bitboard pin_d,
                                                            Bitboard pawns_lr, Square ep, Color c);

    [[nodiscard]] static Bitboard generateKnightMoves(Square sq);

    [[nodiscard]] static Bitboard generateBishopMoves(Square sq, Bitboard pin_d, Bitboard occ_all);

    [[nodiscard]] static Bitboard generateRookMoves(Square sq, Bitboard pin_hv, Bitboard occ_all);

    [[nodiscard]] static Bitboard generateQueenMoves(Square sq, Bitboard pin_d, Bitboard pin_hv, Bitboard occ_all);

    [[nodiscard]] static Bitboard generateKingMoves(Square sq, Bitboard seen, Bitboard movable_square);

    template <Color::underlying c>
    [[nodiscard]] static Bitboard generateCastleMoves(const Board& board, Square sq, Bitboard seen,
                                                      Bitboard pinHV) noexcept;

    // If gm == GenMode::ONE_MOVE_ONLY, the function only adds onemove to the movelist.
    //
    // Returns true iff gm == GenMode::ONE_MOVE_ONLY and at least one move was added to the movelist.
    //
    // Note that for gm == GenMode::ALL_MOVES, the return value is always false.
    template <GenMode gm, typename T>
    static bool whileBitboardAdd(Movelist& movelist, Bitboard mask, T func);

    template <Color::underlying c, MoveGenType mt, GenMode gm>
    static void legalmoves(Movelist& movelist, const Board& board, int pieces);

    template <Color::underlying c>
    static bool isEpSquareValid(const Board& board, Square ep);

    [[nodiscard]] static Bitboard between(Square sq1, Square sq2) noexcept;

    template <Color::underlying c>
    [[nodiscard]] static bool isLegal(const Board& board, const Move move);

    friend class Board;
};

}  // namespace chess
