import "c64"
import "basic-floats"
import "print"

const byte* SCREEN = $0400;

const byte sinlen_x = 221;
const byte[221] sintab_x;
const byte sinlen_y = 197;
const byte[197] sintab_y;
const byte* sprites = $2000;

void main() {
    init();
    do {
        do { } while (*RASTER!=$ff);
        anim();
    } while(true);
}

void init() {
    clear_screen();
    for( byte i : 0..39) {
        COLS[i] = $0;
        COLS[40+i] = $b;
    }
    place_sprites();
    gen_sprites();
    progress_init(SCREEN);
    gen_sintab(sintab_x, sinlen_x, $00, $ff);
    progress_init(SCREEN+40);
    gen_sintab(sintab_y, sinlen_y, $32, $d0);
    clear_screen();
}

void clear_screen() {
    for(byte* sc = SCREEN; sc<SCREEN+1000; sc++) {
        *sc = ' ';
    }
}

// Current position of the progress cursor
byte* progress_cursor = SCREEN;
// Current index within the progress cursor (0-7)
byte progress_idx = 0;

// Initialize the PETSCII progress bar
void progress_init(byte* line) {
    progress_cursor = line;
    progress_idx = 0;
}

// Increase PETSCII progress one bit
// Done by increasing the character until the idx is 8 and then moving to the next char
void progress_inc() {
    // Progress characters
    const byte[] progress_chars = { $20, $65, $74, $75, $61, $f6, $e7, $ea, $e0};
    if(++progress_idx==8) {
        *progress_cursor = progress_chars[8];
        progress_cursor++;
        progress_idx = 0;
    }
    *progress_cursor = progress_chars[progress_idx];
}


byte sin_idx_x = 0;
byte sin_idx_y = 0;

void anim() {
    (*BORDERCOL)++;
    byte xidx = sin_idx_x;
    byte yidx = sin_idx_y;
    byte j2 = 12;
    byte x_msb = 0;
    for( byte j : 0..6) {
        word x = (word)$1e + sintab_x[xidx];
        x_msb = x_msb<<1 | >x;
        SPRITES_XPOS[j2] = <x;
        SPRITES_YPOS[j2] = sintab_y[yidx];
        xidx = xidx+10;
        if(xidx>=sinlen_x) {
            xidx = xidx-sinlen_x;
        }
        yidx = yidx+8;
        if(yidx>=sinlen_y) {
            yidx = yidx-sinlen_y;
        }
        j2 = j2-2;
    }
    *SPRITES_XMSB = x_msb;

    // Increment sin indices
    if(++sin_idx_x>=sinlen_x) {
        sin_idx_x = 0;
    }
    if(++sin_idx_y>=sinlen_y) {
        sin_idx_y = 0;
    }
    (*BORDERCOL)--;
}

void place_sprites() {
    *SPRITES_ENABLE = %01111111;
    *SPRITES_EXPAND_X = %01111111;
    *SPRITES_EXPAND_Y = %01111111;
    byte* sprites_ptr = SCREEN+$3f8;
    byte spr_id = (byte)(sprites/$40);
    byte spr_x = 60;
    byte j2 = 0;
    byte col = $5;
    for( byte j : 0..6) {
        sprites_ptr[j] = spr_id++;
        SPRITES_XPOS[j2] = spr_x;
        SPRITES_YPOS[j2] = 80;
        SPRITES_COLS[j] = col;
        spr_x = spr_x + 32;
        col = col^($7^$5);
        j2++;
        j2++;
    }
}

void gen_sprites() {
    byte[] cml = "camelot";
    byte* spr = sprites;
    for( byte i : 0..6 ) {
        gen_chargen_sprite(cml[i], spr);
        spr = spr + $40;
    }
}

// Generate a sprite from a C64 CHARGEN character (by making each pixel 3x3 pixels large)
// - c is the character to generate
// - sprite is a pointer to the position of the sprite to generate
void gen_chargen_sprite(byte ch, byte* sprite) {
    byte* chargen = CHARGEN+((word)ch)<<3;
    asm { sei }
    *PROCPORT = $32;
    for(byte y:0..7) {
      // current chargen line
      byte bits = chargen[y];
      // current sprite byte
      byte s_gen = 0;
      // #bits filled into current sprite byte
      byte s_gen_cnt = 0;
      for(byte x:0..7) {
        // Find the current chargen pixel (c)
        byte c = 0;
        if((bits & $80) != 0) {
           c = 1;
        }
        // generate 3 pixels in the sprite byte (s_gen)
        for(byte b : 0..2){
            s_gen = s_gen<<1 | c;
            if(++s_gen_cnt==8) {
                // sprite byte filled - store and move to next byte
                sprite[0] = s_gen;
                sprite[3] = s_gen;
                sprite[6] = s_gen;
                sprite++;
                s_gen = 0;
                s_gen_cnt = 0;
            }
        }
        // move to next char pixel
        bits = bits<<1;
      }
      // move 3 lines down in the sprite (already moved 1 through ++)
      sprite = sprite + 6;
    }
    *PROCPORT = $37;
    asm { cli }
}

// Generate a sinus table using BASIC floats
// - sintab is a pointer to the table to fill
// - length is the length of the sine table
// - min is the minimum value of the generated sinus
// - max is the maximum value of the generated sinus
void gen_sintab(byte* sintab, byte length, byte min, byte max) {
    byte[] f_i = {0, 0, 0, 0, 0};   // i * 2 * PI
    byte[] f_min = {0, 0, 0, 0, 0}; // amplitude/2 + min
    byte[] f_amp = {0, 0, 0, 0, 0}; // amplitude/2
    byte* f_2pi = $e2e5;            // 2 * PI
    setFAC((word)max);       // fac = max
    setARGtoFAC();           // arg = max
    // TODO: Kernel JSR clobbers A,X,Y
    asm {
        lda #0
        ldx #0
        ldy #0
    }
    setFAC((word)min);       // fac = min
    setMEMtoFAC(f_min);      // f_min = min
    subFACfromARG();         // fac = max - min
    setMEMtoFAC(f_amp);      // f_amp = max - min
    setFAC(2);               // fac = 2
    divMEMbyFAC(f_amp);      // fac = (max - min) / 2
    setMEMtoFAC(f_amp);      // f_amp = (max - min) / 2
    addMEMtoFAC(f_min);      // fac = min + (max - min) / 2
    setMEMtoFAC(f_min);      // f_min = min + (max - min) / 2
    for(byte i =0; i<length; i++) {
        setFAC((word)i);       // fac = i
        mulFACbyMEM(f_2pi);    // fac = i * 2 * PI
        setMEMtoFAC(f_i);      // f_i = i * 2 * PI
        setFAC((word)length);  // fac = length
        divMEMbyFAC(f_i);      // fac = i * 2 * PI / length
        sinFAC();              // fac = sin( i * 2 * PI / length )
        mulFACbyMEM(f_amp);    // fac =  sin( i * 2 * PI / length ) * (max - min) / 2
        addMEMtoFAC(f_min);    // fac =  sin( i * 2 * PI / length ) * (max - min) / 2 + min + (max - min) / 2
        sintab[i] = (byte)getFAC();
        progress_inc();
    }
}
