//   GreKo chess engine
//   (c) 2002-2021 Vladimir Medvedev <vrm@bk.ru>
//   http://greko.su

#ifndef SEARCH_H
#define SEARCH_H

#include "position.h"
#include "utils.h"

const int MAX_PLY = 256;

struct SearchParams
{
	bool  analysis;

	bool  limitedDepth;
	bool  limitedNodes;
	bool  limitedTime;

	int   limitDepth;
	NODES limitNodes;
	U32   limitTimeHard;
	U32   limitTimeSoft;

	bool  silent;
};
////////////////////////////////////////////////////////////////////////////////

struct SearchResults
{
	Move         bestMove;
	int          depth;
	NODES        nodes;
	vector<Move> pv;
	EVAL         score;
	U32          time;
};
////////////////////////////////////////////////////////////////////////////////

struct SearchContext
{
	int   currentIteration;
	NODES nodes;
	bool  singleReply;
	U32   startTime;
	bool  terminated;

	U8           hashAge;
	int          histTry[64][14];
	int          histSuccess[64][14];
	Move         killers[MAX_PLY + 1];
	Move         mateKillers[MAX_PLY + 1];
	MoveList     mvlists[MAX_PLY + 1];
	vector<Move> pvs[MAX_PLY + 1];
	Move         refutations[MAX_PLY + 1][64][14];
};
////////////////////////////////////////////////////////////////////////////////

extern SearchParams g_searchParams;
extern SearchResults g_searchResults;

void ClearHash();
Move GetRandomMove(Position& pos);
bool IsGameOver(Position& pos, string& result, string& comment);
EVAL SEE(const Position& pos, Move mv);
void SetHashSize(double mb);
void SetStrength(int level);
void StartPerft(Position& pos, int depth);
void StartSearch(const Position& startpos);

enum
{
	HASH_ALPHA = 0,
	HASH_BETA  = 1,
	HASH_EXACT = 2
};

class HashEntry
{
public:
	HashEntry()
	{
		memset(this, 0, sizeof(HashEntry));
	}

	Move GetMove() const { return Move(m_mv); }
	int GetDepth() const { return m_depth; }
	EVAL GetScore(int ply) const
	{
		EVAL score = m_score;
		if (score > CHECKMATE_SCORE - 50 && score <= CHECKMATE_SCORE)
			score -= ply;
		if (score < -CHECKMATE_SCORE + 50 && score >= -CHECKMATE_SCORE)
			score += ply;
		return score;
	}
	U8 GetType() const { return m_type; }
	U8 GetAge() const { return m_age; }

	bool Fits(U64 hash) const { return m_hashLock == (U32)(hash >> 32); }
	void LockHash(U64 hash) { m_hashLock = (U32)(hash >> 32); }

	void SetMove(Move mv) { m_mv = mv.ToInt(); }
	void SetDepth(int depth) { m_depth = (I16)depth; }
	void SetScore(EVAL score, int ply)
	{
		if (score > CHECKMATE_SCORE - 50 && score <= CHECKMATE_SCORE)
			score += ply;
		if (score < -CHECKMATE_SCORE + 50 && score >= CHECKMATE_SCORE)
			score -= ply;
		m_score = score;
	}
	void SetType(U8 type) { m_type = type; }
	void SetAge(U8 age) { m_age = age; }

private:

	U32 m_hashLock;   // 4
	U32 m_mv;         // 4
	I32 m_score;      // 4
	I16 m_depth;      // 2
	U8  m_type;       // 1
	U8  m_age;        // 1
};
////////////////////////////////////////////////////////////////////////////////

#endif
