#include <sys/bios.h>
#include <stdlib.h>
#include <sys/libwwc.h>
#include <chip.h>                                       /* コースグラフィック */
#include <title.h>                                      /* タイトル */
#include <screen.h>                                     /* ステータス部分 */
#include <press.h>                                      /* 『PRESS START』部分 */
#include <numeric.h>                                    /* 数字 */
#include <car.h>                                        /* 自車グラフィック */
#include "sincos.c"                                     /* 三角関数テーブル */
#include "map.c"                                        /* コースデータ */

#define  display_w  16                                  /* 画面上での表示横幅 */
#define  display_h  16                                  /*           表示縦幅 */

char map_data[192][192];

unsigned title_data[  title_width *  title_height];
unsigned screen_data[screen_width * screen_height];
unsigned press_data[  press_width *  press_height];
unsigned car_data[      car_width *    car_height];

unsigned display_data1[display_w*display_w];            /* ＳＣ１への表示内容 */
unsigned display_data2[display_w*display_w];            /* ＳＣ２への表示内容 */
unsigned palette_data[16][16];                          /* 全パレットの色状況格納用 */

unsigned short int sx1,sy1;                             /* ↓拡大縮小回転関連変数 */
unsigned short int sx2,sy2;
unsigned short int dx1,dy1;
unsigned short int dx2,dy2;
unsigned short int dx3,dy3;
unsigned short int c1,c2;
unsigned short int x,y;
unsigned short int mx,my;
unsigned short int mr,mmr;
unsigned short int mz;
short int ms;                                           /* 自車速度 */
unsigned short int mmx,mmy;                             /* 自車位置（２５６倍で使用）*/
unsigned int checker;                                   /* 周回チェック */
int key;                                                /* キー情報 */
int key2;
int lap;                                                /* ラップ */
long int tn,ft,fl,nt;                                   /* 現在ﾀｲﾑ・最速ﾄｰﾀﾙ・最速ﾗｯﾌﾟ・現在ﾄｰﾀﾙ */

void bg_display(short int,short int,short int,short int);
void make_course(void);
void run_init(void);
void run_main(void);
void go_run(void);
void screen_init(void);
void palette_save(void);
void palette_fade_w(unsigned int,int);
void palette_fade_b(unsigned int,int);
void numeric_put(int,int,long int);

/************************************************/
/* メイン                                       */
/************************************************/
void main( void )
{
    unsigned short int i;
    int j,k;
    unsigned shft;

    if ( wwc_get_hardarch() == HARDARCH_WS ) return;

    wwc_set_color_mode ( COLOR_MODE_16COLOR );
    wwc_clear_font();

    wwc_font_set_colordata(  1, chip_width*chip_height, bmp_chip );
    wwc_font_set_colordata(150, title_width*title_height,bmp_title);
    wwc_font_set_colordata(200, screen_width*screen_height,bmp_screen);
    wwc_font_set_colordata(300, press_width*press_height,bmp_press);
    wwc_font_set_colordata(400, numeric_width*numeric_height,bmp_numeric);
    wwc_font_set_colordata(420, car_width*car_height,bmp_car);

    for ( i=0; i<16; i++ ) {
        wwc_palette_set_color( 0, i, palette_chip[i] );
    }
    for ( i=0; i<16; i++ ) {
        wwc_palette_set_color( 1, i, palette_title[i] );
        wwc_palette_set_color( 12, i, palette_title[i] );
    }

    display_control( DCM_SCR1 | DCM_SCR2 | DCM_SPR );

    screen_fill_char( SCREEN1, 0, 0, 28, 18, 0 );
    screen_fill_char( SCREEN2, 0, 0, 28, 18, 0 );
    key = NULL;

    make_course();
    screen_init();
    fl=9999;
    ft=99999;
    key2= (KEY_A | KEY_B);
    numeric_put(26,12,ft);
    numeric_put(26,13,fl);

    palette_save();
    palette_fade_b(0,15);
    palette_fade_b(12,15);
    for (j=0; j<2; j++) {
        for (k=0; k<2; k++) {
            shft = ( 4 << 9) | (420+(j*2+k) | (1 << 13));
            sprite_set_char(j*2+k,shft);
            sprite_set_location(j*2+k,64+k*8,64+j*8);
        }    
    }
    sprite_set_range(0,4);

    while ( (key & KEY_B) == 0) {
        screen_set_char( SCREEN2, 19, 15, press_width, press_height, press_data );
        key = key_press_check();
        if (key & KEY_START) {
            screen_fill_char( SCREEN2,19,15,8,2,0);
            run_init();
            bg_display( mx, my, ((mr+64) & 255), 24 );
            screen_fill_char( SCREEN2,22,7,5,4,0);

            for (i=15; i>0; i--) {
                palette_fade_b(0,i);
                palette_fade_b(12,i);
                bg_display( mx, my, ((mr+64) & 255), 24+i*1.5 );
                sys_wait(5);
            }
            bg_display( mx, my, ((mr+64) & 255), 24);

            for (i=3; i>0; i--) {
                screen_fill_char(SCREEN1,10-i,5,1,1,0);
                screen_fill_char(SCREEN2,10-i,5,1,1,((1 << 9) | 400+i));
                sys_wait(75);
                bg_display( mx, my, ((mr+64) & 255), 24 );
            }
            go_run();
            for (i=0; i<16; i++) {
                palette_fade_b(0,i);
                palette_fade_b(12,i);
                sys_wait(10);
            }
        }
    }
    return;
}

