/**
 * @file main.c
 * @brief Bike rear light implementation for ATTINY202 with low-power standby.
 */

#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 50U                 // Loop period 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 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);
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);

/* --- Timer init: Use RTC PIT for periodic wake-up --- */
void init_timer(void)
{
    RTC.CLKSEL = RTC_CLKSEL_INT1K_gc; // 1 kHz ULP clock for RTC
    while (RTC.STATUS > 0)
    {
    } // Wait for sync

    RTC.PITINTCTRL = RTC_PI_bm;        // Enable PIT interrupt
    RTC.PITCTRLA = RTC_PERIOD_CYC64_gc // ≈64 ms wake-up (~50 ms)
                   | RTC_PITEN_bm;     // Enable PIT
}

ISR(RTC_PIT_vect)
{
    RTC.PITINTFLAGS = RTC_PI_bm; // Clear interrupt flag
}

/* --- MAIN --- */
int main(void)
{
    // 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
    battery_level_indicator(); // TODO: Implement

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

    cli();
    init_timer();
    sei();

    set_sleep_mode(SLEEP_MODE_STANDBY);

    while (1)
    {
        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

            cli();       // Disable global interrupts
            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_enable();
        sleep_cpu(); // Sleep until PIT wakes
        sleep_disable();
    }
}

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

/**
 * @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 Battery monitoring
 */
static void battery_level_indicator(void)
{
    // TODO: Implement
    VPORTA.OUT |= (PA6_SET_MASK | PA7_SET_MASK); // green + red OFF
}

/**
 * @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;
        }
        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 >= 100U)
    {
        brightness = 100U;
        direction = -1;
    }
    else if (brightness == 10U)
    {
        brightness = 10U;
        direction = 1;
    }

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