#include <stdlib.h>
#include <sys/bios.h>
#include <sys/libwwc.h>

#define STEPS_PER_90DEG 16
#define STEPS_PER_360DEG (STEPS_PER_90DEG*4)

float sin_table[STEPS_PER_360DEG];
void make_sin_table() {
  int i, j;
  float s, t, term;

  for(j=0; j<STEPS_PER_90DEG; j++) {
    t = j*3.1416/2/STEPS_PER_90DEG;
    s = 0;
    term = 1;
    for(i=1; i<10; i++) {
      term *= t/i;
      if (i%2 == 1) {
	s += term;
	term *= -1;
      }
    }
    sin_table[j] = s;
  }
  sin_table[STEPS_PER_90DEG] = 1;
  sin_table[STEPS_PER_90DEG*2] = 0;
  sin_table[STEPS_PER_90DEG*3] = -1;
  for(j=1; j<STEPS_PER_90DEG; j++) {
    sin_table[j+STEPS_PER_90DEG] = sin_table[STEPS_PER_90DEG-j];
    sin_table[j+STEPS_PER_90DEG*2] = -sin_table[j];
    sin_table[j+STEPS_PER_90DEG*3] = -sin_table[STEPS_PER_90DEG-j];
  }
}

/* t=0 : north, clockwise */
float polar_to_x(float r, int t) {
  int t0;
  t0 = t%(STEPS_PER_90DEG*4);
  if (t0<0)
    t0 += STEPS_PER_90DEG*4;
  return r*sin_table[t0];
}
float polar_to_y(float r, int t) {
  return polar_to_x(r, t - STEPS_PER_90DEG);
}

#define WIDTH 28
#define HEIGHT 18

void pset_to_font(char * font, int x, int y, int c) {
  char b, bc;
  b = (0x80 >> x);
  bc = ~b;

  if (c&1) 
    font[y<<2] |= b;
  else 
    font[y<<2] &= bc;
  if (c&2)
    font[(y<<2)+1] |= b;
  else 
    font[(y<<2)+1] &= bc;
  if (c&4)
    font[(y<<2)+2] |= b;
  else 
    font[(y<<2)+2] &= bc;
  if (c&8)
    font[(y<<2)+3] |= b;
  else 
    font[(y<<2)+3] &= bc;
}

void draw_ball_to_font(char* font) {
  int x, y;
  for(y=0; y<8; y++)
    for(x=0; x<8; x++) {
      if ((x*2-7)*(x*2-7) + (y*2-7)*(y*2-7) <= 60)
	pset_to_font(font, x, y, 90/((x-3)*(x-3)+(y-3)*(y-3)+6));
      /* INCHIKI! but it looks not so bad. */
      else
	pset_to_font(font, x, y, 0);
    }
}

#define NUMBER_OF_FOUNTS 3
static struct {
  int x;
  int y;
  int s; /* absolute value of speed */
  int t; /* angle */
  int x_rand;
  int y_rand;
  int t_rand;
  int color;
} founts[] = {
    { 0, 40, 600, 8, 3, 3, 1, 0},
    { 30, 80, 600, 6, 3, 3, 1, 1},
    { 180, 60, 600, 58, 3, 3, 1, 3},
/*    { 216, 60, 600, 54, 5, 5, 3, 2}*/
};

#define NUMBER_OF_REFL 2
static struct {
  int x;
  int y;
  int w;
  int h;
  int t; /* angle */
  int color;
} refl[NUMBER_OF_REFL] = {
    { 80, 80, 16, 2, 0, 1},
    { 120, 110, 16, 2, 60, 1}
};

static struct {
  int k; /* kx+ly+m=0 is the equation */
  int l;
  long m;
  int x0; /* end points */
  int y0;
  int x1;
  int y1;
} refl_data[NUMBER_OF_REFL];

void refl_update() {
  int i;
  for(i=0; i<NUMBER_OF_REFL; i++) {
    refl_data[i].x0 = (int)
      (refl[i].x + polar_to_x(refl[i].w, refl[i].t-STEPS_PER_90DEG)/2);
    refl_data[i].y0 = (int)
      (refl[i].y + polar_to_y(refl[i].w, refl[i].t-STEPS_PER_90DEG)/2);
    refl_data[i].x1 = (int)
      (refl[i].x + polar_to_x(refl[i].w, refl[i].t+STEPS_PER_90DEG)/2);
    refl_data[i].y1 = (int)
      (refl[i].y + polar_to_y(refl[i].w, refl[i].t+STEPS_PER_90DEG)/2);
    refl_data[i].k = refl_data[i].y0-refl_data[i].y1;
    refl_data[i].l = refl_data[i].x1-refl_data[i].x0;
    refl_data[i].m = refl_data[i].x0*refl_data[i].y1
      - refl_data[i].x1*refl_data[i].y0;
  }
}

