#include "bittrans.h"
#include <memory.h>

//#define DISABLE_TT

// BitTrans

BitTrans::BitTrans( i32 sizeMegs ) : entries(0), uaEntries(0), size(0), full(0), searchid(0)
{
    resize( sizeMegs );
}

void BitTrans::clear()
{
    if ( entries )
    {
        memset( entries, 0, sizeof(BitTransEntry) * 4 * size );
    }
    full = 0;
    searchid = 0;
}

// get hash table fullness in permill, 0=0%, 1000=100%
i32 BitTrans::getFullnessPermill() const
{
    if ( !entries )
    {
        return 0;
    }
    i32 res = (i32)((i64)full * 1000 / (4*size));
    return res > 1000 ? 1000 : res;
}

static void *ALIGN64BYTES( void *ptr )
{
    intptr_t iptr = (intptr_t)ptr;
    iptr = (iptr+63) & ~63;
    return (void *)iptr;
}

bool BitTrans::resize( i32 sizeMegs )
{
//	sizeMegs = 1;
    i32 nsz = sizeMegs * 1048576 / (4*sizeof( BitTransEntry ));
    i32 nwsz = 1;
    while ( nwsz < nsz )
    {
        nwsz <<= 1;
    }
    nsz = nwsz;

    if ( nsz == size )
    {
        return 1;
    }
    if ( uaEntries )
    {
        delete[] uaEntries;
        entries = uaEntries = 0;
        size = 0;
    }
    entries = uaEntries = new BitTransEntry[ nsz * 4 + 4 ];
    if ( !entries )
    {
        return 0;
    }
    entries = (BitTransEntry *)ALIGN64BYTES( entries );
    size = nsz;
    clear();
    return 1;
}

BitTrans::~BitTrans()
{
    if ( uaEntries )
    {
        delete[] uaEntries;
    }
}

// returns entry for hash value
BitTransEntry *BitTrans::entry( const u64 &hval ) const
{
#ifdef DISABLE_TT
	return 0;
#endif
	BitTransEntry *e = firstEntry( hval );
    if ( !e )
        return 0;
    i32 i;
    for (i=0; i<4; i++)
    {
        if ( e->hash == hval )
            return e;
        e++;
    }
    return 0;
}

inline BitTransEntry *BitTrans::firstEntry( const u64 &hval ) const
{
    return size ? entries + (( (u32)hval & (size-1) )<<2) : 0;
}

bool BitTrans::store( const u64 &hash, i32 ply, i32 depth, i32 type, i32 value, const BitMove &move )
{
    BitTransEntry *fe = firstEntry( hash );
    if ( !fe )
    {
        return 0;
    }
    BitMove mv( move );
    u8 sid = (searchid << 2) & 0xfc;
    // find an entry to replace
    i32 i;
    BitTransEntry *e = fe;
    for (i=0; i<4; i++)
    {
        if ( !(fe->type & 3) || fe->hash == hash )
        {
            // unused entry found -> replace!
            e = fe;
            if ( !mv.packed )
            {
                mv.packed = fe->move.packed;
            }
            break;
        }
        i32 esid = e->type & 0xfc;			// entry_searchid
        i32 fesid = fe->type & 0xfc;		// cur_entry_searchid
        if ( esid == sid )					// best has same age as current
        {
            if ( fesid != sid || fe->depth < e->depth )		// replace older or less deep
            {
                e = fe;
            }
        }
        else
        {
            if ( fesid != sid && fe->depth < e->depth )		// replace only if also old and less deep
            {
                e = fe;
            }
        }
        fe++;
    }
    /*	if ( e->hash == hash && e->depth > depth && (e->type & 0xfc) == sid )
    	{
    		return 0;	//printf("TTSTORE_BUG!\n");
    	}*/

    if ( (e->type & 3) == bttEmpty || (e->type & 0xfc) != sid )
        full++;

    e->hash = hash;
    e->type = type | sid;
    e->depth = depth;

    if ( value <= -bmate + 1000 || value >= bmate - 1000 )
    {
        // convert to relative value!
        if ( value < 0 )
        {
            value -= ply;
        }
        else
        {
            value += ply;
        }
    }

    e->value = value;
    // new: don't store move for fail-low!
    e->move.packed = /*type == bttAlpha ? 0 :*/ mv.packed;
    return 1;
}

// called when new search is about to begin
void BitTrans::newSearch()
{
    full = 0;
    searchid++;
}
