#define	LIBQTOOLS_CORE
#define	LIBQDISPLAY_CORE
#include "../include/libqdisplay.h"
#include "../include/libqsys.h"

int frameCounter;
int displayType;

#ifdef CALCULATE_PIXELDRAW
int pixelDraw;
int pixelOverdraw;
#endif

void run_demo(__memBase)
{
  angvec lastCamera =
  {0, 0, 0};
  int flyMode = FALSE;
  int updownHeight, leftrightWidth;
  float updownAngles, leftrightAngles;
  int position = CONTENTS_EMPTY;				/* content */

#ifdef PROFILE
  for (frameCounter = 0; frameCounter < 18; frameCounter++) {
#else
  struct keyEvent inPut;

  for (frameCounter = 0;; frameCounter++) {
#endif
    /*
     * first verify the validity of the display and the renderers informations
     */
    if (localDim.changedBuffer) {				/* changedSize need not implicate changeBuffer */
      InitMultTables(localDim.Width, localDim.Height);
      set_clip_values(localDim.Width, localDim.Height);
      localDim.changedBuffer = FALSE;
      changedAngles = TRUE;
    }
    if (localDim.changedOffset || localDim.changedSize) {
      if (flyMode) {
	cameraAngles.tx = (int)((localDim.X - updownHeight) * updownAngles);
	cameraAngles.tz = (int)((localDim.Y - leftrightWidth) * leftrightAngles);
	changedAngles = TRUE;
      }
      if (localDim.changedSize) {
	updownHeight = (localDim.dtHeight - localDim.Height) / 2;
	leftrightWidth = (localDim.dtWidth - localDim.Width) / 2;
	updownAngles = 90.0 / updownHeight;
	leftrightAngles = 180.0 / leftrightWidth;
      }
      localDim.changedOffset = FALSE;
      localDim.changedSize = FALSE;
    }
    if (localDim.changedDesktopOffset) {
      localDim.changedDesktopOffset = FALSE;
    }
    if (localDim.changedDesktopSize) {
      updownHeight = (localDim.dtHeight - localDim.Height) / 2;
      leftrightWidth = (localDim.dtWidth - localDim.Width) / 2;
      updownAngles = 90.0 / updownHeight;
      leftrightAngles = 180.0 / leftrightWidth;
      localDim.changedDesktopSize = FALSE;
    }
    __bzero(localDim.frameBuffer, localDim.frameSize * localDim.frameBPP);

#ifdef CALCULATE_PIXELDRAW
    __bzero(localDim.frameBuffer, localDim.frameSize * localDim.frameBPP);
    mprintf("draw pixel: %d, overdrawn pixel: %d\n", pixelDraw, pixelOverdraw);
    pixelDraw = pixelOverdraw = 0;
#endif
#ifndef PROFILE
    /*
     * parse inputevents, this is also the only way to communicate 
     * with the user-interface, if the display changes, verify it in
     * that routine
     */
    while (GetKeys(&inPut)) {
      if (inPut.pressed == RAWKEY_NOTHING)
	break;

      switch (inPut.pressed) {
	case RAWKEY_ESCAPE:
	case RAWKEY_q:
	  return;
	  break;

	case RAWKEY_w:
	  displayType = DISPLAY_WIRE;
	  break;
	case RAWKEY_l:
	  displayType = DISPLAY_FLAT;
	  break;
	case RAWKEY_t:
	  displayType = DISPLAY_TEXTURED;
	  break;

	case RAWKEY_NUMPAD_PGDN:
	  cameraAngles.tx += (inPut.qualifier == RAWQUAL_RSHIFT ? 20 : 10);
	  changedAngles = TRUE;
	  break;
	case RAWKEY_NUMPAD_PGUP:
	  cameraAngles.tx -= (inPut.qualifier == RAWQUAL_RSHIFT ? 20 : 10);
	  changedAngles = TRUE;
	  break;
	case RAWKEY_NUMPAD_HOME:
	  currentSpeed[2] += (inPut.qualifier == RAWQUAL_RSHIFT ? 2 * incSpeed : incSpeed);
	  changedLocation = TRUE;
	  break;
	case RAWKEY_NUMPAD_END:
	  currentSpeed[2] -= (inPut.qualifier == RAWQUAL_RSHIFT ? 2 * incSpeed : incSpeed);
	  changedLocation = TRUE;
	  break;
	case RAWKEY_NUMPAD_AWUP:
	  currentSpeed[0] += (inPut.qualifier == RAWQUAL_RSHIFT ? 2 * incSpeed : incSpeed);
	  changedLocation = TRUE;
	  break;
	case RAWKEY_NUMPAD_AWDN:
	  currentSpeed[0] -= (inPut.qualifier == RAWQUAL_RSHIFT ? 2 * incSpeed : incSpeed);
	  changedLocation = TRUE;
	  break;
	case RAWKEY_NUMPAD_AWRIGHT:
	  cameraAngles.tz += (inPut.qualifier == RAWQUAL_RSHIFT ? 20 : 10);
	  changedAngles = TRUE;
	  break;
	case RAWKEY_NUMPAD_AWLEFT:
	  cameraAngles.tz -= (inPut.qualifier == RAWQUAL_RSHIFT ? 20 : 10);
	  changedAngles = TRUE;
	  break;
	case RAWKEY_NUMPAD_PLUS:
	  cameraAngles.ty += (inPut.qualifier == RAWQUAL_RSHIFT ? 20 : 10);
	  changedAngles = TRUE;
	  break;
	case RAWKEY_NUMPAD_MINUS:
	  cameraAngles.ty -= (inPut.qualifier == RAWQUAL_RSHIFT ? 20 : 10);
	  changedAngles = TRUE;
	  break;
	case RAWKEY_f:
	  if ((flyMode ^= TRUE) == TRUE)
	    currentSpeed[0] = currentSpeed[2] = (maxSpeed / 3);
	  else
	    currentSpeed[2] = -(maxSpeed / 3);
	  changedLocation = TRUE;
	  break;
      }
    }
#else
    switch (frameCounter) {
      case 0:
	cameraAngles.tx += 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 1:
	cameraAngles.tx += 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 2:
	cameraAngles.tx += 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 3:
	cameraAngles.tx -= 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 4:
	cameraAngles.tx -= 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 5:
	cameraAngles.tx -= 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 6:
	cameraAngles.tx -= 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 7:
	cameraAngles.tx -= 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 8:
	cameraAngles.tx -= 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 9:
	cameraAngles.tx += 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 10:
	cameraAngles.tx += 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 11:
	cameraAngles.tx += 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 12:
	cameraAngles.tx += 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 13:
	cameraAngles.tx += 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 14:
	cameraAngles.tx += 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 15:
	cameraAngles.tx -= 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 16:
	cameraAngles.tx -= 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
      case 17:
	cameraAngles.tx -= 15;
	cameraAngles.tz += 10;
	changedAngles = TRUE;
	break;
    }
#endif

    /* validate input events */
    if (flyMode) {
      if (cameraAngles.tz - lastCamera.tz) {
	cameraAngles.ty -= (cameraAngles.tz - lastCamera.tz) / 4;
	changedAngles = TRUE;
      }
      currentSpeed[0] += decSpeed;
      changedLocation = TRUE;
    }

    if (changedAngles) {
      if (cameraAngles.tx > 360)
	cameraAngles.tx -= 360;
      else if (cameraAngles.tx < 0)
	cameraAngles.tx += 360;

      if (cameraAngles.ty > 360)
	cameraAngles.ty -= 360;
      else if (cameraAngles.ty < 0)
	cameraAngles.ty += 360;
      if ((cameraAngles.ty > 45) && (cameraAngles.ty < 180))
	cameraAngles.ty = 45;
      else if ((cameraAngles.ty < 315) && (cameraAngles.ty > 180))
	cameraAngles.ty = 315;

      if (cameraAngles.tz > 360)
	cameraAngles.tz -= 360;
      else if (cameraAngles.tz < 0)
	cameraAngles.tz += 360;

      lastCamera.tz = cameraAngles.tz;
    }

    if (changedLocation) {
      if (currentSpeed[0] >= maxSpeed)
	currentSpeed[0] = maxSpeed;
      else if (currentSpeed[0] <= -maxSpeed)
	currentSpeed[0] = -maxSpeed;

      if (currentSpeed[2] >= maxSpeed)
	currentSpeed[2] = maxSpeed;
      else if (currentSpeed[2] <= -maxSpeed)
	currentSpeed[2] = -maxSpeed;

      position = find_contents(bspMem);
      switch (position) {
	case CONTENTS_EMPTY:					/* not possible */
	  break;
	case CONTENTS_SOLID:					/* correct position */
	  break;
	case CONTENTS_WATER:					/* swim */
	  break;
	case CONTENTS_SLIME:					/* damage */
	  break;
	case CONTENTS_LAVA:					/* more damage */
	  break;
	case CONTENTS_SKY:					/* not possible */
	  break;
      }
    }

    updateTimings();						/* liquid, sky */

    /* recalc renderer if nessecary and render and display */
    set_view_info();
    renderWorld(bspMem);
    localDim.frameBuffer = SwapDisplay(localDim.frameBuffer);
  }
}

static bool inited = FALSE;

bool DisplayBSP(__memBase, char *Title, int width, int height, int depth, int display, bool wait) {
  struct entity *plEnt;

  displayType = display;

  bspMem->mapOptions |= MAP_LOADLIGHTS;
  AllocClusters(bspMem, MAP_ENTITIES);
  LoadMapFile(bspMem, bspMem->shared.quake1.dentdata);

  if (!(visibleFaces = (unsigned char *)kmalloc((bspMem->shared.quake1.numfaces + 32) * sizeof(unsigned char))))
    Error(failed_memory, (bspMem->shared.quake1.numfaces + 32) * sizeof(unsigned char), "visible faces");
  if (!(visibleLeafs = (unsigned char *)kmalloc(((bspMem->shared.quake1.numleafs >> 3) + 32) * sizeof(unsigned char))))
    Error(failed_memory, ((bspMem->shared.quake1.numleafs >> 3) + 32) * sizeof(unsigned char), "visible leafs");
  __memset(visibleLeafs, 0xFF, ((bspMem->shared.quake1.numleafs >> 3) + 1) * sizeof(unsigned char));
  if (!(visibleNodes = (unsigned char *)kmalloc(((bspMem->shared.quake1.numnodes >> 3) + 32) * sizeof(unsigned char))))
    Error(failed_memory, ((bspMem->shared.quake1.numnodes >> 3) + 32) * sizeof(unsigned char), "visible nodes");
  __memset(visibleNodes, 0xFF, ((bspMem->shared.quake1.numnodes >> 3) + 1) * sizeof(unsigned char));

  /* find players start-point */
  if ((plEnt = FindEntityWithKeyPair(bspMem, "classname", "info_player_start"))) {
    VectorCopy(plEnt->origin, cameraLocation);
    cameraAngles.tx = 0;
    cameraAngles.ty = 0;
    cameraAngles.tz = plEnt->angle;
    mprintf("Current Pos - [%f] [%f] [%f]\nAngle [%d]\n", cameraLocation[0], cameraLocation[1], cameraLocation[2], cameraAngles.tz);
    changedAngles = TRUE;
    changedLocation = TRUE;
  }

  /* force load of palette and colormap */
  darkness = 63;
  tfree(GetPalette());

  if (cachedColormap && cachedPalette) {
    if(!inited) {
      SetDisplay(OpenDisplay(width, height, depth, cachedPalette));
      OpenKeys();
      __bzero(localDim.frameBuffer, localDim.frameSize * localDim.frameBPP);
      localDim.frameBuffer = SwapDisplay(localDim.frameBuffer);

      inited = TRUE;
    }
    ChangeDisplay(width, height, 0, 0, Title);

#ifdef	DRIVER_8BIT
    if (localDim.frameDepth == 8) {
/*    UpdateDisplay(cachedColormap, 0, 0, 256, 64);               // only 8bit */

      /* setup water                                              // only 8bit */
      waterTransparency = GetTransparency(DENSITY_WATER);
/*    UpdateDisplay(waterTransparency, 0, 0, 256, 256);           // only 8bit */

      /* setup slime                                              // only 8bit */
      slimeTransparency = GetTransparency(DENSITY_SLIME);
/*    UpdateDisplay(slimeTransparency, 0, 0, 256, 256);           // only 8bit */

      /* setup lava                                               // only 8bit */
      lavaTransparency = GetTransparency(DENSITY_LAVA);
/*    UpdateDisplay(lavaTransparency, 0, 0, 256, 256);            // only 8bit */
    }
#endif

    InitSinCosTables();
    InitFaceCache(bspMem);
    setup_default_point_list();
    run_demo(bspMem);
  }

  kfree();

  if(!wait)
    DisplayEnd();
  
  return TRUE;
}

bool DisplayPicture(void *pic, char *Title, int width, int height, int depth, bool wait) {
  bool noContinue = FALSE;
  struct keyEvent inPut;

  if(!inited) {
    SetDisplay(OpenDisplay(width, height, depth, cachedPalette));
    OpenKeys();
    __bzero(localDim.frameBuffer, localDim.frameSize * localDim.frameBPP);
    localDim.frameBuffer = SwapDisplay(localDim.frameBuffer);

    inited = TRUE;
  }
  
  ChangeDisplay(width, height, depth, cachedPalette, Title);
  __bzero(localDim.frameBuffer, localDim.frameSize * localDim.frameBPP);
  UpdateDisplay(pic, 0, 0, width, height);

  while (GetKeys(&inPut)) {
    /* leave display-loop */
    if (inPut.pressed == RAWKEY_ESCAPE) {
      noContinue = TRUE;
      break;
    }
    /* next picture */
    if (inPut.pressed == RAWKEY_SPACE) {
      noContinue = FALSE;
      break;
    }
  }
  
  if(!wait)
    DisplayEnd();
  
  return noContinue;
}

void DisplayEnd(void) {
  if(inited) {
    CloseKeys();
    CloseDisplay();

    inited = FALSE;
  }
}
