
#include "RobboLito_def.h"
#include "RobboTotalBase.h"

#define TOTAL_CACHE_PER 65536
static uint8 ***TotalBaseCache = NULL;
uint8 *BLOCCO; /* SMP ? */ /* need 1MB+128 for hiperindici */
static volatile uint64 *CACHE_INFO[4]; /* volatile? */
static uint64 TB_CACHE_COUNT = 0;
static MUTEX_TYPE SCOP_PILA_LOCK[1]; /* hold SMP */
static boolean CACHE_INIT = FALSE; /* HACK */

#define SOFT 0
#define HARD 1

int SetTotalBaseCache (int mb)
{
  int i, j;
  mb = 1 << BSR (mb);
  TB_CACHE_COUNT = mb << 2;
  if (!CACHE_INIT)
    InitInitTotalBaseCache (mb);
  for (i = 0; i < 4; i++)
    if (CACHE_INFO[i])
      free (CACHE_INFO[i]);
  for (i = 0; i < 4; i++)
    if (TotalBaseCache[i])
      {
	for (j = 0; j < CURRENT_TOTAL_BASE_CACHE << 2; j++)
	  if (TotalBaseCache[i][j])
	    free (TotalBaseCache[i][j]);
	free (TotalBaseCache[i]);
      }
  CACHE_INFO[0] = malloc (TB_CACHE_COUNT * sizeof (uint64));
  CACHE_INFO[1] = malloc (TB_CACHE_COUNT * sizeof (uint64));
  CACHE_INFO[2] = malloc (TB_CACHE_COUNT * sizeof (uint64));
  CACHE_INFO[3] = malloc (TB_CACHE_COUNT * sizeof (uint64));
  TotalBaseCache[0] = malloc (TB_CACHE_COUNT * sizeof (uint8 *));
  TotalBaseCache[1] = malloc (TB_CACHE_COUNT * sizeof (uint8 *));
  TotalBaseCache[2] = malloc (TB_CACHE_COUNT * sizeof (uint8 *));
  TotalBaseCache[3] = malloc (TB_CACHE_COUNT * sizeof (uint8 *));
  for (j = 0; j < 4; j++)
    for (i = 0; i < TB_CACHE_COUNT; i++)
      TotalBaseCache[j][i] = NULL;
  for (j = 0; j < 4; j++)
    for (i = 0; i < TB_CACHE_COUNT; i++)
      CACHE_INFO[j][i] = 0xffffffff;
  printf ("TotalBaseCache is %dmb + (1mb)\n", mb);
  CURRENT_TOTAL_BASE_CACHE = mb;
  return mb;
}

void InitInitTotalBaseCache (uint64 mb)
{
  int i;
  LOCK_INIT (SCOP_PILA_LOCK);
  TotalBaseCache = malloc (4 * sizeof (uint8 **));
  BLOCCO = malloc (128 + 16 * TOTAL_CACHE_PER); /* for hiperindici */
  for (i = 0; i < 4; i++)
    {
      CACHE_INFO[i] = NULL;
      TotalBaseCache[i] = NULL;
    }
  CACHE_INIT = TRUE;
  SetTotalBaseCache (mb);
}

#define GetCache0(a, b) ( (123 * (a) + 321 * (b) ) & ( TB_CACHE_COUNT - 1) )
#define GetCache1(a, b) ( (179 * (a) + 557 * (b) ) & ( TB_CACHE_COUNT - 1) )
#define GetCache2(a, b) ( (671 * (a) + 409 * (b) ) & ( TB_CACHE_COUNT - 1) )
#define GetCache3(a, b) ( (773 * (a) + 187 * (b) ) & ( TB_CACHE_COUNT - 1) )
#define GetCache(x ,a, b) \
  ((x) >= 2) ? (((x) == 2) ? GetCache2 (a, b) : GetCache3 (a, b)) \
             : (((x) == 1) ? GetCache1 (a, b) : GetCache0 (a, b))
static uint32 espul_conto = 0;
static int fc = 0;
static volatile uint64 CURRENT_XOR = 0;

