#include "hash.h"

UI64 makehashkey(){
	UI64 key1 = 0;
	int i = A1;
	for(;i < nbSquares; ++i, i += (i&8)){
		if(board[i])
			key1 ^= zobrist[board[i]][i];
	}
	if(stm == BLACK) key1 = ~key1;
	key1 ^= zobrist[9][A1|castle];
	key1 ^= zobrist[8][epsquare];
	return key1;
}

sEHASHENTRY * eprobe(void){
	UI64 key1 = stm? key : ~key;
	sEHASHENTRY * h = ehashtable + (key1 & (ehashsize-1));
	assert(key == makehashkey());

	if(h->lock == (unsigned int)(key1 >> 32))
		return h;
	return 0;
}

void estore(int s)
{
	UI64 key1 = stm? key : ~key;
	sEHASHENTRY * h = ehashtable + (key1 & (ehashsize-1));
	assert(key == makehashkey());

	h->lock = (unsigned int)(key1 >> 32);
	h->score = s;
}

void eclear(void)
{
	int i;
	for(i=0; i< ehashsize; ++i)
	{
		ehashtable[i].lock = 0;
	}
}


sHASHENTRY * probe(void){

	sHASHENTRY * h = hashtable + (key & (hashsize-2));
	assert(key == makehashkey());

	if(h->lock == (unsigned int)(key >> 32)){
		
		
		
		return h;
	}

	++h;
	if(h->lock == (unsigned int)(key >> 32)){
		
		
		
		return h;
	}

	return 0;
}

/*
	Notes on hashing:

	threat = IN_CHECK=1, MATE_THREAT=2, ONE_REPLY=4, EMPTY=8, RECAP=16, REAL=32
	
	only mate, stalemate, and null-moves which fail-high have NO move to
	store in hash. All else should.	if by chance we're storing a NM FH, we keep old best move


*/
void store(int type, int draft, int value, int best_move, int threat){

	sHASHENTRY * h = hashtable + (key & (hashsize-2));
	int s;
	unsigned int lock;
	/*
	uMOVE m1, m2;
	*/
	
	assert(key == makehashkey());
	assert(ply >= 0 && ply < MAXPLY);
	assert(value >= -MATE && value <= MATE);
	assert(draft >= 0 && draft < 64);

	/*
		Why is a FL or FH _NOT_ an 'exact' score? A. The score could be better or worse.
		But with mate-distance-pruning, better mates dont matter - most of the time
	*/
	if(value > MATE-MAXPLY){
		draft = MAXPLY-1-(MATE-value);  
		value += ply; 
		
	}
	else if(value < -MATE+MAXPLY){
		draft = MAXPLY-1-(MATE+value); 
		value -= ply; 
		
		
	}

	
	
	
	/*
	Q. How many PV [EXACT_PV] Fail later? 
	These are unique positions which are misinterpreted by search
	*/

	
	assert(type >= 0 && type < 4);
	assert(threat >=0 && threat <= 255);

	lock = (unsigned int)(key >> 32);

	

	if(h->lock == lock){
		if(draft >= (int)(h->data & 63)) {
			
			
			//if(best_move) 
				h->best_move = best_move;
			h->data = (unsigned int)((threat << 16) | (age << 8) | (type << 6) | draft);
			h->value = value;
		}
		
		/*
		else
		{
			if(best_move)
			{
				if(h->best_move == 0)
				{
					h->best_move = best_move;
					
				}
			}
		}
		*/
		
		return;
	}

	++h;

	if(h->lock == lock){
		if(draft >= (int)(h->data & 63)) {
			
			
			//if(best_move) 
				h->best_move = best_move;
			h->data = (unsigned int)((threat << 16) | (age << 8) | (type << 6) | draft);
			h->value = value;
		}
		
		/*
		else
		{
			if(best_move)
			{
				if(h->best_move == 0)
				{
					h->best_move = best_move;
					
				}
			}
		}
		*/
		
		return;
	}
	/*
	vsited nodes are 'freshened' so stale nodes have not been used
	
	if we go several searches without using an entry, it 'grows stale'
	So a shallower search should over-write it though it have less 'draft'
	otherwise, deep, useless stores will hang around for ever

	*One or the other is always replaced - it just may _NEVER_ be the deeper
	**Once an entry > 4 searches old, it is likely useless

	***The current canditate is NEW!      
	scoring: winner gets replaced

	note: h points to 2nd entry
	note: +score means replace 2nd entry

	only mated' entry has no move [or null move]
	pv is rare compared to fail low
	anything useful is refreshed each iteration, so 'old' is really pointless

	FIRST               SECOND
	*/

	/*
	m1.asint = (h-1)->best_move;
	m2.asint = h->best_move;
	*/

	s = 24 * (((h-1)->data & 63) - (h->data & 63));                
	s += 8 * ((((h-1)->data >> 8) & 0xff) - ((h->data >> 8) & 0xff));  
	/*
	s += 4 * (((h-1)->best_move != 0) - (h->best_move != 0));      
	s += 2 * (((((h-1)->data >> 6) & 3) == 2) - ((h->data >> 6) & 3) == 2);  
	
	s += 1 * (m1.data.type > CAQ) - (m2.data.type > CAQ);
	*/

	//h -= (sHASHENTRY *)(s <= 0);
	if(s <= 0) --h;
	

	
	
	
	/*
	FIRST               SECOND
	*/
	
	/*
	m1.asint = best_move;
	m2.asint = h->best_move;
	*/

	//s = 4 * (draft - (h->data & 63));                
	///s += 8 * (age - ((h->data >> 8) & 0xff));  
	
	/*
	s += 4 * ((best_move != 0) - (h->best_move != 0));      
	s += 2 * ((type == 2) - ((h->data >> 6) & 3) == 2);  
	
	s += 1 * (m1.data.type > CAQ) - (m2.data.type > CAQ);
	*/

	//if(s < 0) return;

	used += (h->data == 0); 
	
	
	h->best_move = best_move;
	h->lock = lock;
	h->data = (unsigned int)((threat << 16) | (age << 8) | (type << 6) | draft);
	h->value = value;
	
	return;

}


void storeb(int draft, int value){
	sHASHENTRY * h = hashtable + (key & (hashsize-2));
	unsigned int l = key >> 32;
	
	if(h->lock == l);
	else ++h;
	
	h->value += value; 
	h->lock = l;
	h->data = 0;
}

void stuffpv(int m, int draft){
	sHASHENTRY * h = hashtable + (key & (hashsize-2));
	unsigned int l = key >> 32;
	if(h->lock != l)
	{
		++h;
		h->lock = l;
	}
	
	h->best_move = m;
	
}

void clearhash(void){
	sHASHENTRY * i = hashtable;
	for(; i < hashtable + hashsize; ++i){
		i->lock = 0;
		i->data = 0;
		i->value = 0;
		i->best_move = 0;
	}
	age = 0;
	used = 0;
}

int tallyhash(void){
	sHASHENTRY * i = hashtable;
	int c = 0;
	for(; i < hashtable + hashsize; ++i){
		c += (int)(i->lock > 0);
		
	}
	return c;
}

void agehash(){
	++age;
}

void freshen(sHASHENTRY * h){
	h->data &= 0xffff00ff;
	h->data |= (age << 8);
}        

/*
void stuff_pv(int m){
if(pv[m][1].asint){

domove(&pv[m][1]);

*/

