#include "user_misc.h"
#include "device.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"

struct data_gas_info_s data_gas_info __attribute__((section(".bss_ccm")));
struct led_ctrl_s led_ctrl[2];

void led_init(uint16_t led)
{
    GPIO_InitTypeDef  GPIO_InitStructure = {
        .GPIO_Pin = led,
        .GPIO_Mode = GPIO_Mode_OUT,
        .GPIO_OType = GPIO_OType_PP,
        .GPIO_Speed = GPIO_Speed_2MHz,
        .GPIO_PuPd = GPIO_PuPd_NOPULL,
    };

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
    GPIO_Init(PORT_LED, &GPIO_InitStructure);
    led_off(led);
    for (uint8_t i = 0; i < 2; i++) {
        led_ctrl[i].state = 0;
    }
}

void led_off(uint16_t led)
{
    GPIO_SetBits(PORT_LED, led);
}

void led_on(uint16_t led)
{
    GPIO_ResetBits(PORT_LED, led);
}

void led_toggle(uint16_t led)
{
    GPIO_ToggleBits(PORT_LED, led);
}

void led_det_temperature_init(void)
{
    led_ctrl[0].times_volatile = 0;
    led_ctrl[0].times = ~0;
    led_ctrl[0].on_volatile = 0;
    led_ctrl[0].on = 10;
    led_ctrl[0].off = 10;
    led_ctrl[0].state = LED_BLINK_ON;
}

void led_det_temperature_loop(void)
{
    static uint32_t ms = 0;
    static uint32_t det_temp = 0;
    uint32_t diff;

    if (system_tick_cnt < (ms + 1000 * 60)) {
        return;
    }
    ms = system_tick_cnt;

    if (det_temp < device_data.detector_temperature) {
        diff = device_data.detector_temperature - det_temp;
    } else {
        diff = det_temp - device_data.detector_temperature;
    }
    det_temp = device_data.detector_temperature;
    if (diff < 20) {
        /* deterator temperature is stable */
        led_ctrl[0].state = LED_ON;
    } else {
        /* deterator temperature is not stable */
        led_det_temperature_init();
    }
}

void led_sig_loop(void)
{
    static uint32_t ms = 0;
    uint8_t count = 0;
    uint8_t led;

    if (system_tick_cnt < (ms + 100)) {
        return;
    }
    ms = system_tick_cnt;

    for (uint8_t ch = 0; ch < 4; ch++) {
        if (data_gas_info.id[ch] == GAS_ID_INVALID) {
            count++;
            continue;
        } else {
            if (device_data.gas_sig[ch] < 10) {
                break;
            } else {
                count++;
            }
        }
    }
    if (count >= 4) {
        /* sig is all normal, LED on */
        led = LED_ON;
    } else {
        /* sig is not all normal, LED off */
        led = LED_OFF;
    }

    if (led_ctrl[1].state == LED_BLINK_OFF || led_ctrl[1].state == LED_BLINK_ON) {
        led_ctrl[1].state_save = led;
    } else {
        led_ctrl[1].state = led;
    }
}

void led_loop(void)
{
    static uint32_t ms = 0;
    uint16_t led;

    if (system_tick_cnt < (ms + 100)) {
        return;
    }
    ms = system_tick_cnt;

    for (uint8_t i = 0; i < 2; i++) {
        led = (i == 0) ? LED1 : LED2;
        if (led_ctrl[i].state == LED_OFF) {
            led_off(led);
        } else if (led_ctrl[i].state == LED_ON) {
            led_on(led);
        } else {
            if (led_ctrl[i].state == LED_BLINK_OFF) {
                led_off(led);
                led_ctrl[i].off_volatile++;
                if (led_ctrl[i].off_volatile >= led_ctrl[i].off) {
                    led_ctrl[i].off_volatile = 0;
                    led_ctrl[i].on_volatile = 0;
                    led_ctrl[i].state = LED_BLINK_ON;
                    led_ctrl[i].times_volatile++;
                    if (led_ctrl[i].times == ~0) {
                        /* blink forever */
                    } else if (led_ctrl[i].times_volatile >= led_ctrl[i].times) {
                        led_ctrl[i].state = led_ctrl[i].state_save;
                    }
                }
            } else if (led_ctrl[i].state == LED_BLINK_ON) {
                led_on(led);
                led_ctrl[i].on_volatile++;
                if (led_ctrl[i].on_volatile >= led_ctrl[i].on) {
                    led_ctrl[i].on_volatile = 0;
                    led_ctrl[i].off_volatile = 0;
                    led_ctrl[i].state = LED_BLINK_OFF;
                }
            }
        }
    }
}

