/*
 * Biorythm for WonderSwan
 *   Copyright (c) 2001 hiro <hiro@zob.ne.jp>
 */

#include <stdlib.h>
#include <sys/disp.h>
#include <sys/text.h>
#include <sys/key.h>
#include <sys/system.h>
#include <sys/timer.h>
#include <sys/libwwc.h>

#include "graph.h"
#include "date.h"
#include "math.h"

#define OX 8
#define OY 60
#define WX 4
#define WY 50
#define MX 17

#define BIO_PYS 23
#define BIO_SYM 28
#define BIO_INT 33

#define CX 180
#define CY 100
#define CR 40
#define CW 36
#define HL 28
#define ML 32
#define SL 34

#include <sys/service.h>

typedef struct {
  char name[16];
  int  birth_year;
  char birth_month;
  char birth_day;
  char gender;
  char btype;
} ownerinfo_t;

static char gender[][4] = {
  "", "Mr.", "Ms."
};

static BYTE wcol[] = { 2, 3, 3, 3, 3, 3, 1 };

static char work[16];
static ownerinfo_t o;/**/

static void
draw_clock(void)
{
  datetime_t  d;
  int x, y, r;

  static BYTE h = 0xff, m = 0xff, s = 0xff;

  rtc_get_datetime_struct(&d);

  if (h == d.hour && m == d.minute) return;
  h = d.hour;
  m = d.minute;
  s = d.second;

  g_circle_fill(CX, CY, CR, 1);
  g_circle_fill(CX, CY, CW, 0);

  /* DRAW HOUR */
  r = (h % 12) * 30 + m * 6 / 10;
  x = (sin(r) * HL) / 100;
  y = (cos(r) * HL) / 100;
  g_solid_line(CX, CY, x, -y, 1, 3);
  /* DRAW MIN. */
  r = m * 6;
  x = (sin(r) * ML) / 100;
  y = (cos(r) * ML) / 100;
  g_solid_line(CX, CY, x, -y, 2, 2);
  /* flush */
  g_flush();
}

static void
draw_calendar(d_date_t far *d_)
{
  int d;
  int w, y;

  g_box_fill(4, 4, 15 * 8 , 9 * 8, 0);
  g_box(4, 4, 15 * 8, 9 * 8, 1);

  g_put_numeric(1, 1, 1, 4, NUM_ALIGN_RIGHT, d_->year);
  g_put_string(6, 1, 1, d_month_name(d_, DATE_MONTH_SHORT));

  g_put_char( 2, 2, wcol[0], 'S'); /* Sunday */
  g_put_string( 3, 2, wcol[1], " M T W T F");
  g_put_char(14, 2, wcol[6], 'S'); /* Saturaday */

  w = d_get_day(d_);
  y = 2;

  for (d = 0 ; d < d_get_mdays(d_) ; d++) {
    if (d + 1 == d_->day) {
      g_box_fill((1 + 2 * w) * GRAPH_CHAR_UNIT, (1 + y) * GRAPH_CHAR_UNIT,
            2 * GRAPH_CHAR_UNIT + 1, GRAPH_CHAR_UNIT + 1, 2);
      g_put_numeric(1 + 2 * w, 1 + y, 0, 2, NUM_ALIGN_RIGHT, d + 1);
    } else {
      g_put_numeric(1 + 2 * w, 1 + y, wcol[w], 2, NUM_ALIGN_RIGHT, d + 1);
      g_box((1 + 2 * w) * GRAPH_CHAR_UNIT, (1 + y) * GRAPH_CHAR_UNIT,
            2 * GRAPH_CHAR_UNIT + 1, GRAPH_CHAR_UNIT + 1, 1);
    }
    if (++w > 6) {
      w = 0; ++y;
    }
  }
  g_flush();
}

