/**
 * @file main.c
 * @brief Bike rear light implementation for ATTINY202.
 *
 */

#include <stdint.h>
#include <stdbool.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

#define BUTTON_PIN_MASK 0x01 // PA0 used as RESET/UPDI pin
#define PA1_SET_MASK 0x02    ///< LED 1–2
#define PA2_SET_MASK 0x04    ///< LED 3–6
#define PA3_SET_MASK 0x08    ///< LED 7–8
#define PA6_SET_MASK 0x40    ///< Green LED pin
#define PA7_SET_MASK 0x80    ///< Red LED

#define MAIN_LOOP_SLEEP 10U                 // Main loop delay in ms
#define BUTTON_LONG_PRESS_DURATION_MS 1000U // Long press threshold
#define BUTTON_SHORT_PRESS_DURATION_MS 50U  // Short press threshold
#define BUTTON_IGNORE_DURATION_MS 2000U     // Time button ignored after long press
#define BUTTON_CONFIRMATION_BLINK_LOOPS 10U // Blink animation for confirmation
#define BUTTON_CONFIRMATION_BLINK_DURATION_MS 50U
#define GLOW_BRIGHTNESS_MIN 10U
#define GLOW_BRIGHTNESS_MAX 100U
#define INTERNAL_VREF_MV 1024UL

/** Convert milliseconds to system ticks */
#define MS_TO_TICKS(ms) ((ms) / MAIN_LOOP_SLEEP)

typedef enum _Mode
{
    ANIMATION_BLINK,
    ANIMATION_GLOW,
    STATIC_FULL,
    MAX_COUNT,
} eMode;

/** Global flags */
volatile bool bLedEnabled = true;
volatile bool bBtnPressed = false;
volatile eMode eModeCurrent = ANIMATION_BLINK;

// Forward declarations
ISR(PORTA_PORT_vect);
static void software_reset(void);
static void configureLowPower(void);
void initPWM(void);
void setPWM_PA2(uint8_t duty);
static inline void leds_off(void);
static inline void leds_on(void);
static void battery_level_indicator(void);
static bool handleSwitch(void);
static inline void switchMode(void);
void ledAnimationBlink(bool resetCounters);
void ledAnimationGlow(void);
void ledStaticFull(void);

/**
 * @brief Main entry point
 */
int main(void)
{

    // Disable unused peripherals for power saving
    configureLowPower();

    // Configure LED pins as outputs
    VPORTA.DIR = (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK | PA6_SET_MASK | PA7_SET_MASK);
    initPWM();

    // Configure PA0 as input with pull-up
    VPORTA.DIR &= ~BUTTON_PIN_MASK;    // Input
    PORTA.PIN0CTRL = PORT_PULLUPEN_bm; // Pull-up enabled

    leds_off(); // Ensure all LEDs off at startup

    bool bLedEnabledOld = bLedEnabled;
    eModeCurrent = ANIMATION_BLINK; // Set the mode to start with

    while (true)
    {
        battery_level_indicator();
        bBtnPressed = handleSwitch(); // Check switch state

        // Light LEDs while button is pressed
        if (bBtnPressed)
        {
            leds_on();
        }
        else
        {
            leds_off();
        }

        // Long press detected --> show confirmation blink
        if (bLedEnabledOld != bLedEnabled)
        {
            bLedEnabledOld = bLedEnabled;

            for (uint8_t i = 0U; i < BUTTON_CONFIRMATION_BLINK_LOOPS; i++)
            {
                leds_off();
                _delay_ms(BUTTON_CONFIRMATION_BLINK_DURATION_MS);
                leds_on();
                _delay_ms(BUTTON_CONFIRMATION_BLINK_DURATION_MS);
                leds_off();
            }

            // Give time until button is released
            _delay_ms(BUTTON_IGNORE_DURATION_MS);

            // Activate the interrupt for PA0
            PORTA.PIN0CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc;

            set_sleep_mode(SLEEP_MODE_STANDBY); // Deepest sleep mode (standby)
            sleep_enable();                     // Enable sleep

            sei();       // Re-enable interrupts
            sleep_cpu(); // MCU sleeps here
        }
        else
        {
            if (bLedEnabled && !bBtnPressed)
            {
                switch (eModeCurrent)
                {
                case ANIMATION_BLINK:
                    ledAnimationBlink(false); // run normal blink
                    break;
                case ANIMATION_GLOW:
                    ledAnimationGlow();
                    break;
                case STATIC_FULL:
                    ledStaticFull();
                    break;
                default:
                    break;
                }
            }
        }

        // Sleep during delay instead of busy-wait
        sleep_enable();
        _delay_ms(MAIN_LOOP_SLEEP);
        sleep_disable();
    }
}

