#include "common.h"

/*  Defines  */

enum TITLE_STATE_T : uint8_t {
    STATE_TOP = 0,
    STATE_CREDIT,
    STATE_STARTED,
};

#define IMG_TITLE_BANANA1_W 40
#define IMG_TITLE_BANANA1_H 48
#define IMG_TITLE_BANANA2_W 40
#define IMG_TITLE_BANANA2_H 32
#define IMG_TITLE_BANANA3_W 32
#define IMG_TITLE_BANANA3_H 64
#define IMG_TITLE_LETTERS_W 11
#define IMG_TITLE_LETTERS_H 10

/*  Local Functions  */

static void handleTop(void);
static void handleAnyButton(void);

static void onTop(void);
static void onStart(void);
static void onCredit(void);

static void drawTop(void);
static void drawCredit(void);
static void drawText(const char *p, int16_t y);

/*  Local Functions (macros)  */

#define callHandlerFunc(n)  ((void (*)(void)) pgm_read_ptr(handlerFuncTable + n))()
#define callDrawerFunc(n)   ((void (*)(void)) pgm_read_ptr(drawerFuncTable + n))()

/*  Local Constants  */

PROGMEM static const uint8_t imgTitleBanana1[] = { // 40x48
    0xF0, 0xF0, 0xFC, 0xFC, 0xFE, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xE0, 0xE0, 0xC0,
    0xC0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFC,
    0xFC, 0xF8, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x07, 0x1F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x03, 0x03, 0x07, 0x0F, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x0F
};

PROGMEM static const uint8_t imgTitleBanana2[] = { // 40x32
    0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
    0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
    0xFC, 0xFC, 0xFC, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x1F, 0x1F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F,
    0x3F, 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
    0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x1F
};