boolean tot_valu_type
(RobboTotalBase * tb, uint64 PositionCount, int type, uint8 * res)
{
  uint64 n = tb->num;
  uint64 zo = PositionCount / TOTAL_CACHE_PER, ci = (n << 32) | zo, ZO;
  int h[4], e, i;
  uint8* BPTR;
  uint8 u;
  uint64 SAVE_XOR;
 PRIM:
  SAVE_XOR = CURRENT_XOR;
  for (i = 0; i < 4; i++)
    {
      h[i] = GetCache (i, n, zo);
      if (CACHE_INFO[i][h[i]] == ci)
	{
	  u = TotalBaseCache[i][h[i]][PositionCount % TOTAL_CACHE_PER];
	  if (CACHE_INFO[i][h[i]] == ci)
	    {
	      *res = u;
	      return TRUE;
	    }
	}
    }
  if (type != HARD)
    return FALSE;
  LOCK (SCOP_PILA_LOCK);
  if (SAVE_XOR != CURRENT_XOR)
    {
      UNLOCK (SCOP_PILA_LOCK);
      goto PRIM;
    }
  espul_conto++;
  e = -1;
  for (i = 0; i < 4; i++)
    if (CACHE_INFO[i][h[i]] == 0xffffffff)
      e = i;
  if (e == -1)
    {
      e = 0;
      for (i = 0; i < 16; i++)
	e += (espul_conto >> i);
      e &= 3;
    }
  if (!TotalBaseCache[e][h[e]])
    TotalBaseCache[e][h[e]] = malloc (TOTAL_CACHE_PER);
  CACHE_INFO[e][h[e]] = 0xfffeefff; /* invalidate */
  if (tb->BLOCCO_pro == 0x100000) /* 6 pezzo hiperindici */
    {
      ZO = zo & 0xf;
      zo >>= 4;
    }
  if (tb->TYPE == DISCO_COMPRESSO || tb->TYPE == DISCO_HUFFMAN) /* valid */
    {
      uint32 ba = tb->indici[zo];
      sint32 lun = tb->indici[zo + 1] - ba; /* 32-bit plus under Windows ? */
      uint64 f = 0;
      uint64 pr = ba;
      char STR[1024];
      while (zo >= tb->supp_indice[f]) /* all ways, has the -1 (f has 0) */
	f++;                           
      pr += (f << 32);
      if (!tb->Fdata)
	{
	  fc++;
	  if (fc == MASSIMO_FOPEN) /* FULL */
	    {
	      for (i = 0; i < MAX_TABLES; i++)
		if ((TABLE_TOTAL_BASES + i)->Fdata)
		  {
		    fclose ((TABLE_TOTAL_BASES + i)->Fdata);
		    (TABLE_TOTAL_BASES + i)->Fdata = NULL;
		  }
	      fc = 1;
	    }
	  sprintf (STR, "%s%s%s", tb->DIR_NOME,
		   COMPRESSA_PREFISSO + 1, tb->string);
	  tb->Fdata = fopen (STR, "rb");
	}
      fseek (tb->Fdata, pr, SEEK_SET);
      fread (BLOCCO, 1, lun, tb->Fdata);
      if (tb->BLOCCO_pro == 0x100000) /* 6 pezzo hiperindici */
	{
	  BPTR = BLOCCO + ((uint32*) (BLOCCO + (8 * ZO)))[0];
	  lun = ((uint32*) (BLOCCO + (8 * ZO + 4)))[0];
	}
      else
	BPTR = BLOCCO;
      blocco_decompressa (TotalBaseCache[e][h[e]], BPTR, lun, tb->TYPE);
    }
  CACHE_INFO[e][h[e]] = ci; /* ritard a tiempo */
  CURRENT_XOR ^= ci | (((uint64) h[e]) << 44) | (((uint64) (e)) << 62); /* HACK */
  *res = TotalBaseCache[e][h[e]][PositionCount % TOTAL_CACHE_PER];
  UNLOCK (SCOP_PILA_LOCK);
  return TRUE;
}
