#include <vectrex.h>
#include "controller.h"

#define int8_t int
#define int16_t long

typedef long fp14; // 16 bits on Vectrex  (fp2.14 not fp18.14)

//----------------------------------------------
//
// 2.14 fixed point sine and cosine tables.
//
// These tables assume that there are 256 angles
// in a circle.
//

#define int2fp(x) ((x) << 14)
#define fp2int(x) ((x) >> 14)

const fp14 qsine[65]  __attribute__ ((section(".text"))) = {
     0,    402,    803,   1205,   1605,   2005,   2404,   2801,
  3196,   3589,   3980,   4369,   4756,   5139,   5519,   5896,
  6269,   6639,   7005,   7366,   7723,   8075,   8423,   8765,
  9102,   9434,   9759,  10079,  10393,  10701,  11002,  11297,
 11585,  11866,  12139,  12406,  12665,  12916,  13159,  13395,
 13622,  13842,  14053,  14255,  14449,  14634,  14810,  14978,
 15136,  15286,  15426,  15557,  15678,  15790,  15892,  15985,
 16069,  16142,  16206,  16260,  16305,  16339,  16364,  16379,
 16384,
};

static inline fp14 hsine(unsigned int8_t x) {
  return x>=64 ? qsine[128-x] : qsine[x];
}

static inline fp14 sine(unsigned int8_t x) {
  return x&0x80 ? -hsine(x&0x7f) : hsine(x);
}

static inline fp14 fpSin(unsigned int8_t x) {
  return sine((unsigned int8_t)(x));
}

static inline fp14 fpCos(unsigned int8_t x) {
  return sine((unsigned int8_t)((x)+64));
}

static inline int8_t sin127(int8_t x) {
  return (int8_t)(sine((unsigned int8_t)x&255U)>>7);
}

static inline int8_t cos127(int8_t x) {
  return (int8_t)(sine(((unsigned int8_t)x+64U)&255U)>>7);
}

static inline fp14 mult_sin(fp14 x, unsigned int8_t angle) {
  long long product;
  product = x * sine(angle);
  product >>= 14L;
  return (fp14)product;
}

static inline fp14 mult_cos(fp14 x, unsigned int8_t angle) {
  long long product;
  angle += 64;
  product = x * sine(angle);
  product >>= 14L;
  return (fp14)product;
}

// fast integer approximation to atan2(x,y) for 6809 - no divides used.
// x,y in range -128:127

static inline unsigned int8_t Map (int quadrant, int angle) {     // angle is 0..64
  if (quadrant == 0)     return (unsigned int8_t)angle;
  if (quadrant == 1)     return (unsigned int8_t)(128L - (long)angle);
  if (quadrant == 2)     return (unsigned int8_t)(256L - angle);
  /*if (quadrant == 3)*/ return (unsigned int8_t)(128L + angle);
}