/**
 * @brief Move to next mode
 */
static inline void switchMode(void)
{
    eModeCurrent = (eModeCurrent + 1) % MAX_COUNT;
}

/**
 * @brief Configure for lowest power consumption
 */
static void configureLowPower(void)
{
    return;
    // Set unused pins as outputs LOW to prevent floating inputs
    // Floating inputs can cause extra current consumption
    PORTA.DIRSET = PIN4_bm | PIN5_bm; // Set PA4, PA5 as outputs if unused
    PORTA.OUTCLR = PIN4_bm | PIN5_bm; // Drive them LOW

    // Configure sleep mode
    set_sleep_mode(SLEEP_MODE_IDLE); // Use IDLE when TCA0 PWM needs to run
    // Use SLEEP_MODE_STANDBY for deep sleep when LEDs are off

    // Disable unused timers
    TCB0.CTRLA = 0; // Disable TCB0 if not used
    // TCA0 is used for PWM, keep it enabled

    // Disable ADC (Analog-to-Digital Converter)
    ADC0.CTRLA &= ~ADC_ENABLE_bm;

    // Disable AC (Analog Comparator)
    AC0.CTRLA &= ~AC_ENABLE_bm;

    // Disable unused USART
    USART0.CTRLB = 0;

    // Disable TWI (I2C) if not used
    TWI0.MCTRLA = 0;
    TWI0.SCTRLA = 0;

    // Disable SPI
    SPI0.CTRLA = 0;

    // Disable Watchdog Timer (if not needed)
    // Note: WDT can only be disabled during first 4 clock cycles after reset
    // CCP = CCP_IOREG_gc;
    // WDT.CTRLA = 0;

    // Disable BOD (Brown-Out Detection) in sleep modes for lower power
    // This is done via fuses, not runtime configurable

    // Disable digital input buffers on unused pins to save power
    // Only needed if pins are truly unused (floating)
    // PORTA.PIN4CTRL = PORT_ISC_INPUT_DISABLE_gc;  // PA4 if unused
    // PORTA.PIN5CTRL = PORT_ISC_INPUT_DISABLE_gc;  // PA5 if unused
}

/**
 * @brief Init PWM for PA2
 */
void initPWM(void)
{
    // No PORTMUX needed - PA2 is WO2 by default

    // TCA0 in normal mode (single slope PWM)
    TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP2EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;

    // Set period for ~19.5kHz PWM (5MHz / 256 = ~19.5kHz)
    TCA0.SINGLE.PER = 0xFF;

    // Start timer with DIV1 (no prescaler)
    TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc | TCA_SINGLE_ENABLE_bm;
}

/**
 * @brief Set PWM duty cycle for PA2
 */
void setPWM_PA2(uint8_t duty)
{
    TCA0.SINGLE.CMP2 = duty;
}

/**
 * @brief Turn off all controlled LEDs (PA1, PA2, PA3)
 */
static inline void leds_off(void)
{
    VPORTA.OUT &= (uint8_t) ~(PA1_SET_MASK | PA3_SET_MASK);
    setPWM_PA2(0U);
}

/**
 * @brief Turn on all controlled LEDs (PA1, PA2, PA3)
 */
static inline void leds_on(void)
{
    VPORTA.OUT |= (PA1_SET_MASK | PA3_SET_MASK);
    setPWM_PA2(255U);
}

/**
 * @brief Read battery voltage using internal 1.1V reference
 * @return Estimated battery voltage in millivolts
 */