/************************************************/
/* 数値をスクリーンに表示                       */
/************************************************/
void numeric_put(int x,int y,long int score)
{
    int i;
    long int d,d2;

    screen_fill_char(SCREEN2,x-4,y,5,1,0);
    d = score;
    for (i=0; i<7; i++) {
        d2 = d % 10;
        screen_fill_char(SCREEN2,x-i,y,1,1,((1 << 9) | 400+d2));
        if (d < 10 ) i=99;else d /= 10;
    }
    return;
}        

/************************************************/
/* 画面初期化                                   */
/************************************************/
void screen_init(void)
{
    int i;
    for (i=0; i<title_width*title_height; i++) {
        title_data[i] = ( 1 << 9) | (i+150);
    }
    for (i=0; i<screen_width*screen_height; i++) {
        screen_data[i] = ( 1 << 9) | (i+200);
    }
    for (i=0; i<press_width*press_height; i++) {
        press_data[i] = ( 1 << 9) | (i+300);
    }
    screen_set_char( SCREEN2, 18,1, title_width, title_height, title_data );
    screen_set_char( SCREEN2, 19,7, screen_width, screen_height, screen_data );

    return;
}

/************************************************/
/* コース周回メイン部分                         */
/************************************************/
void go_run(void)
{
    while (lap < 3 ) {
        run_main();
        tn += (tn<9999);
        bg_display( mx, my, ((mr+64) & 255), 24 );
    }
    if ( nt < ft) {
        ft = nt;
        numeric_put(26,12,ft);
        numeric_put(26,10,nt);
    }
    return;
}

/************************************************/
/* 走行初期化                                   */
/************************************************/
void run_init(void)
{
    mx=42;
    my=114;
    mr = 192;
    mmr = 384;
    ms=0;
    mmx = (mx << 8);
    mmy = (my << 8);
    checker=0;
    lap = 0;
    tn = 0;
    nt = 0;    
    return;
}

/************************************************/
/* 走行処理                                     */
/************************************************/
void run_main(void)
{
    long dx,dy;
    key = key_press_check();

    if ((key & KEY_A) && ms <176) {
        ms += 16;
    } else {
        if ( ms > 4 ) ms -= 4;
    }
    if ( (key & KEY_B) && ms > 16 ) ms -= 16;
    
    mmr += 512;
    if (key & KEY_X4) mmr -= 3;
    if (key & KEY_X2) mmr += 3;

    mmr &= 511;
    mr = mmr >> 1;
    dx = ((cos[mr]*ms) >> 8);
    dy = ((sin[mr]*ms) >> 8);
    
    switch ( map_data[(mmy+dy) >> 8][(mmx+dx) >> 8] )
    {
    case '7':
       ms=0;
       mmx = mmx - (dx << 3);
       mmy = mmy - (dy << 3);
       break;
    case '1':
       checker = checker | 1;
       break;
    case '2':
       checker = checker | 2;
       break;
    case '3':
       checker = checker | 4;
       break;
    case '4':
       checker = checker | 8;
       break;
    case '0':
       if ( checker == 15) {
           lap += 1;
           checker = 0;
           numeric_put(26,6+lap,tn);
           if ( tn < fl ) {
               fl=tn;
               numeric_put(26,13,fl);
           }
           nt += tn;
           tn=0;
       }
       break;
    }

    if ( (map_data[(mmy+dy) >> 8][(mmx+dx) >> 8]) != '7' )
    {
       mmx = mmx+dx;
       mmy = mmy+dy;
       mx = mmx >> 8;
       my = mmy >> 8;
    }
    return;
}

