//---------------------------------------------------------------------------
// VRAM allocation module
//
// April 1996, Geoffroy Audy - Konami Computer Entertainment, Chicago (KCEC)
//---------------------------------------------------------------------------

#include <sys\types.h>
#include <libetc.h>
#include <libgte.h>
#include <libgpu.h>
#include <rand.h>
#include <libcd.h>
#include <memory.h>
#include "..\include\compile.h"
#include "..\include\db.h"
#include "..\include\alocvram.h"

/*#define  X_START_VRAM     320              // Starting coordinate of the
#define  Y_START_VRAM     0                // available space in VRAM
#define  WIDTH_VRAM       1024 - 320       // Width and height of the
#define  HEIGHT_VRAM      512              // available space in VRAM
#define  X_MIN_UNIT_VRAM  32               // Min. allocatable block in VRAM,
#define  Y_MIN_UNIT_VRAM  32               // in 16-bit pixels unit
#define  X_VRAM_BLOCK     ((WIDTH_VRAM) / X_MIN_UNIT_VRAM) // Nbr of block on
#define  Y_VRAM_BLOCK     (HEIGHT_VRAM / Y_MIN_UNIT_VRAM)  // X and Y axis
#define  MAXHANDLES       50               // 50 handles maximum

#ifndef __point
   #define __point
   typedef struct
   {
      int x;
      int y;
   } point;
#endif */

int VramBlock[X_VRAM_BLOCK][Y_VRAM_BLOCK]; // To which hdle the blk belongs
int AVHandles[MAXHANDLES];                 // 0 = Handle free, -1 = used
int AVCluts[32];                           // 0 = Clut free, -1 = used

//-------------------------------------
// Free previsously allocated VRAM
//-------------------------------------
// handle is the handle # of the VRAM
// to be freed
//-------------------------------------
int FreeVram (int handle)
{
   int i, j;

   if (handle > MAXHANDLES)
      return (0);

   if (AVHandles[handle])
   {
      for (j = 0; j < Y_VRAM_BLOCK; j++)
      {
         for (i = 0; i < X_VRAM_BLOCK; i++)
         {
            if (VramBlock[i][j] == handle)
               VramBlock[i][j] = 0;
         }
      }

      AVHandles[handle] = 0;
      return (-1);
   }
   else
      return (0);
}

//-------------------------------------
// Clear all the VRAM allocation module
//-------------------------------------
void InitAllocVram (void)
{
   int i, j;
   u_short pal[256];

   for (j = 0; j < Y_VRAM_BLOCK; j++)   // Clear all the VRAM blocks map
      for (i = 0; i < X_VRAM_BLOCK; i++)
         VramBlock[i][j] = 0;

   for (i = 0; i < MAXHANDLES; i++)     // Reinitialize all the VRAM handles
      AVHandles[i] = 0;

   for (i = 0; i < 32; i++)
      AVCluts[i] = 0;

   AVCluts[31] = 1;
   pal[0] = 0x0;
   for (i = 1; i < 256; i++)
      pal[i] = 0x7FFF;

   RECT rect = {0,511, 256,1};          // Put clut in vram
   LoadImage (&rect, (u_long *)pal);
}

//-------------------------------------
// Allocate blocks and return a handle
// if 0 is returned, the allocation
// failed.
//-------------------------------------
// width and height represents the size
// of the zone needed in VRAM, in
// 16-bit pixels unit. Returns the
// coordinate of the VRAM space allo-
// cated, or (-1,-1) if the allocation
// fails.
//-------------------------------------
point AllocVram (int width, int height, short *hdle, int align)
{
   int xneeded, yneeded;
   int handle;
   int i, j, u, v;
   int alloc;
   point po = {-1, -1};
   int crisse;

   if ((!align) && (width < 112))       // Sometimes, we want the allocated
      crisse = 1;                       // space to be aligned to the 64
   else                                 // pixels boundary
      crisse = 64 / X_MIN_UNIT_VRAM;    //

   handle = 1;                          // Find the next available handle,
   while ((AVHandles[handle] != 0) && (handle < MAXHANDLES)) // if any.
      handle++;
   if (handle == MAXHANDLES)
      return (po);

   xneeded = width / X_MIN_UNIT_VRAM;   // Checks how many blocks we need...
   yneeded = height / Y_MIN_UNIT_VRAM;  //
   if (width % X_MIN_UNIT_VRAM)         //
      xneeded++;                        //
   if (height % Y_MIN_UNIT_VRAM)        //
      yneeded++;                        //

   if ((xneeded > X_VRAM_BLOCK) || (yneeded > Y_VRAM_BLOCK))
      return (po);                      // Allocation fail if too big (-1,-1)

   i = 0;                               // Find an available space in VRAM
   j = 0;
   alloc = 0;
   while ((alloc == 0) && (j <= (Y_VRAM_BLOCK - yneeded)))
   {
      if (((j * Y_MIN_UNIT_VRAM) < 256) && (((j + yneeded) * Y_MIN_UNIT_VRAM) > 256))
         j = 256 / Y_MIN_UNIT_VRAM;

      if (VramBlock[i][j] == 0)
      {
         alloc = 1;
         for (v = j; v < (j + yneeded); v++)
         {
            for (u = i; u < (i + xneeded); u++)
            {
               if (VramBlock[u][v] != 0)
               {
                  alloc = 0;
////////                  break;
               }
            }
         }
      }

      if (alloc == 0)
      {
         i += crisse;
         if (i > (X_VRAM_BLOCK - xneeded))
         {
            i = 0;
            j++;
         }
      }
   }

   if (alloc == 1)                      // If a space has been found...
   {
      AVHandles[handle] = -1;

      for (v = j; v < (j + yneeded); v++)
      {
         for (u = i; u < (i + xneeded); u++)
            VramBlock[u][v] = handle;
      }

      po.x = X_START_VRAM + (i * X_MIN_UNIT_VRAM);
      po.y = Y_START_VRAM + (j * Y_MIN_UNIT_VRAM);
   }

   *hdle = handle;
   return (po);
}

