#include <SPI.h>
#include <GD.h>
#include "debug.h"

// ----------------------------------------------------------------------
//     controller: buttons on Arduino pins 3,4,5,6 with 7 grounded
// ----------------------------------------------------------------------

static void controller_init()
{
  // Configure input pins with internal pullups
  byte i;
  for (i = 3; i < 7; i++) {
    pinMode(i, INPUT);
    digitalWrite(i, HIGH);
  }
  // drive pin 7 low
  pinMode(7, OUTPUT);
  digitalWrite(7, 0);
}

#define CONTROL_LEFT  1
#define CONTROL_RIGHT 2
#define CONTROL_UP    4
#define CONTROL_DOWN  8

static byte controller_sense()
{
  byte r = 0;
  if (!digitalRead(6))
    r |= CONTROL_LEFT;
  if (!digitalRead(5))
    r |= CONTROL_RIGHT;
  if (!digitalRead(4))
    r |= CONTROL_DOWN;
  if (!digitalRead(3))
    r |= CONTROL_UP;
  return r;
}

#define FLOCAL 2
#define SEL_local() digitalWrite(FLOCAL, LOW)
#define UNSEL_local() digitalWrite(FLOCAL, HIGH)

struct dirent {
  char name[12];
  uint16_t length;
  uint32_t addr;
};

static uint32_t flash_readn(byte *dst, uint32_t src, size_t n)
{
  SEL_local();
  SPI.transfer(0x03);
  SPI.transfer((byte)(src >> 16));
  SPI.transfer((byte)(src >> 8));
  SPI.transfer((byte)(src >> 0));
  while (n--) {
    *dst++ = SPI.transfer(0);
    src++;
    if ((511 & (uint16_t)src) == 264)
      src = src - 264 + 512;
  }
  UNSEL_local();
  return src;
}

uint16_t samp_len;
uint32_t samp_ptr;

static dirent de;

static byte find_name(uint32_t &ptr, uint16_t &len, uint32_t dirptr, char *name)
{
  while (true) {
    dirptr = flash_readn((byte*)&de, dirptr, sizeof(de));
    if (de.name[0] == 0)
      return 0;   // end of dir, no match found
    if (strcmp(de.name, name) == 0) {
      len = de.length;
      ptr = de.addr;
      return 1;
    }
  }
}

static dirent instruments[16];

struct active {
  byte instrument;
  uint32_t addr;
  uint16_t pos, length;
  uint16_t x, y;
};
#define NACTIVE 3
static active playing[NACTIVE];

active *findidle()
{
  byte i;
  for (i = 0; i < NACTIVE; i++)
    if (playing[i].addr == 0)
      return &playing[i];
  return NULL;
}

static void kickoff(byte i)
{
  active *p = findidle();
  if (p) {
    p->addr = instruments[i].addr;
    p->pos = 0;
    p->length = instruments[i].length;
    p->x = 0;
    p->y = 16 + 16 * i;
#if 0
    Serial.println(instruments[i].name);
    Serial.println(p->addr, HEX);
    Serial.println(p->length, HEX);
    int i;
    uint32_t src = p->addr;
    long tot = 0;
    for (i = 0; i < p->length; i++) {
      byte v;
      src = flash_readn(&v, src, 1);
      tot += v;
    }
    Serial.println(tot, HEX);
#endif
  }
}

#include "r.h"

static void fromflash(uint16_t dst, uint32_t src, uint16_t len)
{
  while (len--) {
    byte v;
    src = flash_readn(&v, src, 1);
    GD.wr(dst++, v);
  }
}

static byte sprstr(int x, int y, byte spr, const char *txt)
{
  char c;
  while ((c = *txt++) != 0) {
    GD.sprite(spr++, x, y, (c - ' ') >> 2, 8 + ((c & 3) << 1), 0);
    x += 11;
  }
  return spr;
}

void setup()
{
  controller_init();
  GD.begin();
  GD.ascii();
  Serial.begin(1000000);
  pinMode(FLOCAL, OUTPUT);
  UNSEL_local();
  GD.wr(IOMODE, 'F');

  uint32_t assetroot = 512L * 640;
  uint32_t dk;
  uint16_t dkl;
  find_name(dk, dkl, assetroot, "voice");

  uint32_t ptr;
  ptr = dk;
  byte i = 0;
  dirent *pi = instruments;
  byte spr = 128;
  while (ptr = flash_readn((byte*)pi, ptr, sizeof(de)), pi->name[0]) {
    Serial.println(pi->name);
    // GD.putstr(0, 2 + 2 * i, pi->name);
    spr = sprstr(0, 16 + 16 * i, spr, pi->name);
    pi++, i++;
  }
  GD.copy(RAM_SPRIMG, r_img, sizeof(r_img));
  GD.copy(RAM_SPRPAL, r_pal, sizeof(r_pal));

  uint32_t sd;
  uint16_t sdl;
  if (0) { 
    find_name(dk, dkl, assetroot, "pickups");
    find_name(sd, sdl, dk, "pal");
    fromflash(RAM_SPRPAL, sd, sdl);
    find_name(sd, sdl, dk, "img");
    fromflash(RAM_SPRIMG, sd, sdl);
  }
  find_name(dk, dkl, assetroot, "fonts");
  find_name(sd, sdl, dk, "font16");
  fromflash(RAM_SPRIMG, sd, sdl);
  GD.wr16(PALETTE4A,     RGB(255,255,255));
  GD.wr16(PALETTE4A + 2, RGB(128,128,128));
  GD.wr16(PALETTE4A + 4, RGB( 64, 64, 64));
  GD.wr16(PALETTE4A + 6, TRANSPARENT);
}

#include "soundbuffer.h"

#define SOUNDBUFFER 0x3f00

void loop()
{
  GD.microcode(soundbuffer_code, sizeof(soundbuffer_code));

  byte writepointer = 0;
  for (;;) {
    active *p;
    byte i;

    byte readpointer = GD.rd(COMM+0);
    byte fullness = writepointer - readpointer;
    while (fullness < 254) {
      char total = 0;
      for (p = playing, i = 0; i < NACTIVE; p++, i++) {
        if (p->addr) {
          char v;
          p->addr = flash_readn((byte*)&v, p->addr, 1);
          total += (v >> 1);
          if (++p->pos >= p->length)
            p->addr = 0;
        }
      }
      // Serial.println(micros() - t0, DEC);
        
      GD.wr(SOUNDBUFFER + writepointer++, total);
      fullness++;
    }

    GD.waitvblank();
    GD.__wstartspr(0);
    for (p = playing, i = 0; i < NACTIVE; p++, i++) {
      if (p->addr) {
        char c = '0' + (p->x >> 16) % 10;
        GD.xsprite(200 + p->x, p->y, 0, 0, (c - ' ') >> 2, 8 + ((c & 3) << 1));
        p->x += 4;
      } else {
        GD.xhide();
      }
    }
    GD.__end();

    if (0) {
      kickoff(random(12));
    }
    static byte prev[4];
    for (i = 0; i < 4; i++) {
      byte v = digitalRead(3 + i) == 0;
      if (v && !prev[i]) {
        kickoff(4 + i);
      }
      prev[i] = v;
    }
  }
}