PROGMEM static const uint8_t imgTitleBanana3[] = { // 32x64
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xFC, 0x3C, 0x18,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x80, 0xE0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0,
    0xF8, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x80, 0x00,
    0x80, 0x80, 0xC0, 0xE0, 0xE0, 0xF0, 0xF8, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x78,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x0F, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x3F,
    0x1F, 0x0F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

PROGMEM static const uint8_t imgTitleLetters[][22] = { // 11x10 x6
    {   // 'B'
        0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x11, 0x11, 0x3F, 0xFF, 0xEE, 0xC0,
        0x00, 0x02, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x03, 0x01
    },{ // 'N'
        0x01, 0xFF, 0xFF, 0x0F, 0x3F, 0xFC, 0xF0, 0xC1, 0xFF, 0xFF, 0x01,
        0x02, 0x03, 0x03, 0x02, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00
    },{ // 'A'
        0x00, 0x80, 0xE0, 0x7C, 0x5F, 0x4F, 0x7F, 0xFC, 0xF0, 0x80, 0x00,
        0x02, 0x03, 0x03, 0x02, 0x00, 0x00, 0x02, 0x03, 0x03, 0x03, 0x02
    },{ // 'O'
        0xFC, 0xFE, 0xFF, 0x03, 0x01, 0x01, 0x01, 0x03, 0xFF, 0xFE, 0xFC,
        0x00, 0x01, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x03, 0x01, 0x00
    },{ // 'S'
        0x8E, 0x9F, 0x3F, 0x39, 0x71, 0xF3, 0xE7, 0xC7, 0x00, 0x00, 0x00,
        0x03, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00
    },{ // 'E'
        0x01, 0xFF, 0xFF, 0xFF, 0x11, 0x11, 0x13, 0x17, 0x87, 0x80, 0x00,
        0x02, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x00
    }
};

PROGMEM static const char creditText[] = \
        "- " APP_TITLE " -\0\0\0" APP_RELEASED "\0PROGRAMMED BY OBONO\0\0" \
        "THIS PROGRAM IS\0RELEASED UNDER\0THE MIT LICENSE.\0\e";

PROGMEM static void(*const handlerFuncTable[])(void) = {
    handleTop, handleAnyButton
};

PROGMEM static void(*const drawerFuncTable[])(void) = {
    drawTop, drawCredit
};

/*  Local Variables  */

static TITLE_STATE_T  state = STATE_TOP;

/*---------------------------------------------------------------------------*/
/*                              Main Functions                               */
/*---------------------------------------------------------------------------*/

void initTitle(void)
{
    onTop();
}

MODE_T updateTitle(void)
{
    callHandlerFunc(state);
    randomSeed(rand() ^ micros()); // Shuffle random
    return (state == STATE_STARTED) ? MODE_GAME : MODE_TITLE;
}

void drawTitle(void)
{
    if (state == STATE_STARTED) return;
    callDrawerFunc(state);
    isInvalid = false;
}

/*---------------------------------------------------------------------------*/
/*                             Control Functions                             */
/*---------------------------------------------------------------------------*/

static void handleTop(void)
{
    counter++;
    handleMenu();
    isInvalid = true;
}

static void handleAnyButton(void)
{
    if (ab.buttonDown(A_BUTTON | B_BUTTON)) {
        playSoundClick();
        state = STATE_TOP;
        isInvalid = true;
    }
}

/*---------------------------------------------------------------------------*/
/*                               Menu Handlers                               */
/*---------------------------------------------------------------------------*/

static void onTop(void)
{
    clearMenuItems();
    addMenuItem(F("GAME START"), onStart);
    addMenuItem(F("CREDIT"), onCredit);
    setMenuItemPos(0);
    setMenuCoords(28, 0, 72, 11, false, true);
    counter = 0;
    state = STATE_TOP;
    isInvalid = true;
}

static void onStart(void)
{
    state = STATE_STARTED;
}

static void onCredit(void)
{
    playSoundClick();
    state = STATE_CREDIT;
    isInvalid = true;
}

/*---------------------------------------------------------------------------*/
/*                              Draw Functions                               */
/*---------------------------------------------------------------------------*/

static void drawTop(void)
{
    ab.clear();

    /*  Title letters  */
    uint32_t pattern = 2075517264UL; // "BANANONSENSE"
    int16_t x = 1;
    for (uint8_t i = 0; i < 12; i++) {
        uint8_t idx = pattern % 6;
        ab.drawBitmap(x + sin((counter - i * 13) * PI / 32.0) * 2,
            28 + sin((counter - i * 17) * PI / 64.0) * 14,
            imgTitleLetters[idx], IMG_TITLE_LETTERS_W, IMG_TITLE_LETTERS_H);
        pattern /= 6;
        x += (idx < 4) ? IMG_TITLE_LETTERS_W : idx * 2;
    }

    /*  Large banana  */
    ab.drawBitmap(8, 16, imgTitleBanana1, IMG_TITLE_BANANA1_W, IMG_TITLE_BANANA1_H, INVERT);
    ab.drawBitmap(48, 32, imgTitleBanana2, IMG_TITLE_BANANA2_W, IMG_TITLE_BANANA2_H, INVERT);
    ab.drawBitmap(88, 0, imgTitleBanana3, IMG_TITLE_BANANA3_W, IMG_TITLE_BANANA3_H, INVERT);

    /*  Others  */
    if (record.banana > 0) {
        ab.printEx(0, 53, F("TOTAL"));
        ab.printEx(0, 59, record.banana);
    }
    drawMenuItems(true);
}

static void drawCredit(void)
{
    if (isInvalid) {
        ab.clear();
        drawText(creditText, 11);
    }
}

/*---------------------------------------------------------------------------*/

static void drawText(const char *p, int16_t y)
{
    while (pgm_read_byte(p) != '\e') {
        uint8_t len = strnlen_P(p, 21);
        ab.printEx(64 - len * 3, y, (const __FlashStringHelper *)p);
        p += len + 1;
        y += (len == 0) ? 2 : 6;
    }
}