#define BALL_CHAR 508
#define BALL_PALETTE_BASE 12
#define BALL(x) (BALL_CHAR + ((BALL_PALETTE_BASE-8+(x))<<9))
static int ball_in_use[128]; 
static int ball_x[128];
static int ball_y[128];
static int ball_v[128];
static int ball_w[128];
void move_ball(int id, int x, int y) {
}

#define BALL_XY_MULT 4
void new_ball(int i, int fount) {
  int t_rand;
  sprite_set_char(i, BALL(founts[fount].color));
  ball_in_use[i] = 1;

  ball_x[i] = (founts[fount].x +
    rand()%founts[fount].x_rand-founts[fount].x_rand/2)*BALL_XY_MULT;
  ball_y[i] = (founts[fount].y +
    rand()%founts[fount].y_rand-founts[fount].y_rand/2)*BALL_XY_MULT;
  t_rand = (rand()%founts[fount].t_rand)-founts[fount].t_rand/2;
  ball_v[i] = (int) polar_to_x(founts[fount].s, founts[fount].t+t_rand);
  ball_w[i] = (int) polar_to_y(founts[fount].s, founts[fount].t+t_rand);

  sprite_set_location(i, ball_x[i]/BALL_XY_MULT-4, ball_y[i]/BALL_XY_MULT-4);
}

#define BALL_V_UNIT 5 /* in power of 2 */
#define BALL_W_UNIT 5
#define GRAVITY 80
#define X_MIN -8
#define X_MAX 224
#define Y_MAX 144
#define REFL_RATE 0.7
void ball_update(int i) {
  long x0, x1, y0, y1;
  int j;
  int s1, s2;

  if (!ball_in_use[i])
    return;
  x0 = ball_x[i];
  y0 = ball_y[i];
  x1 = x0 + (ball_v[i]>>BALL_V_UNIT);
  y1 = y0 + (ball_w[i]>>BALL_W_UNIT);
  for(j=0; j<NUMBER_OF_REFL; j++) {
    s1 = refl_data[j].k*x0+refl_data[j].l*y0+refl_data[j].m*BALL_XY_MULT;
    s2 = refl_data[j].k*x1+refl_data[j].l*y1+refl_data[j].m*BALL_XY_MULT;
    if (s1 < 0 && s2 >= 0) {
      s1 = (y0-y1)*refl_data[j].x0*BALL_XY_MULT + 
	(x1-x0)*refl_data[j].x1*BALL_XY_MULT + (x0*y1-x1*y0);
      s2 = (y0-y1)*refl_data[j].x1*BALL_XY_MULT + 
	(x1-x0)*refl_data[j].x0*BALL_XY_MULT + (x0*y1-x1*y0);
      if ((s1 > 0 && s2 < 0) || (s1 < 0 && s2 > 0)) {
	float a, b, c, d;
	a = polar_to_x(1, refl[j].t);
	b = polar_to_y(1, refl[j].t);
	c = x1 - 2*((x1-refl[j].x*BALL_XY_MULT)*a+
		    (y1-refl[j].y*BALL_XY_MULT)*b)*a;
	d = y1 - 2*((y1-refl[j].x*BALL_XY_MULT)*a+
		    (y1-refl[j].y*BALL_XY_MULT)*b)*b;
	x1 = c;
	y1 = d;
	c = ball_v[i] - 2*(ball_v[i]*a+ball_w[i]*b)*a;
	d = ball_w[i] - 2*(ball_v[i]*a+ball_w[i]*b)*b;
	ball_v[i] = (int) c*REFL_RATE;
	ball_w[i] = (int) d*REFL_RATE;
      }
    }
  }
  ball_x[i] = x1;
  ball_y[i] = y1;
  ball_w[i] += GRAVITY;
  if (ball_x[i]/BALL_XY_MULT < X_MIN || ball_x[i]/BALL_XY_MULT >= X_MAX 
      || ball_y[i]/BALL_XY_MULT >= Y_MAX) {
    ball_in_use[i] = 0;
    sprite_set_location(i, X_MAX, Y_MAX);
    return;
  }
  sprite_set_location(i, ball_x[i]/BALL_XY_MULT-4, ball_y[i]/BALL_XY_MULT-4);
}

