#include "game_timer.h"

static bool is_module_playing;
static bool is_game_playing;

static bool game_time_count_up;

// in ms
static uint32_t game_time_left;
static uint32_t module_time_left;

static void game_timer_task(void *arg);
static uint32_t sat_sub(uint32_t x, uint32_t y);

static void write_game_time(uint32_t millis) {
    if (millis > 60'000) {
        int disp_time = (millis / 60'000) * 100;
        disp_time = disp_time + ((millis % 60'000) / 1'000);
        set_game_sseg_num(disp_time, 2);
    } else {
        set_game_sseg_num(millis / 100, 1);
    }
}
static void write_module_time(uint32_t millis) {
    if (millis > 60'000) {
        int disp_time = (millis / 60'000) * 100;
        disp_time = disp_time + ((millis % 60'000) / 1'000);
        set_module_sseg_num(disp_time, 2);
    } else {
        set_module_sseg_num(millis / 100, 1);
    }
}

void init_game_module_timer(void) {
    xTaskCreate(game_timer_task, "game_module_timer", 4096, NULL, 10, NULL);
}

void start_game_timer(void) {
    is_game_playing = true;
}

void stop_game_timer(void) {
    is_game_playing = false;
}

void start_module_timer(void) {
    is_module_playing = true;
}

void stop_module_timer(void) {
    is_module_playing = false;
}

void set_game_time(int32_t new_time) {
    if (new_time > 0) {
        game_time_count_up = false;
        game_time_left = new_time;
    } else {
        game_time_count_up = true;
        game_time_left = -new_time;
    }
    write_game_time(game_time_left);
}

int32_t get_game_time() {
    if (game_time_count_up) {
        return -((int32_t) game_time_left);
    } else {
        return ((int32_t) game_time_left);
    }
}

void set_module_time(uint32_t new_time) {
    module_time_left = new_time;
    write_module_time(module_time_left);
}

uint32_t get_module_time() {
    return module_time_left;
}

/// Issues a time penalty to the game time
void time_penalty(uint32_t penalty) {
    if (game_time_count_up) {
        game_time_left += penalty;
    } else {
        game_time_left = sat_sub(game_time_left, penalty);
        if (game_time_left == 0) {
            game_time_count_up = true;
        }
    }
}

static void game_timer_task(void *arg)
{
    TickType_t lastWakeTime = xTaskGetTickCount();

    const uint32_t frequency = 100;
    while (1) {
        vTaskDelayUntil( &lastWakeTime, pdMS_TO_TICKS(frequency));
        if (is_game_playing) {
            if (game_time_count_up) {
                game_time_left += frequency;
            } else {
                game_time_left = sat_sub(game_time_left, frequency);
                if (game_time_left == 0) {
                    game_time_count_up = true;
                }
            }
            write_game_time(game_time_left);
        }
        if (is_module_playing) {
            module_time_left = sat_sub(module_time_left, frequency);
            write_module_time(module_time_left);
        }
    }

    vTaskDelete(NULL);
}

static uint32_t sat_sub(uint32_t x, uint32_t y)
{
    uint32_t res = x - y;
    res &= -(res <= x);
    return res;
}