uint16_t readBatteryVoltage(void)
{
    // Enable ADC with proper configuration
    ADC0.CTRLC = ADC_REFSEL_VDDREF_gc | // VCC as reference
                 ADC_PRESC_DIV4_gc;     // Prescaler DIV4

    // 10-bit resolution (default)
    ADC0.CTRLA = ADC_RESSEL_10BIT_gc;

    // Select internal voltage reference as input to measure
    ADC0.MUXPOS = ADC_MUXPOS_INTREF_gc;

    // Enable ADC
    ADC0.CTRLA |= ADC_ENABLE_bm;

    // Wait for ADC to stabilize
    _delay_us(100);

    // Dummy conversion for stability
    ADC0.COMMAND = ADC_STCONV_bm;
    while (!(ADC0.INTFLAGS & ADC_RESRDY_bm))
        ;
    ADC0.INTFLAGS = ADC_RESRDY_bm; // Clear flag

    // Actual conversion
    ADC0.COMMAND = ADC_STCONV_bm;
    while (!(ADC0.INTFLAGS & ADC_RESRDY_bm))
        ;

    uint16_t adcResult = ADC0.RES;

    // Disable ADC to save power
    ADC0.CTRLA |= !ADC_ENABLE_bm;

    // Check for valid reading
    if (adcResult == 0 || adcResult >= 1023)
    {
        return 0; // Invalid reading
    }

    // Calculate VCC: V_VCC = VREF × 1023 / ADC_result
    uint32_t voltage_mv = (INTERNAL_VREF_MV * 1023 / adcResult);

    return (uint16_t)voltage_mv;
}

/**
 * @brief Battery monitoring
 */
static void battery_level_indicator(void)
{
    uint16_t voltage = readBatteryVoltage();

    // 1S LiPo voltage ranges:
    // Good: >=3700mV
    // Low:  >=3500mV --> 

    // VPORTA.OUT &= ~(PA6_SET_MASK | PA7_SET_MASK); // Turn off both LEDs first
    VPORTA.OUT |= (PA6_SET_MASK | PA7_SET_MASK); // Red Debug ON

    if (voltage <= 65534)
    {
        // Green OFF, Red ON - Low battery
        //VPORTA.OUT &= ~PA7_SET_MASK; // Red Debug OFF
    }

    return;

    if (voltage >= 3700)
    {
        // Green ON, Red OFF - Good battery
        VPORTA.OUT &= ~PA6_SET_MASK; // Green ON (active low)
    }
    else if (voltage >= 3500)
    {
        // Both ON (yellow/orange) - Medium battery
        VPORTA.OUT &= ~(PA6_SET_MASK | PA7_SET_MASK);
    }
    else
    {
        // Green OFF, Red ON - Low battery
        VPORTA.OUT &= ~PA7_SET_MASK; // Red ON (active low)
    }
}

/**
 * @brief Handle momentary switch input on PA0
 *
 * A long press toggles bLedEnabled.
 * A short press swiches the mode.
 */
static bool handleSwitch(void)
{
    static uint16_t pressTicks = 0;  ///< Press duration counter
    static bool prevPressed = false; ///< Previous button state

    bool pressed = !(VPORTA.IN & BUTTON_PIN_MASK); // Active-low

    if (pressed)
    {
        if (pressTicks < 0xFFFF)
            pressTicks++; // Prevent overflow
    }
    else
    {
        // Button released
        if (prevPressed)
        {
            // Check if it was a short press (not a long press)
            if (pressTicks >= MS_TO_TICKS(BUTTON_SHORT_PRESS_DURATION_MS) &&
                pressTicks < MS_TO_TICKS(BUTTON_LONG_PRESS_DURATION_MS))
            {
                switchMode();
            }
        }

        pressTicks = 0;
    }

    if (pressed && pressTicks >= MS_TO_TICKS(BUTTON_LONG_PRESS_DURATION_MS))
    {
        bLedEnabled = !bLedEnabled; // Toggle LED blinking
    }

    prevPressed = pressed;
    return pressed;
}

/**
 * @brief Perform software reset
 */
static void software_reset(void)
{
    CCP = CCP_IOREG_gc; // unlock protected registers
    RSTCTRL.SWRR = 1;   // trigger software reset
    while (1)
        ; // wait for reset
}

/**
 * @brief Interrupt service routine
 */
ISR(PORTA_PORT_vect)
{
    // Clear interrupt flags for PA0
    PORTA.INTFLAGS = BUTTON_PIN_MASK;

    if (!(VPORTA.IN & BUTTON_PIN_MASK)) // check PA0 low
    {
        // Turn off all LEDs
        software_reset();
    }
}