unsigned int8_t iatan2_256 (int8_t x, int8_t y) {
  // Much faster binary search (compared to original linear skip-chain version)...
  // returns angle in 256 units per circle
  int quadrant = 0;
  if (x > 0) x = -x; else quadrant |= 2;              // make both negative avoids issue with negating -128
  if (y > 0) y = -y; else quadrant |= 1;
  if (y) {
    int16_t x128 = x * 128L;
    if (x128 <= y * 131L) {
      int16_t x64 = x128>>1;
      if (x64 <= y * 160L) {
        int16_t x32 = x64>>1;
        if (x32 <= y * 172L) {
          int16_t x16 = x32>>1;
          if (x16 <= y * 186L) {
            int16_t x8 = x16>>1;
            if (x8 <= y * 217L) {
              int16_t x2 = x8>>2;
              if (x2 <= y * 163L)
                return Map (quadrant, 64);      // tan(64.5)*2  Is this ever reached?
              else
                return Map (quadrant, 63);      // tan(63.5)*2
            } else {
              if (x8 <= y * 130L)
                return Map (quadrant, 62);      // tan(62.5)*8
              else
                return Map (quadrant, 61);      // tan(61.5)*8
            }
          } else {
            if (x32 <= y * 236L) {
              if (x16 <= y * 144L)
                return Map (quadrant, 60);      // tan(60.5)*16
              else
                return Map (quadrant, 59);      // tan(59.5)*16
            } else {
              if (x32 <= y * 199L)
                return Map (quadrant, 58);      // tan(58.5)*32
              else
                return Map (quadrant, 57);      // tan(57.5)*32
            }
          }
        } else {
          if (x64 <= y * 221L) {
            if (x32 <= y * 135L) {
              if (x32 <= y * 151L)
                return Map (quadrant, 56);      // tan(56.5)*32
              else
                return Map (quadrant, 55);      // tan(55.5)*32
            } else {
              if (x64 <= y * 243L)
                return Map (quadrant, 54);      // tan(54.5)*64
              else
                return Map (quadrant, 53);      // tan(53.5)*64
            }
          } else {
            if (x64 <= y * 186L) {
              if (x64 <= y * 202L)
                return Map (quadrant, 52);      // tan(52.5)*64
              else
                return Map (quadrant, 51);      // tan(51.5)*64
            } else {
              if (x64 <= y * 172L)
                return Map (quadrant, 50);      // tan(50.5)*64
              else
                return Map (quadrant, 49);      // tan(49.5)*64
            }
          }
        }
      } else {
        if (x128 <= y * 197L) {
          if (x128 <= y * 247L) {
            if (x64 <= y * 140L) {
              if (x64 <= y * 149L)
                return Map (quadrant, 48);      // tan(48.5)*64
              else
                return Map (quadrant, 47);      // tan(47.5)*64
            } else {
              if (x64 <= y * 131L)
                return Map (quadrant, 46);      // tan(46.5)*64
              else
                return Map (quadrant, 45);      // tan(45.5)*64
            }
          } else {
            if (x128 <= y * 220L) {
              if (x128 <= y * 233L)
                return Map (quadrant, 44);      // tan(44.5)*128
              else
                return Map (quadrant, 43);      // tan(43.5)*128
            } else {
              if (x128 <= y * 208L)
                return Map (quadrant, 42);      // tan(42.5)*128
              else
                return Map (quadrant, 41);      // tan(41.5)*128
            }
          }
        } else {
          if (x128 <= y * 160L) {
            if (x128 <= y * 177L) {
              if (x128 <= y * 187L)
                return Map (quadrant, 40);      // tan(40.5)*128
              else
                return Map (quadrant, 39);      // tan(39.5)*128
            } else {
              if (x128 <= y * 168L)
                return Map (quadrant, 38);      // tan(38.5)*128
              else
                return Map (quadrant, 37);      // tan(37.5)*128
            }
          } else {
            if (x128 <= y * 145L) {
              if (x128 <= y * 152L)
                return Map (quadrant, 36);      // tan(36.5)*128
              else
                return Map (quadrant, 35);      // tan(35.5)*128
            } else {
              if (x128 <= y * 138L)
                return Map (quadrant, 34);      // tan(34.5)*128
              else
                return Map (quadrant, 33);      // tan(33.5)*128
            }
          }
        }
      }
    } else {
      if (x128 <= y * 55L) {
        if (x128 <= y * 88L) {
          if (x128 <= y * 108L) {
            if (x128 <= y * 119L) {
              if (x128 <= y * 125L)
                return Map (quadrant, 32);      // tan(32.5)*128
              else
                return Map (quadrant, 31);      // tan(31.5)*128
            } else {
              if (x128 <= y * 113L)
                return Map (quadrant, 30);      // tan(30.5)*128
              else
                return Map (quadrant, 29);      // tan(29.5)*128
            }
          } else {
            if (x128 <= y * 97L) {
              if (x128 <= y * 102L)
                return Map (quadrant, 28);      // tan(28.5)*128
              else
                return Map (quadrant, 27);      // tan(27.5)*128
            } else {
              if (x128 <= y * 93L)
                return Map (quadrant, 26);      // tan(26.5)*128
              else
                return Map (quadrant, 25);      // tan(25.5)*128
            }
          }
        } else {
          if (x128 <= y * 70L) {
            if (x128 <= y * 79L) {
              if (x128 <= y * 83L)
                return Map (quadrant, 24);      // tan(24.5)*128
              else
                return Map (quadrant, 23);      // tan(23.5)*128
            } else {
              if (x128 <= y * 75L)
                return Map (quadrant, 22);      // tan(22.5)*128
              else
                return Map (quadrant, 21);      // tan(21.5)*128
            }
          } else {
            if (x128 <= y * 62L) {
              if (x128 <= y * 66L)
                return Map (quadrant, 20);      // tan(20.5)*128
              else
                return Map (quadrant, 19);      // tan(19.5)*128
            } else {
              if (x128 <= y * 59L)
                return Map (quadrant, 18);      // tan(18.5)*128
              else
                return Map (quadrant, 17);      // tan(17.5)*128
            }
          }
        }
      } else {
        if (x128 <= y * 27L) {
          if (x128 <= y * 41L) {
            if (x128 <= y * 48L) {
              if (x128 <= y * 51L)
                return Map (quadrant, 16);      // tan(16.5)*128
              else
                return Map (quadrant, 15);      // tan(15.5)*128
            } else {
              if (x128 <= y * 44L)
                return Map (quadrant, 14);      // tan(14.5)*128
              else
                return Map (quadrant, 13);      // tan(13.5)*128
            }
          } else {
            if (x128 <= y * 34L) {
              if (x128 <= y * 37L)
                return Map (quadrant, 12);      // tan(12.5)*128
              else
                return Map (quadrant, 11);      // tan(11.5)*128
            } else {
              if (x128 <= y * 30L)
                return Map (quadrant, 10);      // tan(10.5)*128
              else
                return Map (quadrant, 9);       // tan(9.5)*128
            }
          }
        } else {
          if (x128 <= y * 14L) {
            if (x128 <= y * 21L) {
              if (x128 <= y * 24L)
                return Map (quadrant, 8);       // tan(8.5)*128
              else
                return Map (quadrant, 7);       // tan(7.5)*128
            } else {
              if (x128 <= y * 17L)
                return Map (quadrant, 6);       // tan(6.5)*128
              else
                return Map (quadrant, 5);       // tan(5.5)*128
            }
          } else {
            if (x128 <= y * 8L) {
              if (x128 <= y * 11L)
                return Map (quadrant, 4);       // tan(4.5)*128
              else
                return Map (quadrant, 3);       // tan(3.5)*128
            } else {
              if (x128 <= y * 5L) {
                return Map (quadrant, 2);       // tan(2.5)*128 hi==lo
              } else {
                if (x128 <= y * 2L) // remember x and y are negative.
                  return Map (quadrant, 1);     // tan(1.5)*128
                else
                  return Map (quadrant, 0);     // tan(0.5)*128 - need to check edge conditions.
              }
            }
          }
        }
      }
    }
  }
  // this logic isn't what was intended because x and y have already been negated
  // BUT it does seem to work since I checked it against the atan2 results using doubles.
  // Probably means it can be simplified. 
  if (x < 0) return 192;
  if (x) return 64; // does this ever happen? x > 0 should not be possible.
  return 0; // x == 0
  // originally returned: Map (quadrant, 64);
}

