#include <am.h>
#include <stdio.h>
#include <klib-macros.h>

#define FPS 30
#define CHAR_WHITE '.'
#define CHAR_BLACK 'X'

typedef struct {
  uint8_t pixel[VIDEO_ROW * VIDEO_COL / 8];
} frame_t;

static void sleep_until(uint64_t next) {
  while (io_read(AM_TIMER_UPTIME).us < next) ;
}

static uint8_t getbit(uint8_t *p, int idx) {
  int byte_idx = idx / 8;
  int bit_idx = idx % 8;
  bit_idx = 7 - bit_idx;
  uint8_t byte = p[byte_idx];
  uint8_t bit = (byte >> bit_idx) & 1;
  return bit;
}

int main() {
  extern uint8_t video_payload, video_payload_end;
  extern uint8_t audio_payload, audio_payload_end;
  int audio_len = 0, audio_left = 0;
  Area sbuf;

  ioe_init();

  frame_t *f = (void *)&video_payload;
  frame_t *fend = (void *)&video_payload_end;
  printf("\033[H\033[J");  // screan_clear

  bool has_audio = io_read(AM_AUDIO_CONFIG).present;
  if (has_audio) {
    io_write(AM_AUDIO_CTRL, AUDIO_FREQ, AUDIO_CHANNEL, 1024);
    audio_left = audio_len = &audio_payload_end - &audio_payload;
    sbuf.start = &audio_payload;
  }
  
  uint64_t now = io_read(AM_TIMER_UPTIME).us;
  for (; f < fend; f ++) {
     printf("\033[0;0H");  // reset cursor
     for (int y = 0; y < VIDEO_ROW; y++) {
       for (int x = 0; x < VIDEO_COL; x++) {
         uint8_t p = getbit(f->pixel, y * VIDEO_COL + x);
         putch(p ? CHAR_BLACK : CHAR_WHITE);
       }
       putch('\n');
     }

     if (has_audio) {
       int should_play = (AUDIO_FREQ / FPS) * sizeof(int16_t);
       if (should_play > audio_left) should_play = audio_left;
       while (should_play > 0) {
         int len = (should_play > 4096 ? 4096 : should_play);
         sbuf.end = sbuf.start + len;
         io_write(AM_AUDIO_PLAY, sbuf);
         sbuf.start += len;
         should_play -= len;
       }
       audio_left -= should_play;
     }

     uint64_t next = now + (1000 * 1000 / FPS);
     sleep_until(next);
     now = next;
  }
  return 0;
}