/**
 * @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_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

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

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

// Forward declarations
void blinkLed(bool resetCounters);
static inline void leds_off(void);
static inline void leds_on(void);
static void battery_level_indicator(void);
static void handleSwitch(void);
static void software_reset(void);
ISR(PORTA_PORT_vect);

/**
 * @brief Main entry point
 */
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);

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

    // Ensure all LEDs off at startup
    leds_off();
    battery_level_indicator(); // TODO: Implement

    bool bLedEnabledOld = bLedEnabled;

    while (true)
    {
        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)
            {
                blinkLed(false); // run normal blink
            }
        }

        _delay_ms(MAIN_LOOP_SLEEP);
    }
}

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

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

/**
 * @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.
 */
static void 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)
    {
        bBtnPressed = true;
        if (pressTicks < 0xFFFF)
            pressTicks++; // Prevent overflow
    }
    else
    {
        bBtnPressed = false;
        pressTicks = 0;
    }

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

    prevPressed = 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 blinkLed(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
        VPORTA.OUT |= PA2_SET_MASK;
        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
        VPORTA.OUT |= PA2_SET_MASK;
        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
        VPORTA.OUT |= (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK);
        if (counter >= T250)
        {
            counter = 0;
            state = 0; // restart normal sequence
        }
        break;
    }
}
