//---------------------------------------------------------------------------
// Double bufferering class
//---------------------------------------------------------------------------

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

void DumpDataAllocVram (DoubleBuffer *db);

//-------------------------------------
// Clear the drawing buffer
//-------------------------------------
void DoubleBuffer::ClearDrawArea (char r = 0, char g = 0, char b = 0)
{
   Draw[0].r0 = r;
   Draw[0].g0 = g;
   Draw[0].b0 = b;
   Draw[1].r0 = r;
   Draw[1].g0 = g;
   Draw[1].b0 = b;
}

//-------------------------------------
// Constructor
//-------------------------------------
DoubleBuffer::DoubleBuffer (int x1, int y1, int x2, int y2, int w, int h)
{
   int i;
   ResetGraph (0);
   InitGeom ();
   SetGraphDebug (0);
   SetGeomOffset (160, 120);
   SetGeomScreen (512);

   SetDefDispEnv (&Disp[0], x1,y1, w,h);
   SetDefDispEnv (&Disp[1], x2,y2, w,h);

   SetDefDrawEnv (&Draw[0], x2,y2, w,h);
   SetDefDrawEnv (&Draw[1], x1,y1, w,h);

   Draw[0].ofs[0] = 0;
   Draw[0].ofs[1] = 0;
   Draw[1].ofs[0] = x1;
   Draw[1].ofs[1] = y1;

   for (i = 0; i < 2; i++)
   {
      Draw[i].isbg = 1;
      Draw[i].r0 = 0;
      Draw[i].g0 = 0;
      Draw[i].b0 = 240;
      Draw[i].dfe = 0;
      Draw[i].tpage = GetTPage (1, 0, 320,256);
   }

   Index = 0;

   framecnt = framepersec = 0;
   timecnt1 = VSync (-1);

   ClearOTag (OT[0], NumOT);
   ClearOTag (OT[1], NumOT);
   PolyPtr[0] = 0;
   PolyPtr[1] = 0;
   PlainPtr[0] = 0;
   PlainPtr[1] = 0;
   SprPtr[0] = 0;
   SprPtr[1] = 0;
   DrModePtr[0] = 0;
   DrModePtr[1] = 0;

   int j;
   for (i = 0; i < 2; i++)
   {
      for (j = 0; j < NumPolys; j++)
      {
         SetPolyFT4 (&(Poly[i][j]));
         setRGB0 (&(Poly[i][j]), 128,128,128);
         SetSemiTrans (&(Poly[i][j]), 0);
         SetShadeTex (&(Poly[i][j]), 1);
         Poly[i][j].u0 = 0;
         Poly[i][j].u2 = 0;
         Poly[i][j].v0 = 0;
         Poly[i][j].v1 = 0;
      }

      for (j = 0; j < NumSpr; j++)
      {
         SetSprt (&(Spr[i][j]));
         setRGB0 (&(Spr[i][j]),128,128,128);
         SetShadeTex (&(Spr[i][j]), 1);
         SetSemiTrans (&(Spr[i][j]), 0);
      }

      for (j = 0; j < NumPlain; j++)
      {
         SetPolyF4 (&(Plain[i][j]));
         SetShadeTex (&(Plain[i][j]), 1);
         SetSemiTrans (&(Plain[i][j]), 0);
      }

      for (j = 0; j < NumGouraud; j++)
      {
         SetPolyG4 (&(Gouraud[i][j]));
         SetShadeTex (&(Gouraud[i][j]), 1);
         SetSemiTrans (&(Gouraud[i][j]), 0);
      }

   }

   level = 0;

   SetDispMask (1);

   Index = Index ? 0 : 1;
   PolyPtr[Index] = 0;
   PlainPtr[Index] = 0;
   GouraudPtr[Index] = 0;
   SprPtr[Index] = 0;
   DrModePtr[Index] = 0;
   ClearOTag (OT[Index], NumOT);
}

//-------------------------------------
// Add a polygon to the ordering table
//-------------------------------------
// poly = POLY_FT4 pointer
// depth = Depth of the polygon in the
//         image (0 = deepest 15 = top)
//-------------------------------------
void DoubleBuffer::Add_Poly (POLY_FT4 *poly, int depth)
{
   AddPrim (&OT[Index][depth], poly);
}

//-------------------------------------
// Add a plain polygon to the ordering
// table
//-------------------------------------
// plain = POLY_F4 pointer
// depth = Depth of the polygon in the
//         image (0 = deepest 15 = top)
//-------------------------------------
void DoubleBuffer::Add_Plain (POLY_F4 *plain, int depth)
{
   AddPrim (&OT[Index][depth], plain);
}

