/*
 * The port of 'lines_intersect' (from Graphics Gems II) is failing on the
 * Vectrex.  Unfortunately this is a critical component of the Planarity game.
 *
 * By using the same random number generator and the same size of data objects,
 * the vectrex and linux compilations of this program generate identical pairs
 * of lines. By selecting a pair where the result (intersection detection) is
 * visibly incorrect, we can compare the steps in the calculation and determine
 * where the error is.
 */

#ifdef LINUX

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

#include <stdint.h>

#else

#define uint8_t unsigned int

#define int8_t  int

#define int16_t long

#define int32_t long long

#include <vectrex.h>

#define NULL 0

#endif


#ifndef TRUE

#define TRUE (0==0)

#define FALSE (0!=0)

#endif


#ifdef LINUX

static void drawline(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, int dashed) {
  if (dashed) fprintf(stderr, "Intersecting ");
  fprintf(stderr, "line from (%d,%d) to (%d,%d)\n", x1,y1, x2,y2);
} 
#else

static unsigned int dotmask = 0xC3;

#define DRAWING_SCALE 0x80

#define CROSSHAIR_SCALE 0x40


#define set_scale(s) do { VIA_t1_cnt_lo = s; } while (0)


static unsigned char patList[2];
static void drawline_patterned(int y, int x, unsigned char pat)
{
  patList[0]=(unsigned char)y;
  patList[1]=(unsigned char)x;
  *(volatile unsigned char *)0xC829 = pat;
  *(volatile unsigned char *)0xC823 =0;
  Draw_Pat_VL(patList);
}

static void drawline(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, int dashed) {
  long ax1, ay1, dx, dy;
  ax1 = ((long)x1-128L);     ay1 = ((long)y1-128L);
  dx  = ((long)x2-(long)x1); dy  = ((long)y2-(long)y1);
  Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_5F();
  Moveto_d((int)ay1,(int)ax1);

  // id dx is 255, it splits into 127 and 128 - which causes a problem because +128 is not possible

  if (dy == 255 || dx == 255) {
    // 255-unit long lines are split in three, all others are split in two.

    drawline_patterned((int)(dy/3L), (int)(dx/3L), (dashed ? dotmask : 0xFF));
    dy -= dy/3L; dx -= dx/3L;
  }
  if (dy < -128L || dy > 127L || dx < -128L || dx > 127L) {
    drawline_patterned((int)(dy>>1L), (int)(dx>>1L), (dashed ? dotmask : 0xFF));
    // don't care which way it rounds (>>1 or /2) because this picks up the odd bit:

    dy -= dy>>1L; dx -= dx>>1L;
  }
  drawline_patterned((int)dy, (int)dx, (dashed ? dotmask : 0xFF));
}
#endif


static char debug_buffer[64];
void debug_int32(int y, int x, char *var, int32_t i) {
#ifdef LINUX

  fprintf(stderr, "%s: %d\n", var, i);
#else

  char *s = debug_buffer, *startpos, *endpos;
  (void)y; (void)x;
  while (*var != '\0') {
    char c;
    c = *var++;
    *s++ = c;
  }
  *s++ = ':'; *s++ = ' ';
  if (i < 0) *s++ = '-'; else i = -i;
  startpos = s;
  for (;;) {
    int32_t j = i / ((typeof(i))(10));
    char digit = (char)(i - j*((typeof(i))(10)));
    *s++ = '0'-digit;
    i = j;
    if (i == 0) break;
  }
  endpos = s-1;
  //reverse(startpos, s);

  while (startpos < endpos) {
    char t = *startpos;
    *startpos = *endpos;
    *endpos = t;
    startpos += 1; endpos -= 1;
  }
  *s++ = 0x80;
  *s = '\0';
  Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_5F();
  Print_Str_d(y, x, debug_buffer);
#endif

}

void debug_uint8(int y, int x, char *var, uint8_t i) {
#ifdef LINUX

  fprintf(stderr, "%s: %d\n", var, i);
#else

  debug_int32(y, x, var, (int32_t)i);
#endif

}

void debug_int8(int y, int x, char *var, int8_t i) {
#ifdef LINUX

  fprintf(stderr, "%s: %d\n", var, i);
#else

  debug_int32(y, x, var, (int32_t)i);
#endif

}

