#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* PENDING: 
    1) Starting singlecpu nowadays only shows stars, not the text/logo.
       I must have broken something with the most recent optimisations.
       (which I think were all in JPP8???)
 */


/* #define DUALCPU */ /* Smart-Aleck debugging technique - usually passed in via makefile */
/*
    dualcpu is a version which includes both my own translated
    code, and retrocade's emulator.  Instructions are executed
    in lockstep, and the virtual processor is halted if a mis-
    match is found between both copies of any register.  This
    helps me find bugs in my code generation extremely easily.
 */
#ifndef FALSE
#define FALSE (0!=0)
#define TRUE (0==0)
#endif

int tracing = FALSE;
int debug = FALSE;
int onscreen_debug = TRUE;
int beginner = FALSE;

#include "mdep.h"

#include "optimiser.c"

#ifdef ALLEGRO
#define GRAPHICS
#else
#define SCREEN_H 1024
#define SCREEN_W 1280
#endif

#include "mdep.c"

static int LastFrames = 0;
static int FPS;
static int pleasedisplay = TRUE;
static clock_t now = 0, then = 0;
static clock_t nowtick = 0, thentick = 0;

void CinemaClearScreen(void)
{
  static int parity = 0, slowdown = 0;
  static int second = 0;
  int mx, my;

  parity ^= 1;
  second += 1;
  if (second == 120) { /* Regenerate shields when low, slowly. */
    second = 0;
    if (beginner) {
      if (ram[0x81] < 9) ram[0x81] += 1;
#ifdef DUALCPU
      if (RCram[0x81] < 9) RCram[0x81] += 1;
#endif
    }
  }
  if (parity) {

    Frames++;
#ifdef ALLEGRO
    /* Calculate FPS */
    nowtick = clock(); now = nowtick/CLOCKS_PER_SEC;
    if (now != then) {
      then = now;
      FPS = Frames - LastFrames;
      LastFrames = Frames;
    }
#ifdef NEVER
    ioSwitches |= IO_COIN; /* Clear coin after one frame */
    coinflag = IO_COIN;
#endif
    mousecode = 0;
    get_mouse_mickeys(&mickeyx, &mickeyy);
    mx = mickeyx; my = mickeyy;
    mousecode |= IO_LEFT; mousecode |= IO_RIGHT;

    if (mx > xmousethresh) mousecode &= ~IO_RIGHT;
    if ((-mx) > xmousethresh) mousecode &= ~IO_LEFT;

    if (my > ymousethresh) mousecode |= IO_UP; else mousecode &= ~IO_UP;
    if (-my > ymousethresh) mousecode |= IO_DOWN; else mousecode &= ~IO_DOWN;

    if (mouse_b & 1) fireflag &= ~IO_FIRE; else fireflag |= IO_FIRE;
    if (mouse_b & 2) shieldsflag &= ~IO_SHIELDS; else shieldsflag |= IO_SHIELDS;

    if (keypressed()) {
      int k = readkey() & 0xff;
      if (k == '1') startflag = 0;
      if (k == '3') {
        ioSwitches &= ~IO_COIN; /* Clear coin counter the bodgy way */
        coinflag = 0;
      }
      if (k == ' ') shieldsflag = IO_SHIELDS;
	if (k == 27) {
        if (debugf) {
          fprintf(debugf, "Closing due to ESC pressed\n");
          fflush(debugf); fclose(debugf);
        }
        /* Save PC, registers and 256 bytes of Ram!  Reload on restart - keep high-score */
        save_config(); 
        exit(0);
      }
    } else {
      startflag = IO_START; /* Bodge.  1/FPS key debouncing! */
    }

    ioInputs = mousecode | fireflag | shieldsflag | startflag;

    /*if (ccpudebug)*/ if (onscreen_debug) {char msg[80];
      sprintf(msg, "ioInputs = %02x Shields = %02x Mx = %04d  My = %04d  Snd %0d/%0d  FPS %0d",
        ioInputs, ram[0x81], mx, my, sound_addr, sound_data, FPS);
      /* the mouse button RCstate is stored in the variable mouse_b */
      textout(buffer, font, msg, 16, 450, 15);
      /*textout(buffer, font, disassembly, 16, 430, 15);*/
    }
fprintf(stderr, "vsync()\n");
    vsync(); /* remove this and the vsync below to see just how fast the game can run!  (WOW!) */

    if (TRUE || pleasedisplay) {
      blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
      pleasedisplay = FALSE;
    }

#ifdef NEVER /* Only needed in speed testing */
    if ((nowtick>>1) != (thentick>>1)) { /* Upper limit on speed */
      clear(buffer);
      pleasedisplay = TRUE;
      thentick = nowtick;
    }
#else
    clear(buffer);
#endif

#else
    if (ccpudebug) fprintf(stdout, "CLEAR: %d\n", Frames);
#endif
  } else {
    /* Doing every second vsync() here makes the speed match that
       of Retrocade (and I presume the original?) almost exactly. */
#ifdef ALLEGRO
#ifndef DUALCPU
    if ((slowdown ^= 1) == 0) vsync();
#endif
#endif
  }
}

static int MinX=1000, MinY=1000, MaxX=0, MaxY=0;