static void
calendar(d_date_t far *d_)
{
  d_date_t d;
  static g_font_t back[10 * 16];

  memcpy(&d, d_, sizeof(d));

  g_save_buffer(0, 0, 16, 10, back); /**/
  for (;;) {
    d_adjust_last(&d);
    draw_calendar(&d);
_again:;
    draw_clock();
    switch (key_press_check()) {
    case KEY_RIGHT1:
      if (++d.month > 12) {
	d.month = 1;
	++d.year;
      }
      break;
    case KEY_LEFT1:
      if (--d.month == 0) {
	d.month = 12;
	--d.year;
      }
      break;
    case KEY_UP1:
      --d.year;
      break;
    case KEY_DOWN1:
      ++d.year;
      break;
    case KEY_B:
      goto _done;
    default:
      goto _again;
    }
  }
_done:;
  g_restore_buffer(0, 0, 16, 10, back); /**/
  g_flush();
}

static void
draw_date(int x_, int y_, int color_, int year_, int month_, int day_)
{
  g_box_fill(x_ * GRAPH_CHAR_UNIT, y_ * GRAPH_CHAR_UNIT,
             GRAPH_CHAR_UNIT * 10, GRAPH_CHAR_UNIT, 0);
  g_put_string(x_, y_, 1, "    /  /  ");
  g_put_numeric(x_, y_, 1, 4, NUM_ALIGN_RIGHT, year_);
  x_ += 5;
  g_put_numeric(x_, y_, 1, 2, NUM_ALIGN_RIGHT, month_);
  x_ += 3;
  g_put_numeric(x_, y_, 1, 2, NUM_ALIGN_RIGHT, day_);
}

void
draw_profile(ownerinfo_t far *o_)
{
  g_put_string(0, 0, 1, gender[o_->gender]);
  g_put_stringn(3, 0, 1, o_->name, 16);
  g_put_string(0, 17, 1, "BIRTH:");
  draw_date(6, 17, 1, o_->birth_year, o_->birth_month, o_->birth_day);
  g_flush();
}

void
draw_graph(d_date_t far *today_, d_date_t far *owner_)
{
  int last;
  int d, x;
  int p, s, i, dp, ds, di;
  int wp, ws, wi;
  ulong days;

  last = d_get_mdays(today_);
  /* clear graph */
  g_box_fill(OX, OY - WY, WX * 31, 2 * WY + 1, 0);
  /* draw frame */
  g_box(OX, OY - WY, WX * (last - 1) + 1, 2 * WY + 1, 1);
  g_line(OX, OY, WX * (last - 1), 0, 1);
  g_put_char(0, 1, 1, '+');
  g_put_char(0, 7, 1, '0');
  g_put_char(0,13, 1, '-');

  g_box_fill(MX * GRAPH_CHAR_UNIT, 0, 
             (GRAPH_CHAR_WIDTH - MX) * GRAPH_CHAR_UNIT, 56, 0);
  g_put_string(MX, 1, 1, "Pys:");
  g_put_string(MX, 3, 2, "Sym:");
  g_put_string(MX, 5, 3, "Int:");
  g_flush();

  /* draw statistics */
  g_put_string(0, 15, 1, "TODAY:");
  draw_date(6, 15, 1, today_->year, today_->month, today_->day);
  days = d_get_days(today_) - d_get_days(owner_);
  g_box_fill(0, 16 * GRAPH_CHAR_UNIT,
	     11 * GRAPH_CHAR_UNIT, GRAPH_CHAR_UNIT, 0);
  g_put_numeric( 0, 16, 1, 11, NUM_SIGNED|NUM_ALIGN_RIGHT, days);
  g_put_string(12, 16, 1, "days");
  /* draw rythm by step */
  days -= today_->day - 1;
  dp = (sin((days % BIO_PYS) * 360 / BIO_PYS) * WY) / 100;
  ds = (sin((days % BIO_SYM) * 360 / BIO_SYM) * WY) / 100;
  di = (sin((days % BIO_INT) * 360 / BIO_INT) * WY) / 100;
  wp = 0; ws = 0; wi = 0;
  for(x = OX, d = 1 ; d < last ; d++) {
    if (d == today_->day) {
      g_line(x, OY - WY, 0, 2 * WY, 2); /* cursor */
    } else {
      g_line(x, OY - 2, 0, 5, 1); /* tick */
    }
    ++days;
    p = (sin((days % BIO_PYS) * 360 / BIO_PYS) * WY) / 100;
    s = (sin((days % BIO_SYM) * 360 / BIO_SYM) * WY) / 100;
    i = (sin((days % BIO_INT) * 360 / BIO_INT) * WY) / 100;
    g_line(x, OY - dp, WX, dp - p, 1);
    g_line(x, OY - ds, WX, ds - s, 2);
    g_line(x, OY - di, WX, di - i, 3);
    /* warning check */
    if (dp > 0 && p < 0) {
      g_put_numeric(MX + wp, 2, 1, 2, NUM_ALIGN_RIGHT, d);
      wp += 3;
      g_put_numeric(MX + wp, 2, 1, 2, NUM_ALIGN_RIGHT, d + 1);
      wp += 3;
    }
    if (ds > 0 && s == 0) {
      g_put_numeric(MX + ws, 4, 2, 2, NUM_ALIGN_RIGHT, d + 1);
      ws += 3;
    }
    if (di > 0 && i < 0) {
      g_put_numeric(MX + wi, 6, 3, 2, NUM_ALIGN_RIGHT, d);
      wi += 3;
      g_put_numeric(MX + wi, 6, 3, 2, NUM_ALIGN_RIGHT, d + 1);
      wi += 3;
    }
    dp = p;
    ds = s;
    di = i;
    x += WX;
  }
  if (today_->day == d) {
    g_line(x, OY - WY, 0, 2 * WY, 2); /* cursor */
  }
  g_flush();
}

