/*
 * RETRO TECH COMMUNITY
 * GAT Addon Firmware
 * by true
 *
 * version 0.0.3
 * Last Update 20240808
 *
 * code was made for different random addons I designed for dc32,
 * then adapted to each one. so things might be a mess.
 *
 * I had only a few hours on and off over a week to get the code done.
 * lots of shit was copied from demo code and adapted to work. certainly
 * isn't the best way to get this done.
 *
 * I know I could save some power, but seeing as all the badges burn it in
 * LEDs and almost nobody else power optimizes their badge code, who cares?
 *
 * sorry. wasn't procrastination I swear. all the ideas were last minute.
 */

#include <ch32v00x.h>
#include <stdint.h>

#include "src/adc.h"
#include "src/btn.h"
#include "src/config.h"
#include "src/i2c.h"
#include "src/led.h"
#include "src/led_rgbprog.h"
#include "src/rand.h"
#include "src/ui.h"


void systick_init(void)
{
    SysTick->CMP = (SystemCoreClock / 512) - 1;     // we want a 512Hz interrupt
    SysTick->CNT = 0;                               // clear counter
    SysTick->CTLR = 0xF;                            // start counter in /1 mode, enable interrupts, auto-reset counter
    SysTick->SR = 0;                                // clear count comparison flag

    NVIC_EnableIRQ(SysTicK_IRQn);                   // enable interrupt
}

void gpio_init()
{
    GPIO_InitTypeDef gpio = {0};

    gpio.GPIO_Speed = GPIO_Speed_2MHz;

    // GAT SoftI2C, USART TX/RX; currently unused
    gpio.GPIO_Mode = GPIO_Mode_IPD;
    gpio.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;
    GPIO_Init(GPIOD, &gpio);

    gpio.GPIO_Pin = GPIO_Pin_1;
    GPIO_Init(GPIOA, &gpio);

    // lightsense LED cathode
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Pin = GPIO_Pin_2;
    GPIO_Init(GPIOA, &gpio);

    // lightsense LED anode
    gpio.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOD, &gpio);

    // unused pins
    gpio.GPIO_Mode = GPIO_Mode_IPD;
    gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_7;
    GPIO_Init(GPIOC, &gpio);

    gpio.GPIO_Pin = GPIO_Pin_2;
    GPIO_Init(GPIOD, &gpio);

    // I2C SCL, SCA for on-board devices
    gpio.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    gpio.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_Init(GPIOC, &gpio);

    // IS_SDB (PC3) IS31FL3729 shutdown pin (active low)
    // WP (PC6) for EEPROM
    GPIOC->BCR = GPIO_Pin_3;
    GPIOC->BSHR = GPIO_Pin_6;
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_6;
    GPIO_Init(GPIOC, &gpio);

    // BTN1 (PC5) pull up, BTN2 (PC4) pull down
    gpio.GPIO_Pin = GPIO_Pin_4;
    gpio.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(GPIOC, &gpio);

    gpio.GPIO_Pin = GPIO_Pin_5;
    gpio.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOC, &gpio);
}

int main(void)
{
    // configure core
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();

    // enable peripheral clocks
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR   | RCC_APB1Periph_I2C1, ENABLE);
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO  | RCC_APB2Periph_GPIOA |
                            RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
                            RCC_APB2Periph_ADC1  | RCC_APB2Periph_TIM1, ENABLE);

    // configure gpio pins, hard buttons (used for settings reset)
    gpio_init();

    // get saved settings, or reset if BTN1 is pushed
    i2c_init();
    userconf_load((BTN_PORT->INDR & (1 << BTN1_PIN)) ? 0 : 1);

    // configure hardware
    adc_init();
    btn_init();
    led_init();

    // configure random
    tinymt32_init(&tinymt32_s, DBGMCU_GetCHIPID() | userconf.checksum);

    // configure UI
    ui_init();

    // configure systick interrupt
    systick_init();

    // set up LEDs initially
    led_rgb_firstrun();

    // do system shit
    while(1) {
        __WFI();

        // after sending LEDs, run LED render program
        // to update the LED buffer for next time
        // we have approx 7ms to render... plenty of time
        if ((ticnt & 0x3) == 0) {
            ui_render();
        }
    }
}