//-------------------------------------
// Write on the screen which vram
// blocks are allocated and which one
// are not
//-------------------------------------
void DumpDataAllocVram (DoubleBuffer *db)
{
   int ii, jj;
   POLY_F4  *plain;

   for (ii = 0; ii < 32; ii++)
   {
      plain = db->Get_Plain ();
      setXY4 (plain, 25 + (ii << 2),220,  27 + (ii << 2),220,
                     25 + (ii << 2),222,  27 + (ii << 2),222);
      if (AVCluts[ii] == 0)
         setRGB0 (plain, 0,255,0);
      else
         setRGB0 (plain, 255,0,0);
      db->Add_Plain (plain, 15);
   }

/*   for (ii = 0; ii < Y_VRAM_BLOCK; ii++)
   {
      for (jj = 0; jj < X_VRAM_BLOCK; jj++)
      {
         plain = db->Get_Plain ();
         setXY4 (plain, 220 + (jj << 2),      155 + (ii << 2),
                        220 + (jj << 2) + 2,  155 + (ii << 2),
                        220 + (jj << 2),      155 + (ii << 2) + 2,
                        220 + (jj << 2) + 2,  155 + (ii << 2) + 2);
         if (VramBlock[jj][ii] == 0)
            setRGB0 (plain, 0,255,0);
         else
            setRGB0 (plain, 255,0,0);
         db->Add_Plain (plain, 15);
      }
   } */

//   for (jj = 0; jj < Y_VRAM_BLOCK; jj++)
//   {
//      for (ii = 0; ii < X_VRAM_BLOCK; ii++)
//         FntPrint ("%d", VramBlock[ii][jj]);
//      FntPrint ("\n");
//   }
}

//-------------------------------------
// Allocate a block at a fixed place in
// the VRAM.  if 0 is returned, the al-
// location failed.
//-------------------------------------
// width and height represents the size
// of the zone needed in VRAM, in
// 16-bit pixels unit. Returns the
// coordinate of the VRAM space allo-
// cated, or (-1,-1) if the allocation
// fails.
//-------------------------------------
point FixAllocVram (int coorx, int coory, int width, int height, int *hdle)
{
   int xneeded, yneeded;
   int handle;
   int i, j, u, v;
   int alloc;
   point po = {-1, -1};

   if ((coorx < X_START_VRAM) || (coory < Y_START_VRAM))
      return (po);

   coorx -= X_START_VRAM;
   coory -= Y_START_VRAM;

   handle = 1;                          // Find the next available handle,
   while ((AVHandles[handle] != 0) && (handle < MAXHANDLES)) // if any.
      handle++;
   if (handle == MAXHANDLES)
      return (po);

   i = coorx % X_MIN_UNIT_VRAM;
   width += i;
   coorx -= i;
   i = coory % Y_MIN_UNIT_VRAM;
   height += i;
   coory -= i;

   xneeded = width / X_MIN_UNIT_VRAM;   // Checks how many blocks we need...
   yneeded = height / Y_MIN_UNIT_VRAM;  //
   if (width % X_MIN_UNIT_VRAM)         //
      xneeded++;                        //
   if (height % Y_MIN_UNIT_VRAM)        //
      yneeded++;                        //

   i = coorx / X_MIN_UNIT_VRAM;         //
   j = coory / Y_MIN_UNIT_VRAM;         //

//   if ((xneeded > (X_VRAM_BLOCK - i)) || (yneeded > (Y_VRAM_BLOCK - j)))
//      return (po);                      // Allocation fail if too big (-1,-1)

   alloc = 1;                           // Check if the requested space is
/*   for (v = j; v < (j + yneeded); v++)  // available
   {
      for (u = i; u < (i + xneeded); u++)
      {
         if (VramBlock[u][v] != 0)
            alloc = 0;
      }
   } */
      
   if (alloc == 1)                      // If the space is available...
   {
      AVHandles[handle] = -1;

      for (v = j; v < (j + yneeded); v++)
      {
         for (u = i; u < (i + xneeded); u++)
            VramBlock[u][v] = handle;
      }

      po.x = X_START_VRAM + (i * X_MIN_UNIT_VRAM);
      po.y = Y_START_VRAM + (j * Y_MIN_UNIT_VRAM);
   }

   *hdle = handle;
   return (po);
}

//-------------------------------------
// Get a clut
//-------------------------------------
// Returns the clut number, or -1 if
// no more clut available
//-------------------------------------
int AllocClut ()
{
   int i = 0;

   while ((i < 31) && (AVCluts[i] != 0))
      i++;

   if (i == 31)
      return (-1);

   AVCluts[i] = 1;
   return (i);
}

//-------------------------------------
// Free a clut.
//-------------------------------------
// clut is the clut number to be freed.
// Returns 0 if free failed, -1 other-
// wise
//-------------------------------------
int FreeClut (int clut)
{
   if (AVCluts[clut] == 0)
      return (0);

   AVCluts[clut] = 0;
   return (-1);
}