//-------------------------------------
// Add a sprite to the ordering table
//-------------------------------------
// poly = SPRT pointer
// depth = Depth of the polygon in the
//         image (0 = deepest 15 = top)
//-------------------------------------
void DoubleBuffer::Add_Spr (SPRT *spr, int depth)
{
   AddPrim (&OT[Index][depth], spr);
}

//-------------------------------------
// Add a Poly_G4 to the ordering table
//-------------------------------------
// poly = Poly_G4 pointer
// depth = Depth of the Poly_G4 in the
//         image (0 = deepest 15 = top)
//-------------------------------------
void DoubleBuffer::Add_Gouraud (POLY_G4 *poly, int depth)
{
   AddPrim (&OT[Index][depth], poly);
}

//-------------------------------------
// Add a drmode to the ordering table
//-------------------------------------
void DoubleBuffer::Add_DrMode (DR_MODE *drmode, int depth)
{
   AddPrim (&OT[Index][depth], drmode);
}

//-------------------------------------
// Returns the address of the next
// available polygon
//-------------------------------------
POLY_FT4 *DoubleBuffer::Get_Poly (void)
{
   int x = PolyPtr[Index]++;
   return (&Poly[Index][x]);
}

//-------------------------------------
// Returns the address of the next
// available plain polygon
//-------------------------------------
POLY_F4 *DoubleBuffer::Get_Plain (void)
{
   int x = PlainPtr[Index]++;
   return (&Plain[Index][x]);
}

//-------------------------------------
// Returns the address of the next
// available dr_mode
//-------------------------------------
DR_MODE *DoubleBuffer::Get_DrMode (void)
{
   int x = DrModePtr[Index]++;
   return (&Dr_Mode[Index][x]);
}

//-------------------------------------
// Returns the address of the next
// available Sprite
//-------------------------------------
SPRT *DoubleBuffer::Get_Spr (void)
{
   int x = SprPtr[Index]++;
   return (&Spr[Index][x]);
}

//-------------------------------------
// Returns the address of the next
// available Gouraud shaded polygon
//-------------------------------------
POLY_G4 *DoubleBuffer::Get_Gouraud (void)
{
   int x = GouraudPtr[Index]++;
   return (&Gouraud[Index][x]);
}

//-------------------------------------
// Swap drawing & displaying buffers
//-------------------------------------
void DoubleBuffer::Swap (int debug)
{
   // Calculate the frame rate per second
//   framecnt++;
//   timecnt2 = VSync (-1);
//   if (timecnt2 - timecnt1 >= 60)
//   {
//      timecnt1 = timecnt2;
//      framepersec = framecnt;
//      framecnt = 0;
//   }

   if (debug)
   {
      FntPrint ("Frame/Sec: %d\n", framepersec);
      FntPrint ("total polys %d\n", PolyPtr[Index]);
      FntPrint ("total plains %d\n", PlainPtr[Index]);
      FntPrint ("total sprs %d\n", SprPtr[Index]);
      FntPrint ("total drmodes: %d\n", DrModePtr[Index]);
   }

   DrawSync (0);
   VSync (0);
//   pollhost();
   PutDispEnv (&Disp[Index]);
   PutDrawEnv (&Draw[Index]);

   DrawOTag (OT[Index]);

   if (debug)
      FntFlush (-1);

   Index = Index ? 0 : 1;
   PolyPtr[Index] = 0;
   PlainPtr[Index] = 0;
   GouraudPtr[Index] = 0;
   SprPtr[Index] = 0;
   DrModePtr[Index] = 0;
   ClearOTag (OT[Index], NumOT);
}

//-------------------------------------
//
//-------------------------------------
void DoubleBuffer::FlashScreen (void)
{
   setRGB0 (&Draw[Index], 255,255,255);
   ResetGraph (3);
   DrawSync (0);
   VSync (0);
   PutDispEnv (&Disp[Index]);
   PutDrawEnv (&Draw[Index]);

   Index = Index ? 0 : 1;
   PolyPtr[Index] = 0;
   PlainPtr[Index] = 0;
   GouraudPtr[Index] = 0;
   SprPtr[Index] = 0;
   DrModePtr[Index] = 0;
   ClearOTag (OT[Index], NumOT);
}

//---------------------------------------------------------------------------
DoubleBuffer *dbullshit;

//-------------------------------------
// Set some crap for the dump message
// shit
//-------------------------------------
void SetDumpMessage (DoubleBuffer *db)
{
   dbullshit = db;
}

extern u_long MemoryP;

//-------------------------------------
// Dump a message on the screen.  Can
// be used for debugging...
//-------------------------------------
void DumpMessage (char *message)
{
   dbullshit->Swap (1);
   FntPrint (message);
   FntPrint ("\n");
   FntPrint ("TopMemory: 0x%08x\n", MemoryP);
   dbullshit->Swap (1);
   dbullshit->Swap (1);
}