void CinemaVectorData(int RCFromX, int RCFromY, int RCToX, int RCToY, int vgColour)
{
  /*Remove stars: if ((RCFromX == RCToX) && (RCFromY == RCToY)) return;*/

  RCFromX = (RCFromX * 2) / 3; RCToX = (RCToX * 2) / 3;
  RCFromY = (RCFromY * 2) / 3; RCToY = (RCToY * 2) / 3;
  RCFromX -= 32; RCToX -= 32;
  RCFromY = SCREEN_H-RCFromY; RCToY=SCREEN_H-RCToY;

/* HACK */ if (vgColour & 0x8) vgColour = 0xf; else vgColour = 0x7;


#ifdef ALLEGRO
  if (ccpudebug) if (debugf) fprintf(debugf, "LINE: From (%0d,%0d) To (%0d,%0d) Col %0d\n",
          RCFromX, RCFromY, RCToX, RCToY, vgColour);
#else
//  fprintf(stdout, "LINE: From (%0d,%0d) To (%0d,%0d) Col %0d\n",
//          RCFromX, RCFromY, RCToX, RCToY, vgColour);
#endif

  if (RCFromX < MinX) MinX = RCFromX;
  if (RCToX < MinX) MinX = RCToX;
  if (RCFromY < MinY) MinY = RCFromY;
  if (RCToY < MinY) MinY = RCToY;
  if (RCFromX > MaxX) MaxX = RCFromX;
  if (RCToX > MaxX) MaxX = RCToX;
  if (RCFromY > MaxY) MaxY = RCFromY;
  if (RCToY > MaxY) MaxY = RCToY;
#ifdef ALLEGRO
  line(buffer,RCFromX,RCFromY,RCToX,RCToY,vgColour);
#endif
}

void xUNFINISHED(char *s, int PC)
{
  if (!debugf) {
    debugf = fopen("retro.crash", "w");
  }
  if (debugf) fprintf(debugf, "%04x DANGER: Unimplemented instruction - %s\n",
    PC, s);
  exit(1);
}
#define UNFINISHED(s) xUNFINISHED(s, register_PC)

/* Reset C-CPU registers, flags, etc to default starting values
 */
void cineSetJMI(UINT8 j) {
  ccpu_jmi_dip = j;
}

void cineSetMSize(UINT8 m) {
  ccpu_msize = m;

}

void cineSetMonitor(UINT8 m) {
  ccpu_monitor = m;
}

void cineReleaseTimeslice ( void ) {
  bBailOut = TRUE;
}

/* extra functions */

#ifdef DUALCPU

#include "cinedbg.c"

char disassembly[256];
#include "cineops.c"

/*#define RAWIO*/   /* Used to debug higher-level IO procedures */
#include "tailgunr-exec.c"

#define CHECK(register_A) \
    if (register_A != RC##register_A) \
      {ERROR("L%04x: RC%s = %d  %s = %d\n", old_PC, #register_A, RC##register_A, #register_A, register_A); anyfail = TRUE;}

static int anyfail = FALSE;
#endif

int main(int argc, char **argv) {
#ifndef DUALCPU
#include "dispatch-ops.h"  /* Switch label array */
#endif
  int old_PC, i;
  /* We do read the eprom contents into memory, in case the code is
     pulling data out of the rom, but we do not execute from this copy
     (unless we later discover that there is some self-modifying code
     going on, but that's unlikely in this architecture since it's
     stored in an eprom :-) )
   */


  clock_t before, after;

  /* -- Context information ends. */

  if ((argc > 1) && (strcmp(argv[1], "-b") == 0)) beginner = TRUE;

#ifdef DUALCPU
  debugf = fopen("retro.log", "w");
#else
  debugf = NULL;
#endif

  before = clock();

  init_graph();

  bNewFrame = 0;
  sdwGameXSize = SCREEN_W;
  sdwGameYSize = SCREEN_H;
  bFlipX = 0;  bFlipY = 0;     bSwapXY = 0;
  ioInputs = 0xffff;

  ioSwitches = 0xffff; /* Tweaked  testing bottom bit = quarters/game */
  ioSwitches = (ioSwitches & (~SW_SHIELDMASK)) | SW_SHIELDS; /* GT */

  ioSwitches &= ~SW_QUARTERS_PER_GAME; /* One quarter per game */
  ioSwitches |= quarterflag;

  /*bOverlay = 0xff;*/

  cineSetJMI(0);     /* Use external input */
  cineSetMSize(1);   /* 8K */
  cineSetMonitor(0); /* bi-level */

  CinemaClearScreen(); /* Initialise ioInputs etc before game starts */

  memset(ram, 0, sizeof(ram));

  (void)load_config();

  /* We cannot use gtExec() in a loop on its own the same wasy as cineExec()
     at the moment, because we are not yet correctly updating the PC in that code */

  for (;;) {
    switch (register_PC) {
/*#define RAWIO*/   /* Used to debug higher-level IO procedures */
#include "tailgunr-ops.c"
    default:
        /* Jumping to any illegal address or end of eprom will come here */
        fprintf(stderr, "Error: control was passed to 0x%04x (%d)\n",
          register_PC, register_PC);
        {FILE *errfile = fopen("error.log", "w");
          if (errfile != NULL) {
            fprintf(errfile, "Error: control was passed to 0x%04x (%d)\n",
              register_PC, register_PC);
            fclose(errfile);
          }
        }
        exit(1); /* Illegal instruction */
    }
  }

  exit(0);
  return(0);
}