/************************************************/
/* コース作成                                   */
/************************************************/
void make_course(void)
{
    int x,y,i,j;
    char c;

    for (y=0; y<16; y++) {
        for (x=0; x<16; x++) {
            c = course_data[y][x];
            for (j=0; j<16; j++) {
                for (i=0; i<16; i++) {
                    map_data[y*12+j][x*12+i] = c;
                }
            }
        }
    }
    srand(sys_get_tick_count() & 0x7fff);
    for (i=0; i<3000; i++) {
        x = rand() % 192;
        y = rand() % 192;
        if (map_data[y][x] == '5') map_data[y][x] = '6'; 
    }
    return;
}

/************************************************/
/* 画面左側描画                                 */
/************************************************/
void bg_display( short int mx, short int my, short int mr, short int mz )
{
    dx1 = ( cos[mr] * mz )            >> 5;                                                             
    dy1 = ( sin[mr] * mz )            >> 5;
    dx2 = ( cos[(mr+64) & 255] * mz ) >> 5;
    dy2 = ( sin[(mr+64) & 255] * mz ) >> 5;

    sx1  = (mx << 8) + (( cos[(mr+192) & 255] * mz) >> 1) + ((cos[(mr+128) & 255] * mz) >> 1 );
    sy1  = (my << 8) + (( sin[(mr+192) & 255] * mz) >> 1) + ((sin[(mr+128) & 255] * mz) >> 1 );
    sx2  = sx1 + dx2;
    sy2  = sy1 + dy2;

    dx3 = sx1;
    dy3 = sy1;

    for ( y=0; y<display_h; y++ ) {
        for ( x=0; x<display_w; x++) {
            c1 = map_data[(dy3 >> 8)][(dx3 >> 8)]         - 48;
            c2 = map_data[(dy3+dy1) >> 8][(dx3+dx1) >> 8] - 48;
            display_data1[(y << 4)+x] = (c1 << 3) + c2 + 1;
            dx3 += (dx1 << 1);
            dy3 += (dy1 << 1);
        }
        sx1 += (dx2 << 1);
        sy1 += (dy2 << 1);
        dx3 = sx1;
        dy3 = sy1;
    }

    dx3 = sx2;
    dy3 = sy2;
    for ( y=0; y<display_h; y++ ) {
        for ( x=0; x<display_w; x++ ) {
            c1 = map_data[(dy3 >> 8)][(dx3 >> 8)]         - 48;
            c2 = map_data[(dy3+dy1) >> 8][(dx3+dx1) >> 8] - 48;
            display_data2[(y << 4)+x] = (c1 << 3) + c2 + 65;
            dx3 += (dx1 << 1);
            dy3 += (dy1 << 1);
        }
        sx2 += (dx2 << 1);
        sy2 += (dy2 << 1);
        dx3 = sx2;
        dy3 = sy2;
    }
    screen_set_char( SCREEN1, 1, 1, display_w, display_h, display_data1);
    screen_set_char( SCREEN2, 1, 1, display_w, display_h, display_data2);
    return;
}

/************************************************/
/* パレット情報保存                             */
/************************************************/
void palette_save(void)
{
    unsigned int i,j;
    for (i=0; i<16; i++) {
        for (j=0; j<16; j++) {
            palette_data[i][j] = wwc_palette_get_color(i,j);
        }
    }
    return;
}

/************************************************/
/* 白にフェード                                 */
/************************************************/
void palette_fade_w(unsigned int pal,int pow) 
{
    unsigned int i,p,r,g,b;
    for (i=0; i<16; i++) {
        r = palette_data[pal][i] & 0x0f00;
        g = palette_data[pal][i] & 0x00f0;
        b = palette_data[pal][i] & 0x000f;

        r += 256*pow;
        g += 16*pow;
        b += pow;
        if (r > 3840) r=3840;
        if (g > 240) g=240;
        if (b > 15) b=15;

        p = r+g+b;
        wwc_palette_set_color(pal,i,p);
    }
    return;
}

/************************************************/
/* 黒にフェード                                 */
/************************************************/
void palette_fade_b(unsigned int pal,int pow) 
{
    unsigned int i,p,r,g,b,dr,dg,db;
    for (i=0; i<16; i++) {
        r = palette_data[pal][i] & 0x0f00;
        g = palette_data[pal][i] & 0x00f0;
        b = palette_data[pal][i] & 0x000f;

        dr = 256*pow;
        dg = 16*pow;
        db = pow;
        if (dr >= r) dr=r;
        if (dg >= g) dg=g;
        if (db >= b) db=b;
        r -= dr;
        g -= dg;
        b -= db;

        p = r+g+b;
        wwc_palette_set_color(pal,i,p);
    }
    return;
}