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

/* #define DUALCPU */ /* Smart-Aleck debugging technique - usually passed in via makefile */

#ifndef FALSE
#define FALSE (0!=0)
#define TRUE (0==0)
#endif

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

#include "mdep.h"

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

#include "mdep.c"

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;
  }
  if (parity) {

    Frames++;
#ifdef ALLEGRO
    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 (onscreen_debug) {char msg[80];
      sprintf(msg, "Mousecode = %02x Shields = %02x Mx = %04d  My = %04d  Sound %0d/%0d",
        mousecode, ram[0x81], mx, my, sound_addr, sound_data);
      /* 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);*/
    }
    vsync(); /* remove this and the vsync below to see just how fast the game can run!  (WOW!) */

    blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); 

    if (keypressed()) {
      int k = readkey() & 0xff;
      if (k == '1') startflag = 0;
      if (k == '3') {
        ioSwitches &= ~SW_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! */
      ioSwitches |= SW_COIN; /* Clear coin counter the bodgy way */
    }
    clear(buffer);

    ioInputs = mousecode | fireflag | shieldsflag | startflag /*| coinflag*/;

#else
    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
    if ((slowdown ^= 1) == 0) vsync();
#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 (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"
#include "tailgunr-exec.c"
#endif

static int anyfail = FALSE;

#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;}

int main(int argc, char **argv) {
#include "dispatch.h"  /* Switch label array */
  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;

  debugf = NULL /*fopen("retro.log", "w")*/;

  before = clock();

#ifdef GRAPHICS
  init_graph();
#endif

  bNewFrame = 0;
  sdwGameXSize = SCREEN_W;
  sdwGameYSize = SCREEN_H;
  bFlipX = 0;  bFlipY = 0;     bSwapXY = 0;
  ioInputs = 0xffff;
  ioSwitches = 0xfff0; /* Orig */
  ioSwitches = 0xffff; /* Tweaked */
  ioSwitches &= ( ~(1<<1) ); /* GT */
  bOverlay = 0xff;
  cineSetJMI(0);     /* Use external input */
  cineSetMSize(1);   /* 8K */
  cineSetMonitor(0); /* bi-level */

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

#ifdef DUALCPU
  RCregister_PC = 0;
#endif

  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 */
#ifdef DUALCPU
  for (;;) {
    old_PC = register_PC = RCregister_PC;
    cineExec(); gtExec();
    CHECK(register_A);
    CHECK(register_B);
    CHECK(register_I);
    CHECK(register_J);
    CHECK(register_P);
    CHECK(cmp_old);
    CHECK(cmp_new);
    for (i = 0; i < 256; i++) {
      if (RCram[i] != ram[i]) {
        anyfail = TRUE;
        ERROR("L%04x: RCram[0x%02x] = 0x%04x  ram[0x%02x] = 0x%04x\n", old_PC, i, RCram[i], i, ram[i]);
      }
    }
    if (anyfail) exit(1);
    if (bNewFrame != 0) {
      bNewFrame = 0;
      CinemaClearScreen();
    }
    if (bailOut) bailOut = FALSE;
  }
#else
  for (;;) {
#include "tailgunr-ops.c"
    Lelse:
        /* 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 */
  }
#endif
  exit(0);
  return(0);
}