void debug_int16(int y, int x, char *var, int16_t i) {
#ifdef LINUX

  fprintf(stderr, "%s: %d\n", var, i);
#else

  debug_int32(y, x, var, (int32_t)i);
#endif

}

static uint8_t _x, _a, _b, _c; 
static void initRandom8(unsigned int s1,unsigned int s2,unsigned int s3, unsigned int x0) {
  _x = x0; _a = s1; _b = s2; _c = s3; _x++; _a = (_a^_c^_x); _b = (_b+_a); _c = ((_c+(_b>>1))^_a);
}

static uint8_t random8(void) { // assert returns unsigned value that fits in an int.

  _x++; _a = (_a^_c^_x); _b = (_b+_a); _c = ((_c+(_b>>1))^_a);
  return _c;
}

/* lines_intersect:  AUTHOR: Mukesh Prasad, from Graphics Gems II
 *
 *   This function computes whether two line segments,
 *   respectively joining the input points (x1,y1) -- (x2,y2)
 *   and the input points (x3,y3) -- (x4,y4) intersect.
 *   If the lines intersect, the output variables x, y are
 *   set to coordinates of the point of intersection.
 *
 *   All values are in integers.  The returned value is rounded
 *   to the nearest integer point.
 *
 *   If non-integral grid points are relevant, the function
 *   can easily be transformed by substituting floating point
 *   calculations instead of integer calculations.
 *
 *   Entry
 *        x1, y1,  x2, y2   Coordinates of endpoints of one segment.
 *        x3, y3,  x4, y4   Coordinates of endpoints of other segment.
 *
 *   Exit
 *        x, y              Coordinates of intersection point.
 *
 *   The value returned by the function is one of:
 *
 *        DONT_INTERSECT    0
 *        DO_INTERSECT      1
 *        COLLINEAR         2
 *
 * Error conditions:
 *
 *     Depending upon the possible ranges, and particularly on 16-bit
 *     computers, care should be taken to protect from overflow.
 *
 *     In the following code, 'long' values have been used for this
 *     purpose, instead of 'int'.
 *
 */
// I (gt) believe I have modified this correctly for gcc6809's "-mint8" environment,

// but the end of the game is not being detected which would imply that this call

// is failing.  Looks like a job for the debugger...

#define  DONT_INTERSECT    0

#define  DO_INTERSECT      1

#define  COLLINEAR         2


/**************************************************************
 *                                                            *
 *    NOTE:  The following macro to determine if two numbers  *
 *    have the same sign, is for 2's complement number        *
 *    representation.  It will need to be modified for other  *
 *    number systems.                                         *
 *                                                            *
 **************************************************************/

