#include <gui/icon.h>
#include "dice_app_icons.h"

#define TAG "DiceApp"

#define DICE_TYPES 8

#define HISTORY_SIZE 10
#define HISTORY_COL HISTORY_SIZE / 2
#define HISTORY_START_POST_X 2
#define HISTORY_START_POST_Y 10
#define HISTORY_STEP_X 66
#define HISTORY_STEP_Y 10
#define HISTORY_X_GAP 11

#define MAX_DICE_COUNT 10
#define MAX_COIN_FRAMES 9
#define MAX_DICE_FRAMES 4

#define DICE_X 45
#define DICE_Y 6
#define DICE_Y_T 0

#define DICE_GAP 44

#define RESULT_BORDER_X 44
#define RESULT_OFFSET 20

#define SWIPE_DIST 11

const Icon* coin_heads_start[] = {&I_coin_1, &I_coin_2};
const Icon* coin_heads_end[] = {&I_coin_7, &I_coin_1};
const Icon* coin_tails_start[] = {&I_coin_5, &I_coin_6};
const Icon* coin_tails_end[] = {&I_coin_4, &I_coin_5};
const Icon* coin_frames[] = {
    &I_coin_1,
    &I_coin_2,
    &I_coin_3,
    &I_coin_4,
    &I_coin_5,
    &I_coin_6,
    &I_coin_3,
    &I_coin_7,
    &I_coin_1,
};

const int8_t result_frame_pos_y[] = {-30, -20, -10, 0};
const Icon* dice_frames[] = {
    &I_d4_1,   &I_d4_2,   &I_d4_3,   &I_d4_1, // d4
    &I_d6_1,   &I_d6_2,   &I_d6_3,   &I_d6_4, // d6
    &I_d8_1,   &I_d8_2,   &I_d8_3,   &I_d8_4, // d8
    &I_d10_1,  &I_d10_2,  &I_d10_3,  &I_d10_4, // d10
    &I_d12_1,  &I_d12_2,  &I_d12_3,  &I_d12_4, // d12
    &I_d20_1,  &I_d20_2,  &I_d20_3,  &I_d20_4, // d20
    &I_d100_1, &I_d100_2, &I_d100_3, &I_d100_4, // d100
};

const uint8_t screen_pos[] = {};

typedef struct {
    uint8_t type;
    int x;
    int y;
    char* name;
} Dice;

typedef struct {
    int8_t index;
    uint8_t count;
    uint8_t result;
} History;

static const Dice dice_types[] = {
    {2, 0, 0, "Coin"},
    {4, 0, 0, "d4"},
    {6, 0, 0, "d6"},
    {8, 0, 0, "d8"},
    {10, 0, 0, "d10"},
    {12, 0, 0, "d12"},
    {20, 0, 0, "d20"},
    {100, 0, 0, "d100"},
};

typedef enum { EventTypeTick, EventTypeKey } EventType;
typedef enum {
    SelectState,
    SwipeLeftState,
    SwipeRightState,
    AnimState,
    AnimResultState,
    ResultState,
    HistoryState,
} AppState;

typedef struct {
    EventType type;
    InputEvent input;
} AppEvent;

typedef struct {
    AppState app_state;
    uint16_t roll_result;
    uint8_t rolled_dices[MAX_DICE_COUNT];
    uint8_t anim_frame;
    uint8_t dice_index;
    uint8_t dice_count;
    int8_t result_pos;
    Dice dices[DICE_TYPES];
    History history[HISTORY_SIZE];
    FuriMutex* mutex;
} State;

void init(State* const state) {
    state->app_state = SelectState;
    state->roll_result = 0;
    state->dice_index = 0;
    state->anim_frame = 0;
    state->dice_count = 1;

    for(uint8_t i = 0; i < DICE_TYPES; i++) {
        state->dices[i] = dice_types[i];
        state->dices[i].x = DICE_X + (i * DICE_GAP);
        state->dices[i].y = i == 0 ? DICE_Y_T : DICE_Y;
    }

    for(uint8_t i = 0; i < HISTORY_SIZE; i++) {
        state->history[i].index = -1;
    }
}

void add_to_history(State* const state, uint8_t index, uint8_t count, uint8_t result) {
    uint8_t last = HISTORY_SIZE - 1;
    if(state->history[last].index >= 0) {
        for(uint8_t i = 1; i < HISTORY_SIZE; i++) {
            state->history[i - 1] = state->history[i];
        }

        state->history[last].index = index;
        state->history[last].count = count;
        state->history[last].result = result;
        return;
    }

    for(uint8_t i = 0; i < HISTORY_SIZE; i++) {
        if(state->history[i].index < 0) {
            state->history[i].index = index;
            state->history[i].count = count;
            state->history[i].result = result;
            return;
        }
    }
}

void coin_set_start(uint16_t type) {
    if(type == 1) {
        coin_frames[0] = coin_heads_start[0];
        coin_frames[1] = coin_heads_start[1];
    } else {
        coin_frames[0] = coin_tails_start[0];
        coin_frames[1] = coin_tails_start[1];
    }
}

void coin_set_end(uint16_t type) {
    if(type == 1) {
        coin_frames[MAX_COIN_FRAMES - 2] = coin_heads_end[0];
        coin_frames[MAX_COIN_FRAMES - 1] = coin_heads_end[1];
    } else {
        coin_frames[MAX_COIN_FRAMES - 2] = coin_tails_end[0];
        coin_frames[MAX_COIN_FRAMES - 1] = coin_tails_end[1];
    }
}

bool isResultVisible(AppState state, uint8_t dice_index) {
    return (state == ResultState || state == AnimResultState) && dice_index != 0;
}

bool isDiceNameVisible(AppState state) {
    return state != SwipeLeftState && state != SwipeRightState;
}

bool isDiceButtonsVisible(AppState state) {
    return isDiceNameVisible(state) && state != AnimResultState && state != ResultState &&
           state != AnimState && state != HistoryState;
}

bool isOneDice(uint8_t dice_index) {
    return dice_index == 0 || dice_index == 7;
}

bool isDiceSettingsDisabled(AppState state, uint8_t dice_index) {
    return isOneDice(dice_index) || state == ResultState || state == AnimResultState ||
           state == AnimState || state == HistoryState;
}

bool isAnimState(AppState state) {
    return state == SwipeLeftState || state == SwipeRightState || state == AnimResultState ||
           state == AnimState;
}

bool isMenuState(AppState state) {
    return state == SwipeLeftState || state == SwipeRightState || state == SelectState;
}