/**
 * @brief LED blink state machine (bike rear light style)
 *
 * Normal cycle:
 *   0: all off, wait 250 ms
 *   2: LED 1–2 + 7–8 ON, wait 50 ms
 *   5: all off, wait 100 ms
 *   7: LED 1–2 + 7–8 ON, wait 50 ms
 *   10: all off, wait 250 ms
 *   12: LED 3–6 ON, wait 50 ms
 *   14: all off, wait 100 ms
 *   16: LED 3–6 ON, wait 50 ms
 *   18: all off, wait 250 ms → restart
 * Special: every 3rd cycle, all LEDs ON for 250 ms
 */
void ledAnimationBlink(bool resetCounters)
{
    const uint8_t T50 = MS_TO_TICKS(50);
    const uint8_t T100 = MS_TO_TICKS(100);
    const uint8_t T250 = MS_TO_TICKS(250);

    static uint16_t counter = 0;
    static uint8_t state = 2; // start with LEDs-on state
    static uint8_t cycle = 0;

    if (resetCounters)
    {
        counter = 0;
        state = 12; // start with LEDs on
        cycle = 0;
    }

    counter++;

    switch (state)
    {
    case 0: // all LEDs off, wait 250 ms
        leds_off();
        if (counter >= T250)
        {
            counter = 0;
            state = 2;
        }
        break;

    case 2: // LED 1–2 + 7–8 on, wait 50 ms
        VPORTA.OUT |= (PA1_SET_MASK | PA3_SET_MASK);
        if (counter >= T50)
        {
            counter = 0;
            state = 5;
        }
        break;

    case 5: // all LEDs off, wait 100 ms
        leds_off();
        if (counter >= T100)
        {
            counter = 0;
            state = 7;
        }
        break;

    case 7: // LED 1–2 + 7–8 on, wait 50 ms
        VPORTA.OUT |= (PA1_SET_MASK | PA3_SET_MASK);
        if (counter >= T50)
        {
            counter = 0;
            state = 10;
        }
        break;

    case 10: // all LEDs off, wait 250 ms
        leds_off();
        if (counter >= T250)
        {
            counter = 0;
            state = 12;
        }
        break;

    case 12: // LED 3–6 on, wait 50 ms
        setPWM_PA2(255U);
        if (counter >= T50)
        {
            counter = 0;
            state = 14;
        }
        break;

    case 14: // all LEDs off, wait 100 ms
        leds_off();
        if (counter >= T100)
        {
            counter = 0;
            state = 16;
        }
        break;

    case 16: // LED 3–6 on, wait 50 ms
        setPWM_PA2(255U);
        if (counter >= T50)
        {
            counter = 0;
            state = 18;
        }
        break;

    case 18: // all LEDs off, wait 250 ms
        leds_off();
        if (counter >= T250)
        {
            counter = 0;
            if (++cycle >= 3)
            {
                cycle = 0;
                state = 20; // special all-LEDs-on state
            }
            else
            {
                state = 0; // restart normal cycle
            }
        }
        break;

    case 20: // special: all LEDs on for 250 ms
        leds_on();
        if (counter >= T250)
        {
            counter = 0;
            state = 0; // restart normal sequence
        }
        break;
    }
}

/**
 * @brief All LEDs with static full power
 */
void ledStaticFull(void)
{
    leds_on();
}

/**
 * @brief Inner LEDs with glow animation
 */
void ledAnimationGlow(void)
{
    static uint8_t brightness = 0;
    static int8_t direction = 1;

    // Update brightness level every call (10ms)
    brightness += direction;

    // Reverse direction at limits
    if (brightness >= GLOW_BRIGHTNESS_MAX)
    {
        brightness = GLOW_BRIGHTNESS_MAX;
        direction = -1;
    }
    else if (brightness == GLOW_BRIGHTNESS_MIN)
    {
        brightness = GLOW_BRIGHTNESS_MIN;
        direction = 1;
    }

    // Apply PWM brightness to LED 3-6
    setPWM_PA2(brightness * 255 / 100); // Scale 0-100 to 0-255
}