static int lines_intersect(int16_t x1, int16_t y1,  /* First line segment */
                           int16_t x2, int16_t y2,
                           int16_t x3, int16_t y3,   /* Second line segment */
                           int16_t x4, int16_t y4) {
    int16_t b1, b2;
    int32_t a1, a2, c1, c2; /* Coefficients of line eqns. */
    int32_t r1, r2, r3, r4; /* 'Sign' values */
    int32_t denom;          /* Intermediate values */

// no apparent change in cycles regardless of which macro is used:

//#define SAME_SIGNS( a, b ) (((a)^(b)) >= 0)

#define SAME_SIGNS( a, b )  ((((a)<0) && ((b)<0)) || (((a)>=0) && ((b)>=0)))


    /* Compute a1, b1, c1, where line joining points 1 and 2
     * is "a1 x  +  b1 y  +  c1  =  0".
     */

    a1 = y2 - y1;            //debug_int32(120, -120, "A1", a1);

    b1 = x1 - x2;            //debug_int16(108, -120, "B1", b1);

    c1 = (int32_t)x2 * (int32_t)y1 - (int32_t)x1 * (int32_t)y2;  //debug_int32( 96, -120, "C1", c1);


    /* Compute r3 and r4. */

    r3 = ((int32_t)a1 * (int32_t)x3) + ((int32_t)b1 * (int32_t)y3) + (int32_t)c1; //debug_int32( 84, -120, "R3", r3);

    r4 = ((int32_t)a1 * (int32_t)x4) + ((int32_t)b1 * (int32_t)y4) + (int32_t)c1; //debug_int32( 72, -120, "R4", r4);


    /* Check signs of r3 and r4.  If both point 3 and point 4 lie on
     * same side of line 1, the line segments do not intersect.
     */

    if ( r3 != 0 &&
         r4 != 0 &&
         SAME_SIGNS( r3, r4 ))
        return ( DONT_INTERSECT );

    /* Compute a2, b2, c2 */

    a2 = y4 - y3;                debug_int32( 64, -120, "A2", a2);
    b2 = x3 - x4;                debug_int16( 52, -120, "B2", b2);
    c2 = (int32_t)x4 * (int32_t)y3 - (int32_t)x3 * (int32_t)y4;      debug_int32( 40, -120, "C2", c2);

    /* Compute r1 and r2 */
    r1 = (a2 * (int32_t)x1) + ((int32_t)b2 * (int32_t)y1) + (int32_t)c2; debug_int32( 28, -120, "R1", r1);
    r2 = (a2 * (int32_t)x2) + ((int32_t)b2 * (int32_t)y2) + (int32_t)c2; debug_int32( 16, -120, "R2", r2);

    /* Check signs of r1 and r2.  If both point 1 and point 2 lie
     * on same side of second line segment, the line segments do
     * not intersect.
     */

    if ( r1 != 0 &&
         r2 != 0 &&
         SAME_SIGNS( r1, r2 ))
        return ( DONT_INTERSECT );

    /* Line segments intersect: compute intersection point. */

    denom = a1 * b2 - a2 * b1;
    if ( denom == 0 )
        return ( COLLINEAR );

    return ( DO_INTERSECT ); /* lines_intersect */
#undef SAME_SIGNS

}

int main(void) {
  uint8_t x1,y1,x2,y2,x3,y3,x4,y4;
  int new_data_wanted = TRUE;
  uint8_t LineNo = 0;
  uint8_t PrevBtnState = 0;

#ifdef LINUX

  debug_int32(0,0,"Test", 0);
  debug_int32(0,0,"Test", 2);
  debug_int32(0,0,"Test", 23);
  debug_int32(0,0,"Test", 32767);
  debug_int32(0,0,"Test", 32768);
  debug_int32(0,0,"Test", 0x7FFFFFFF);
  debug_int32(0,0,"Test", -2);
  debug_int32(0,0,"Test", -23);
  debug_int32(0,0,"Test", -32767);
  debug_int32(0,0,"Test", -32768);
  debug_int32(0,0,"Test", ((int32_t)0x80000000));
#endif


  initRandom8(1,2,3,123);

#ifndef LINUX

  Wait_Recal();
  Read_Btns();
#endif

  debug_buffer[0] = 0x80; debug_buffer[1] = '\0';
  for (;;) {
    int Crosses;
    if (new_data_wanted) {
      LineNo += 1;
      x1 = random8(); y1 = random8();
      x2 = random8(); y2 = random8();
      x3 = random8(); y3 = random8();
      x4 = random8(); y4 = random8();
      new_data_wanted = FALSE;
    }
    debug_uint8(-120, -40, "LINE", LineNo);
    Crosses = lines_intersect(x1,y1,x2,y2,x3,y3,x4,y4) == DO_INTERSECT;
    drawline(x1, y1, x2, y2, Crosses);
    drawline(x3, y3, x4, y4, Crosses);
#ifdef LINUX

    {
      int c;
      char *s = debug_buffer;
      while ((*s&0x7f) != '\0') {fputc(*s, stderr); s += 1; }
      fprintf(stderr, "\nNext>");
      for (;;) {
        c = fgetc(stdin);
        if (c == EOF) exit(0);
        if ((c == 'y')||(c == 'j')) new_data_wanted = TRUE; /*i18n ;-) */
        if (c == '\n' && new_data_wanted) break;
        if (c == '\n') fprintf(stderr, "Next>");
      }
      new_data_wanted = TRUE;
    }
#else

    if ((Vec_Btn_State&~PrevBtnState)&1) { // edge only

      new_data_wanted = TRUE;
    }
    PrevBtnState = Vec_Btn_State;
    Wait_Recal();
    Read_Btns();
#endif

  }
  return 0;
}