// vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab
/*
 * This file is part of the libopencm3 project.
 *
 * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
 *
 * This library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/cdc.h>

#include "usb_cdcacm.h"

uint32_t n_millis_total = 0;
uint32_t n_millis_cur = 0;
uint8_t n_millis_loops = 0;
usbd_device *usbd_dev;
static uint32_t game_time = 14 * 60 * 1000; // 15 minutes of 1ms ticks

void sys_tick_handler(void) {
    ++n_millis_cur;
    ++n_millis_total;

    if (n_millis_total >= game_time) {
        gpio_clear(GPIOC, GPIO13); // turn on LED
        return;
    }

    /* calculate remaining seconds to print out on the serial port, only do this every
       1000 millis or so */
    if (n_millis_loops >= 4) {
        char time_remaining[6];
        uint32_t seconds_remaining = (uint32_t)((game_time - n_millis_total) / 1000);
        int minutes = (int)(seconds_remaining / 60);
        int seconds = (int)(seconds_remaining % 60);
        sprintf(time_remaining, "%02d:%02d", minutes, seconds);
        for (int i = 0; i < 5; i++) {
            cdcacm_putchar(time_remaining[i]);
        }
        cdcacm_putchar('\r');
        cdcacm_putchar('\n');
    }

    if (n_millis_cur >= 250) {
        gpio_toggle(GPIOC, GPIO13);
        ++n_millis_loops;
        n_millis_cur = 0;
        return;
    }

    return;
}

static void clock_setup(void) {
    /* Run CPU at 72MHz */
    rcc_clock_setup_in_hse_8mhz_out_72mhz();
    rcc_periph_clock_enable(RCC_GPIOA);
    rcc_periph_clock_enable(RCC_GPIOC);
    rcc_periph_clock_enable(RCC_AFIO);

    AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON;

    /* 72MHz / 8 => 9000000 counts per second */
    systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);

    /* 9000000 / 9000 = 1000 overflows per second - every 1ms one interrupt */
    /* SysTick interrupt every N clock pulses: set reload to N-1 */
    systick_set_reload(8999); // 1 ms
    systick_interrupt_enable();
    systick_counter_enable();
}

static void gpio_setup(void) {
    // Built-in LED on blue pill board, PC13
    gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ,
        GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
    gpio_set(GPIOC, GPIO13);
}

int main(void)
{
    clock_setup();
    gpio_setup();
    cdcacm_setup();

    gpio_set(GPIOC, GPIO13);

    while (1) {
        __asm__("nop");
    }

    return 0;
}