void heat_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure = {
        .GPIO_Pin = PIN_HEAT,
        .GPIO_Mode = GPIO_Mode_OUT,
        .GPIO_OType = GPIO_OType_PP,
        .GPIO_Speed = GPIO_Speed_2MHz,
        .GPIO_PuPd = GPIO_PuPd_NOPULL,
    };

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    GPIO_Init(PORT_HEAT, &GPIO_InitStructure);
    heat_off();
    device_status.cali_activate_status = 0;
}

void heat_on(void)
{
    GPIO_SetBits(PORT_HEAT, PIN_HEAT);
}

void heat_off(void)
{
    GPIO_ResetBits(PORT_HEAT, PIN_HEAT);
}

void gas_id_init(void)
{
    uint32_t id[4];
    uint32_t available;
    uint8_t cnt_invalid, cnt_ref, cnt_gas;
    uint8_t ref[4];
    uint8_t ref_idx;

    id[0] = bitmap_main->gas1_sub_id;
    id[1] = bitmap_main->gas2_sub_id;
    id[2] = bitmap_main->gas3_sub_id;
    id[3] = bitmap_main->gas4_sub_id;
    available = bitmap_main->gasn_avail_bitmap;
    cnt_invalid = 0;
    cnt_ref = 0;
    cnt_gas = 0;
    
    for (uint8_t i = 0; i < 4; i++) {
        if ((available >> i) & 1) { /* channel not available */
            data_gas_info.id[i] = 0xFFFFFFFF;
            cnt_invalid++;
        } else {
            if (id[i] == 0) { /* channel is reference */
                data_gas_info.id[i] = 0;
                ref[cnt_ref] = i;
                cnt_ref++;
            } else { /* channel is normal gas */
                data_gas_info.id[i] = id[i];
                cnt_gas++;
            }
        }
    }
    ref_idx = 0;
    for (uint8_t i = 0; i < 4; i++) {
        if (data_gas_info.id[i] == 0xFFFFFFFF) {
            data_gas_info.ref[i] = 0;
        } else if (data_gas_info.id[i] == 0) {
            data_gas_info.ref[i] = 0;
        } else {
            if (cnt_ref == 1) {
                data_gas_info.ref[i] = ref[0];
            } else if (cnt_ref == 2) {
                data_gas_info.ref[i] = ref[ref_idx];
                ref_idx++;
            } else {
                while (1);
            }
        }
    }
    if ((cnt_invalid == 2) && (cnt_ref == 1) && (cnt_gas == 1)) {
        data_gas_info.comb = GAS_COMB_1_1;
    } else if ((cnt_invalid == 0) && (cnt_ref == 2) && (cnt_gas == 2)) {
        data_gas_info.comb = GAS_COMB_2_2;
    } else if ((cnt_invalid == 0) && (cnt_ref == 1) && (cnt_gas == 3)) {
        data_gas_info.comb = GAS_COMB_3_1;
    } else if ((cnt_invalid == 1) && (cnt_ref == 1) && (cnt_gas == 2)) {
        data_gas_info.comb = GAS_COMB_2_1;
    } else {
        data_gas_info.comb = GAS_COMB_NULL;
        while (1);
    }
}