void
bio(void)
{
  datetime_t  d;
  d_date_t    owner, today;

  rtc_get_datetime_struct(&d);

  /* copy struct */
  owner.year  = o.birth_year;
  owner.month = o.birth_month;
  owner.day   = o.birth_day;
  today.year  = 2000 + d.year;
  today.month = d.month;
  today.day   = d.date;

  for (;;) {
    draw_profile(&o);
    draw_graph(&today, &owner);

_again:;

    draw_clock();

    switch (key_press_check()) {
    case KEY_START:
      goto _done;
      break;
    case KEY_A:
      calendar(&today);
      goto _again;
      break;
    case KEY_B:
      goto _again;
      break;
    case KEY_UP1:
      --today.year;
      d_adjust_last(&today);
      break;
    case KEY_DOWN1:
      ++today.year;
      d_adjust_last(&today);
      break;
    case KEY_LEFT1:
      if (--today.month == 0) {
	--today.year;
	today.month = 12;
      }
      d_adjust_last(&today);
      break;
    case KEY_RIGHT1:
      if (++today.month > 12) {
	++today.year;
	today.month = 1;
      }
      d_adjust_last(&today);
      break;
    case KEY_UP2:
    case KEY_DOWN2:
      goto _again;
      break;
    case KEY_LEFT2:
      if (--today.day == 0) {
	if (--today.month == 0) {
	  today.month = 12;
	  --today.year;
	}
	today.day = d_get_mdays(&today);
      }
      break;
    case KEY_RIGHT2:
      if (++today.day > d_get_mdays(&today)) {
	if (++today.month > 12) {
	  today.month = 1;
	  ++today.year;
	}
	today.day = 1;
      }
      break;
    default:
      goto _again;
    }
  }
 _done:;
  g_clear();
  g_flush();
}

void
main(int argc, char *argv[])
{
  sys_get_ownerinfo(sizeof(o), (char far*)&o);

  g_init(SCREEN1, COLOR_MODE_4COLOR);
  g_setmode(GRAPH_BUFFERED);

  bio();

  g_init(SCREEN1, COLOR_MODE_GRAYSCALE); /* finalize */
}
