#ifdef OPENGL
#include <stdio.h>
#else
/* Need the header for WinDrawLine or we get a link-time error!  Weird! */
#include <PalmOS.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <GL/glut.h>
#define COLORS 16
GLubyte colors[COLORS][3] = {
    0,0,0,
    1,1,1,
    3,3,3,
    7,7,7,
    15,15,15,
    31,31,31,
    63,63,63,
    127,127,127,
    0+128,0+128,0+128,
    1+128,1+128,1+128,
    3+128,3+128,3+128,
    7+128,7+128,7+128,
    15+128,15+128,15+128,
    31+128,31+128,31+128,
    63+128,63+128,63+128,
    127+128,127+128,127+128,

};


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

#define SCREEN_H 800
#define SCREEN_W 1023

#include "externs.h"
#include "mdep.h"
#include "mdep.c"

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

static int debounce_oneshots = 0, debounce_shields = 0;

void
CinemaClearScreen (void)
{
  static long Frames = 0L;
  int mx = 0, my = 0;

  mousecode = 0;

#ifdef NEVER
  get_mouse_mickeys (&mickeyx, &mickeyy);
  mx = mickeyx;
  my = mickeyy;
#endif

  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 (... fire button is pressed ...) fireflag &= ~IO_FIRE; else fireflag |= IO_FIRE;
     if (... shields button is pressed ...) shieldsflag &= ~IO_SHIELDS; else shieldsflag |= IO_SHIELDS;

     If the start button is pressed, set "startflag = 0;" here
     If the coin button is pressed, set 
     ioSwitches &= ~IO_COIN; / * Clear coin counter the bodgy way * /
     coinflag = 0;
     if the shields button is pressed, set "shieldsflag = IO_SHIELDS;"

     HOWEVER if *nothing* is pressed,       

     startflag = IO_START; / * Bodge.  Clear start button. Allows it to be down for 1/50sec * /

   */

  if (debounce_oneshots > 0) {
    debounce_oneshots -= 1;
    if (debounce_oneshots == 0) {
      startflag = IO_START; fireflag |= IO_FIRE;
    }
  }

  if (debounce_shields > 0) {
    debounce_shields -= 1;
    if (debounce_shields == 0) {
      shieldsflag |= IO_SHIELDS;
    }
  }

  ioInputs =
    (unsigned short) (mousecode | fireflag | shieldsflag | startflag);

  /* CAN WE DO SOMETHING LIKE: vsync(); */

#ifdef DOUBLE_BUFFERING
  blit (buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
  clear (buffer);
#endif

#ifdef OPENGL
  fprintf (stdout, "Clear: %ld [bb: (%ld,%ld) (%ld,%ld)]\n", Frames++,
    MinX, MinY, MaxX, MaxY);
#endif
}

void
CinemaVectorData (int FromX, int FromY, int ToX, int ToY, int vgColour)
{

  /* TWEAK THESE TO MAKE THEM FIT... */

#ifdef NEVER
  FromX = (FromX * 2) / 3;
  ToX = (ToX * 2) / 3;
  FromY = (FromY * 2) / 3;
  ToY = (ToY * 2) / 3;
  FromX -= 32;
  ToX -= 32;
  FromY = SCREEN_H - FromY;
  ToY = SCREEN_H - ToY;		/* Y in inverted on the PC */
#endif

#ifndef OPENGL
  FromX >>= 3;
  ToX >>= 3;
  FromY >>= 3;
  ToY >>= 3;
#endif

#ifdef NEVER
  /* Probably all one intensity on the Palm for now */
  /* HACK */ if (vgColour & 0x8)
    vgColour = 0xf;
  else
    vgColour = 0x7;
#endif

ToX = ToX << 16;
ToX = ToX >> 16;
FromX = FromX << 16;
FromX = FromX >> 16;

  if (FromX < MinX)
    MinX = FromX;
  if (ToX < MinX)
    MinX = ToX;
  if (FromY < MinY)
    MinY = FromY;
  if (ToY < MinY)
    MinY = ToY;
  if (FromX > MaxX)
    MaxX = FromX;
  if (ToX > MaxX)
    MaxX = ToX;
  if (FromY > MaxY)
    MaxY = FromY;
  if (ToY > MaxY)
    MaxY = ToY;

#ifdef OPENGL
  if ((FromX == ToX) && (FromY == ToY)) {
    fprintf (stdout, "Point: %d %d (%d)\n", FromX, FromY, vgColour);
    glColor3ubv(colors[vgColour % COLORS]);
    glVertex2i(FromX, FromY);
    glVertex2i(ToX+1, ToY+1);
  } else {
    fprintf (stdout, "Line: %d %d -> %d %d (%d)\n", FromX, FromY, ToX, ToY, vgColour);
    glColor3ubv(colors[vgColour % COLORS]);
    glVertex2i(FromX, FromY);
    glVertex2i(ToX, ToY);
  }
#else
  WinDrawLine (FromX, FromY, ToX, ToY);
#endif
}

#define UNFINISHED(s)		/* Do Nothing */

/* 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;
}

/* main interpreter body */

extern void cineExecute0000 (void);
extern void cineExecute0400 (void);
extern void cineExecute0800 (void);
extern void cineExecute0c00 (void);
extern void cineExecute1000 (void);
extern void cineExecute1400 (void);
extern void cineExecute1800 (void);
extern void cineExecute1c00 (void);


void
cineExecuteFrame (void)
{
  bNewFrame = 0;
  for (;;)
    {
      switch ((register_PC >> 10))
	{
	case 0:
	  cineExecute0000 ();
	  break;
	case 1:
	  cineExecute0400 ();
	  break;
	case 2:
	  cineExecute0800 ();
	  break;
	case 3:
	  cineExecute0c00 ();
	  break;
	case 4:
	  cineExecute1000 ();
	  break;
	case 5:
	  cineExecute1400 ();
	  break;
	case 6:
	  cineExecute1800 ();
	  break;
	case 7:
	  cineExecute1c00 ();
	  break;
	}
      if (bNewFrame == 1)
	return;
    }
}

void
initTailGunner (void)
{
  int i;
  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 &= (unsigned short) ~SW_QUARTERS_PER_GAME;	/* One quarter per game */
  ioSwitches |= (unsigned short) quarterflag;

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

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

  for (i = 0; i < 256; i++)
    ram[i] = 0;
  return;
}

#ifdef OPENGL

/* 
    qix.c
    Nate Robins, 1997

    An example of a 'qix'-like line demo, but without the traditional
    erase lines, and with anti-aliased lines.

 */




/* #define SCREEN_SAVER_MODE */


typedef struct _point {
    float x, y;
    struct _point* next;
} point;


point* first;
point* second;
int    num_points = 73;
float  first_vx = 0.223487f;
float  first_vy = 0.532978f;
float  second_vx = 0.63257f;
float  second_vy = 0.325897f;
float  vstep = 27.232743f;



void
reshape(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, width, 0, height, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glEnable(GL_BLEND);
    glEnable(GL_LINE_SMOOTH);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void
display(void)
{
    int i;
    static int color = 0;
    static int odd = 0;

    odd = (odd+1)&15;
    if (odd == 0) {
    glClear(GL_COLOR_BUFFER_BIT);
    
    glBegin(GL_LINES);
      cineExecuteFrame ();
    glEnd();

    glutSwapBuffers();
    }
}

void
idle(void)
{
    glutPostRedisplay();
}

void
bail(int code)
{
    int i;
    point* nuke;

    for (i = 0; i < num_points; i++) {
	nuke = first;
	first = first->next;
	free(nuke);
    }
    for (i = 0; i < num_points; i++) {
	nuke = second;
	second = second->next;
	free(nuke);
    }
    exit(code);
}

#ifdef SCREEN_SAVER_MODE
void
ss_keyboard(char key, int x, int y)
{
    bail(0);
}

void
ss_mouse(int button, int state, int x, int y)
{
    bail(0);
}

void
ss_passive(int x, int y)
{
    static int been_here = 0;

    /* for some reason, GLUT sends an initial passive motion callback
       when a window is initialized, so this would immediately
       terminate the program.  to get around this, see if we've been
       here before. (actually if we've been here twice.) */

    if (been_here > 1)
	bail(0);
    been_here++;
}

#else

void
keyboard(unsigned char key, int x, int y)
{
    static int old_x = 50;
    static int old_y = 50;
    static int old_width = SCREEN_W;
    static int old_height = SCREEN_H;

    if (key == 27) {
        bail(0);
    } else if (key == 'w') {
        glutPositionWindow(old_x, old_y);
        glutReshapeWindow(old_width, old_height);
    } else if (key == 'f') {
	if (glutGet(GLUT_WINDOW_WIDTH) != glutGet(GLUT_SCREEN_WIDTH)) {
	    old_x = glutGet(GLUT_WINDOW_X);
	    old_y = glutGet(GLUT_WINDOW_Y);
	    old_width = glutGet(GLUT_WINDOW_WIDTH);
	    old_height = glutGet(GLUT_WINDOW_HEIGHT);
	    glutFullScreen();
	}
    } else if (key == '3') {
      ioSwitches &= ~IO_COIN; /* Clear coin counter the bodgy way */
      coinflag = 0;
    } else if (key == '1') {
      startflag = 0;
      debounce_oneshots = 4;
    } else if (key == '\r') {
      fireflag &= ~IO_FIRE;
      debounce_oneshots = 2;
    } else if (key == '\t') {
      shieldsflag &= ~IO_SHIELDS;
      if (debounce_shields <= 1) debounce_shields = 16;
    }
  /*
     if (... fire button is pressed ...) fireflag &= ~IO_FIRE; else fireflag |= IO_FIRE;
     if (... shields button is pressed ...) shieldsflag &= ~IO_SHIELDS; else shieldsflag |= IO_SHIELDS;

     If the start button is pressed, set "startflag = 0;" here
     If the coin button is pressed, set 
     ioSwitches &= ~IO_COIN; / * Clear coin counter the bodgy way * /
     coinflag = 0;
     if the shields button is pressed, set "shieldsflag = IO_SHIELDS;"

     HOWEVER if *nothing* is pressed,       

     startflag = IO_START; / * Bodge.  Clear start button. Allows it to be down for 1/50sec * /

   */
}
#endif

int
main(int argc, char** argv)
{
    int i;
    point* newborn;

    initTailGunner ();

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(50, 50);
    glutInitWindowSize(SCREEN_W, SCREEN_H);
    glutInit(&argc, argv);

    glutCreateWindow("Tailgunner");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
#ifdef SCREEN_SAVER_MODE
    glutPassiveMotionFunc(ss_passive);
    glutKeyboardFunc(ss_keyboard);
    glutMouseFunc(ss_mouse);
    glutSetCursor(GLUT_CURSOR_NONE);
    glutFullScreen(); 
#else
    glutKeyboardFunc(keyboard);
#endif

    if (argc > 1) {
	if (strcmp(argv[1], "-h") == 0) {
	    fprintf(stderr, "%s [lines] [step]\n", argv[0]);
	    exit(0);
	}
	sscanf(argv[1], "%d", &num_points);
	if (argc > 2)
	    sscanf(argv[2], "%d", &vstep);
    }      

    first_vx *= vstep;
    first_vy *= vstep;
    second_vx *= vstep;
    second_vy *= vstep;

    for (i = 0; i < num_points; i++) {
	newborn = (point*)malloc(sizeof(point));
	newborn->next = first;
	newborn->x = -1.0;
	newborn->y = -1.0;
	first = newborn;
    }

    newborn = first;
    while(newborn->next)
	newborn = newborn->next;
    newborn->next = first;

    first->x = rand() % glutGet(GLUT_WINDOW_WIDTH);
    first->y = rand() % glutGet(GLUT_WINDOW_HEIGHT);

    for (i = 0; i < num_points; i++) {
	newborn = (point*)malloc(sizeof(point));
	newborn->next = second;
	newborn->x = -1.0;
	newborn->y = -1.0;
	second = newborn;
    }

    newborn = second;
    while(newborn->next)
	newborn = newborn->next;
    newborn->next = second;

    second->x = rand() % glutGet(GLUT_WINDOW_WIDTH);
    second->y = rand() % glutGet(GLUT_WINDOW_HEIGHT);

    glutIdleFunc(idle);
    glutMainLoop();
    return 0;
}
#endif