// scales up well to signed 16 bits
unsigned int8_t latan2_256 (int16_t x, int16_t y) { // Much faster binary search... returns angle in 256 units per circle
  while (!(((x&0xFF80) == 0 || (x&0xFF80) == 0xFF80) && ((y&0xFF80) == 0 || (y&0xFF80) == 0xFF80))) {
    x = x>>1; y = y>>1;
  }
  return iatan2_256((int8_t)x, (int8_t)y);
}

int main(void) {
int x,y,compass;
  enable_controller_1_x();
  enable_controller_1_y();
  for (;;) {
    Joy_Analog();
    Wait_Recal();
    Intensity_a(0x7f);
    Print_Str_d(120, -90, "  NEAREST ANGLE \x80");
    Print_Str_d(100, -90, "POINT TO JOYSTICK \x80");

    x = joystick_1_x(); y = joystick_1_y();

    compass = (int)iatan2_256(x,y);

    Reset0Ref();
    Intensity_a(0x0f);
    Moveto_d(0, 0);
    Draw_Line_d(y, x);

    Reset0Ref();
    Intensity_a(0x3f);
    Moveto_d(0, 0);
    Draw_Line_d(cos127(compass)>>1, sin127(compass)>>1);
    // for this demo, no special handling of the dead zone around 0,0
    // while testing robustness of x/y check when y=0.
  }

  return 0;
}