#define SCREEN1_FONTS_MAX 10
static int display[WIDTH*HEIGHT];
static int fonts_used;
static char screen1_font[SCREEN1_FONTS_MAX][32];

void pset_screen1(int x, int y, int c) {
  int k, l;
  k = x/8;
  l = y/8;
  if (!display[k+l*WIDTH]) {
    display[k+l*WIDTH] = fonts_used;
    fonts_used++;
  }
  pset_to_font((char*)&screen1_font[display[k+l*WIDTH]], x%8, y%8, c);
}

void draw_screen1() {
  int i, j, x, x0, y0, x1, y1;
  fonts_used = 0;
  for (i=0; i<32; i++)
    screen1_font[0][i] = 0;
  for (i=0; i<WIDTH*HEIGHT; i++)
    display[i] = 0;
  fonts_used++;

  for(j=0; j<NUMBER_OF_REFL; j++) {
    x0 = refl_data[j].x0;
    y0 = refl_data[j].y0;
    x1 = refl_data[j].x1;
    y1 = refl_data[j].y1;
    for(i = 0; i < refl[j].h; i++)
      for(x=x0; x<x1; x++)
	pset_screen1(x, ((y1-y0)*(x-x0))/(x1-x0)+y0+i, 15);
  }
  wwc_font_set_colordata(0, fonts_used, screen1_font);
  screen_set_char(SCREEN1, 0, 0, WIDTH, HEIGHT, display);
}

main() {
  static char font[32];
  int i, j;
  int key, lastkey;
  int fount;

  make_sin_table();

  display_control(0x05); /* SCREEN1 and sprite. */
  wwc_set_color_mode(COLOR_MODE_16COLOR);
  wwc_clear_font();

  for (i=0; i<16; i++)
    wwc_palette_set_color(0, i, (15-i)*0x111);
  for (i=0; i<16; i++)
    wwc_palette_set_color(BALL_PALETTE_BASE, i, i*0x111);
  for (i=0; i<16; i++)
    wwc_palette_set_color(BALL_PALETTE_BASE+1, i, i*0x011);
  for (i=0; i<16; i++)
    wwc_palette_set_color(BALL_PALETTE_BASE+2, i, i*0x110);
  for (i=0; i<16; i++)
    wwc_palette_set_color(BALL_PALETTE_BASE+3, i, i*0x100);

  draw_ball_to_font(font);
  wwc_font_set_colordata(BALL_CHAR, 1, font);
  sprite_set_range(0, 128);
  for(i=0; i<128; i++)
    ball_in_use[i] = 0;

  fount = 0;
  lastkey = -1;
  while(1) {
    for(j=0; j<NUMBER_OF_FOUNTS; j++) {
      for(i=0; i<128; i++) {
	if (!ball_in_use[i]) {
	  new_ball(i, j);
	  break;
	}
      }
    }
    refl_update();
    draw_screen1();
    for(i=0; i<128; i++) {
      ball_update(i);
    }
    key = key_press_check();
    if (key == 2)
      break;
    if (key == 0x100 && lastkey != key) /* Y1 */
      fount = (fount+NUMBER_OF_FOUNTS-1)%NUMBER_OF_FOUNTS;
    if (key == 0x400 && lastkey != key) /* Y3 */
      fount = (fount+1)%NUMBER_OF_FOUNTS;

    if (key == 0x200) /* Y2 */
      founts[fount].t = (founts[fount].t+1)%STEPS_PER_360DEG;
    if (key == 0x800) /* Y4 */
      founts[fount].t = (founts[fount].t+STEPS_PER_360DEG-1)%STEPS_PER_360DEG;
    if (key == 0x10) /* X1 */
      founts[fount].y--;
    if (key == 0x20) /* X2 */
      founts[fount].x++;
    if (key == 0x40) /* X3 */
      founts[fount].y++;
    if (key == 0x80) /* X4 */
      founts[fount].x--;
    lastkey = key;
    /*    sys_wait(3);*/
  }
}
