#pragma once

#include "bitboard.h"
#include "move.h"
#include "rephash.h"
#include "bittrans.h"
#include "bithistory.h"
#include "biteval.h"
#include "search.h"						// FIXME: dumb: PVMoves!

// use board copying rather than undo (a bit faster for now)
// FIXME: doesn't work due to a bug!
//#define COPY_MOVE

enum bmax_values
{
    bmaxDepth	=	60,					// maximum search depth
    bmaxPly		=	2*bmaxDepth,		// maximum reachable ply
    bmaxPV		=	2*bmaxDepth + 2,	// maximum PV length
    bmaxMoves	=	256,				// maximum number of moves per position (218, according to wikipedia)
    bkillers	=	2,					// number of killer slots
};

class BitSearchStackEntry
{
public:
    BitMove move;						// current move
    BitMove mateKiller;					// matekiller (mate in 1 move) slot
    BitMove killers[bkillers];			// killer slots
    BitMove pv[ bmaxPV ];				// principal variation
#ifdef COPY_MOVE
    BitBoard board;						// board!
#endif

    // returns true if move is a killer move
    bool isKiller( const BitMove &bm ) const;

    BitSearchStackEntry();
};

class BitSearchMode
{
public:
    std::vector< BitMove > searchMoves;		// restrict search to these moves (only if a nonzero vector)
    i32 mateSearch;							// if nonzero, search for mate in x moves
    i32 maxDepth;							// maximum search depth
    i32 maxNodes;							// maximum nodes to search (only if nonzero)
    i32 maxTime;							// maximum time in milliseconds (0 if infinite)
	bool absLimit;							// search limit is absolute and cannot be extended
    bool native;							// native search mode? (=> no_uci!)
    volatile bool ponder;					// pondering?
	u8 multiPV;								// multiPV (default: 1 => no multipv)
    BitSearchMode();
};

class BitMultiPV
{
public:
	BitMove move;
	BitMove ponder;							// ponder move (to report bestmove if available)
	i32 score;
	i32 depth;
	i32 selDepth;
	u64 nodes;
	u64 tbHits;
	u32 nps;
	u32 hashFull;
	u32 time;
	String line;							// line to be sent to gui
};

class GTBWrapper;

class BitSearch
{
protected:
    // add cutoff move to history table
    void addHistory( i32 depth, const BitMove &bm );
    // update killer move for a given ply
    void updateKiller( i32 ply, const BitMove &bm );

	BitMultiPV multiPVData[ bmaxMoves ];	// multiPV data
	i32 multiPVSort[ bmaxMoves ];			// multiPV sort data
	i32 multiPVAlpha;						// root alpha in the case of multiPV search

    i32 maxTime;					// timelimit for current search in ms (0=infinite)
	bool absLimit;					// timelimit is absolute and cannot be extended
	u8 multiPV;						// multiPV value

    i32 startTicks;					// start milisec ticks
    i32 lastReport;					// last reported infostr

    i32 currMoveTicks;				// last reported currmove ticks. currently, currmove is reported each 250 ms to save I/O bandwidth

    i32 ownrh;						// owned rephash flag
    i32 owneval;					// owned evaluator flag

    i32 rvm;						// for root searching!
    BitMove rm[ bmaxMoves ];		// root moves to search
    i32 rs[ bmaxMoves ];			// root move node scores
    i64 rn[ bmaxMoves ];			// root move node counts

    i64 nodeLimit;					// (real)nodelimit (0=none)

    i32 lastRootScore;				// last (ply) root score
    i32 selDepth;					// selective depth (=max_ply_reached)

    // quiescence search
    i32 qsearch( i32 depth, i32 ply = 0, i32 alpha = -bmate, i32 beta = +bmate );
    // regular (pv) search
    i32 isearch( i32 fdepth, i32 ply = 0, i32 alpha = -bmate, i32 beta = +bmate );
    // null window search
    i32 isearchNull( i32 fdepth, i32 ply = 0, i32 alpha = -bmate, bool usenull = 0 );
    // root node search
    i32 isearchRoot( i32 fdepth, i32 alpha, i32 beta );

    // update principal variation
    void updatePV( i32 ply, const BitMove &mv );

    // update principal variation (on hashtable cutoff)
    void updatePVHash( i32 ply, const BitMove &mv );

    // store PV back into transposition table
    void storePV( i32 depth );

    // handle extensions (nullw: null window search flag)
    i32 extend( i32 fdepth, const BitBoard &b, const BitMove &m, bool mateThreat = 0, bool nullw = 0,
                bool isCheck = 0 ) const;

    // simply print current pv (ply 0 only!)
    void sendPVToGUI( i32 depth, i32 score, i32 alpha, i32 beta );

    // root scoring helper
    void setRootScore( const BitMove &bm, i32 score );

    // make_first_move
    void makeFirstRoot( const BitMove &bm, i32 killer = 0 );

    // init stack at ply
    void initPly( i32 ply );

    void nativeCallback( i32 depth, i32 curmove, i32 nummoves, const BitMove &best, const BitMove &cur,
                         i32 scorecp, bool full = 1 );

    // try to reconstruct PV from hash table!
    void reconstructPV();

	// 0 = draw, otherwise mate
	// -1 = failure
	i32 tbProbe( i32 ply, const BitBoard &board );

public:
    BitSearchStackEntry stack[ bmaxPV + 4 ];	// current search stack
    BitTrans trans;					// local transposition table!
    BitEval *evaluator;				// local evaluator
	GTBWrapper *tb;					// tb wrapper

    BitBoard board;					// board
    Board rboard;					// real board (debugging)
	i32 rturn;

    i32 numMoves;
    BitMove moves[ bmaxMoves ];		// root moves
    RepHash *rh;					// rephash reference
    i32 lastScore;					// last score (used for aspiration search)

    BitHistory hist;

    BitSearchMode mode;				// current searchmode

    volatile bool stopSearch;		// external flag used to abort current search

    // statistics:
    i64 nodes;						// nodes searched (total) (including quiescence)
    i64 qnodes;						// qnodes searched (quiescence nodes only)
    i64 evals;						// evals called (total)
    i64 drawCuts;					// fitycounter/rep cuts
	i64 tbHits;						// tablebase hits
    i32 timeLimit;					// timelimit (ticks)

    bool quiet;						// quiet mode (don't print anything)

	i32 tbMen;						// <3 => don't use TB; 3..5 valid range to use GTB
	i32 tbUsage;					// 0 = never, 1 = root, 2 = always

    BitSearch( RepHash *rh_ = 0, BitEval *eval_ = 0 );
    ~BitSearch();

    // sort noncap moves using history table
    void sortHistory( i32 vm, BitMove *m ) const;

    // evaluate
    i32 eval( const BitBoard &cb, i32 alpha = -bmate, i32 beta = +bmate );

    // clear history table
    void clearHistory();

    // clear all slots
    void clearSlots();

    // timelimit given in seconds
    i32 search( BitSearchMode sm = BitSearchMode() );

    // advance search one move forward. this will shift killer/pv buffers
    void advance();

    // useSAN: true => SAN, otherwise UCI style
    void getBestLine( String &line, bool useSAN = true );

    // alternate version
    void getBestLine( PVMoves *pv );

    // replace current evaluator with a new one
    // owned: owned_flag (delete when done)
    // returns true on success (evl must not be null!)
    bool setEvaluator( BitEval *evl, bool owned = 1 );

    Move getStdMove( const BitBoard &b, const BitMove &bm ) const;

    // tell the search the we have a ponderhit
    void ponderHit();
};

void boardFromBitBoard( Board &b, const BitBoard &bb );
