#pragma once

#include "core/core.h"
#include "piece.h"
#include "move.h"

// move flags
enum move_flags
{
    mfNone			=		0,
    mfCapture		=		1,
    mfEPCapture		=		2,
    mfCastling		=		4
};

typedef struct PMove
{
    u8 from, to;			// square_from, square_to
    u8 val;					// value (for sorting)
    u8 flags;				// flags (bit 0: capture, bit 1: ep capture, bit 2: castling)

    inline bool operator <( const PMove &o )
    {
        return val > o.val;
    }
} PMove;					// pseudolegal move struct

typedef struct MovTab
{
    i8 next;				// next_index (-1 => done)
    i8 skip;				// skip_index (sliders only, -1 => done)
} MovTab;

// [original][current]
extern MovTab qmnew[64][64];
extern MovTab rmnew[64][64];
extern MovTab bmnew[64][64];
extern MovTab nmnew[64][64];	// note: only next used
extern MovTab kmnew[64][64];	// note: only next used
extern u8 kprox[64][64];		// king proximity flag: 0 = not close, 1 = close
// [pos][probe_pos]

typedef u64 posHash;

typedef struct BoardFlags {
    union {
        struct {
            u8 kingPos[2];				// managed king position
            u8 lastMoved;				// last moved piece (255 = none)
            u8 castled:2;				// castled bit flags. bit 0: white, bit 1: black
            u8 ep:6;					// enpassant pawn position (0=illegal=none)
            u8 fiftyCounter;			// fifty move rule counter
            u8 check:1;					// currently in check
        };
        u64 packed;
    };
} BoardFlags;

enum fifty_move_rule_counter_limit {
    fiftyCounterMax = 101				// fifty move rule counter limit
};

enum ValidError
{
    veOk			=		0,			// ok
    vePawn			=		1,			// misplaced pawn(s)
    veManyP			=		2,			// too pany pawns
    veKMiss			=		3,			// one of the kings is missing
    veManyK			=		4,			// too many kings
    veOppCheck		=		5,			// opponent in check
    veBadEP			=		6,			// bad enpassant piece
    veBadCast		=		7,			// bad castling rights for one of the kings
    veBadFifty		=		8			// bad fifty rule counter
};

class Board
{
public:
    union {
        mutable Piece piece[8][8];		// [row][col]
        mutable Piece pieces[64];
    };
    mutable posHash curHash;	// current hash
    mutable BoardFlags flags;	// current flags
    mutable i32 dmat, tmat;		// current material difference/total non-pawn material
    mutable u64 mask;			// used piece mask

    // update incremental stuff
    void update( i32 turn );

    inline Board()
    {
        flags.packed = 0;
        dmat = tmat = 0;
        mask = 0;
        flags.lastMoved = 255;
    }

    // for debugging only!
    bool comparePieces( const Board &o ) const;

    // parse FEN (Forsyth-Edwards Notation) position
    // optionally return turn and move number
    // out: true on success
    bool parseFEN( const char *fen, i32 *turn = 0, i32 *movnum = 0 );
    // get FEN for current position
    void getFEN( char *fen, i32 turn = white ) const;

    bool isDraw() const;	// is drawish position?
    void dump( String *st = 0 ) const;		// dump to stdio, optionally return result in a string
    void reset();			// reset to starting position
    void clear( i32 turn );			// clear board
    posHash hash( i32 turn = white ) const;		// zobrist-hash whole board
    posHash hashPiece( u8 pos ) const;	// zobrist-hash one piece

    // returns true if eval cached for this board and turn...
    // if true => val filled with cached eval_value...
    bool evalCached( i32 turn, i32 &val, i32 *specFlags = 0 ) const;

    // quiescence: if quiescence == true => material-only eval is used
    i32 eval( i32 bonusturn, bool quiescence = 0, u64 *hangMask = 0, i32 *specFlags = 0, i32 alpha = -mate,
              i32 beta = +mate) const;		// full position evaluation
    // returns position of either king
    u8 kingPosition( i32 turn = white ) const;

    // movegen bit flags:
    enum moveGenFlags
    {
        quietOnly	= 1,
        noSort		= 2,
        useMoveHash	= 128
    };
    // returns number of valid moves
    i32 moves( Move *m, i32 turn = white, i32 flags = 0) const;
    // get 'fake' null move (king to same pos)
    Move nullMove( i32 turn = white ) const;
//	// returns true if white/black king in check
//	bool check( i32 turn = white ) const;
    // returns true if king at kx, ky in check; optionally don't checks knights and pawns
    // if distant is true, checks for bishops/rooks/queens
    bool check( u8 pos, bool checkKnightsPawns = true, bool distant = true ) const;
    // returns potential pin mask (because of movegen)
    u64 potPins( u8 kp ) const;
    // rq: rook/queen masks
    // bq: bishop/queen masks
    // returns potential non-knight check mask (because of movegen)
    u64 potChecks( u8 kp, u64 *rq = 0, u64 *bq = 0 ) const;
    // returns true if piece/square at [x,y] attacked by enemy; optionally don't checks knights and pawns
    // if distant is true, checks for bishops/rooks/queens
    bool attacked( u8 pos, i32 turn, bool checkKnightsPawns = true, bool distant = true ) const;
    // do move; optionally create undo buffer[ maxUndo ]
    void move( const Move &m, Undo *undo = 0 );
    // try to perform an uci move (+parse)
    // returns true on success
    bool ucimove( i32 turn, const char *mvtext );
    // undo move using undo buffer
    void undoMove( const Undo *undo );
    // clear enpassant flags
    // optionally create undo buffer[ maxUndo ]
////	void clearEnpassant( Undo *undo = 0 );

    // return current stage in game, 0..100 (0 = opening, 100 = late endgame)
    i32 stage() const;
    // game stage: 0 = opening, 1 = middle, 2 = endgame
    i32 gameStage() const;

    // returns least valuable attacker for who (turn), sq
    // returns attacker square (packed: 0..63) or -1 if none
    i32 LVA( i32 who, i32 sq ) const;

    // fixme: untested. very simple recursive SEE eval...
    i32 SEE( i32 turn, i32 sq, i32 fromsq = -1 ) const;

    // returns capture quality in pawn units (this is commonly known as SEE)
    i32 captureQuality( const Move &tm ) const;

    // returns capture quality in pawn units (this is commonly known as SEE)
    i32 captureQuality( const PMove &tm ) const;

    // returns castling rights for a player
    // 0 = no castling rights
    // otherwise:
    // lo-nybble: long castling rights (1 = A .. 8 = 'H')
    // hi-nybble: short castling rights (1 = A .. 8 = 'H')
    i32 castlingRights( i32 who ) const;

    // validate board
    // returns 0 if ok, otherwise ValidError condition
    // optionally returns position index of where error was detected (-1 => nowhere/unknown)
    i32 validate( i32 turn, i32 *pos = 0 ) const;

    // parse SAN move
    // optionally return move quality description
    // returns non-null (advanced) if ok
    const char *parseSAN( const char *c, i32 turn, Move &outm, String *desc = 0, i32 *NAG = 0 ) const;
};
