use embedded_hal::digital::{OutputPin, PinState};

use crate::leds::{Leds, Toggle};

enum States {
    Red(StateRed),
    Blue(StateBlue),
    Green(StateGreen),
}

trait State<
    RP: OutputPin, RT: Toggle<RP>,
    GP: OutputPin, GT: Toggle<GP>,
    BP: OutputPin, BT: Toggle<BP>
> {
    fn new(leds: &mut Leds<RP, RT, GP, GT, BP, BT>) -> Self;
    fn second_passed(&self, leds: &mut Leds<RP, RT, GP, GT, BP, BT>) -> States;
}

pub struct StateMachine<'a,
    RP: OutputPin, RT: Toggle<RP>,
    GP: OutputPin, GT: Toggle<GP>,
    BP: OutputPin, BT: Toggle<BP>
> {
    state: States,
    leds: &'a mut Leds<'a, RP, RT, GP, GT, BP, BT>,
}
impl<'a,
    RP: OutputPin, RT: Toggle<RP>,
    GP: OutputPin, GT: Toggle<GP>,
    BP: OutputPin, BT: Toggle<BP>
> StateMachine<'a, RP, RT, GP, GT, BP, BT> 
{
    pub fn new(leds: &'a mut Leds<'a, RP, RT, GP, GT, BP, BT>) -> Self {
        Self {
            state: States::Red(StateRed::new(leds)),
            leds,
        }
    }
    pub fn second_passed(&mut self) {
        self.state = match &self.state {
            States::Red(state) => state.second_passed(self.leds),
            States::Blue(state) => state.second_passed(self.leds),
            States::Green(state) => state.second_passed(self.leds),
        }
    }
}

// ####################################################
// ## RED #############################################
// ####################################################

struct StateRed {
    time_passed: u32
}
impl<
    RP: OutputPin, RT: Toggle<RP>,
    GP: OutputPin, GT: Toggle<GP>,
    BP: OutputPin, BT: Toggle<BP>
> State<RP, RT, GP, GT, BP, BT> for StateRed {
    fn new(leds: &mut Leds<RP, RT, GP, GT, BP, BT>) -> Self {
        leds.set_all(PinState::High, PinState::Low, PinState::Low);
        Self {
            time_passed: 0
        }
    }
    fn second_passed(&self, leds: &mut Leds<RP, RT, GP, GT, BP, BT>) -> States {
        if self.time_passed + 1 >= 4 {
            States::Green(StateGreen::new(leds))
        } else {
            States::Red(Self {
                time_passed: self.time_passed + 1
            })
        }
    }
}

// ####################################################
// ## GREEN ###########################################
// ####################################################

struct StateGreen {
    time_passed: u32
}
impl<
    RP: OutputPin, RT: Toggle<RP>,
    GP: OutputPin, GT: Toggle<GP>,
    BP: OutputPin, BT: Toggle<BP>
> State<RP, RT, GP, GT, BP, BT> for StateGreen {
    fn new(leds: &mut Leds<RP, RT, GP, GT, BP, BT>) -> Self {
        leds.set_all(PinState::Low, PinState::High, PinState::Low);
        Self {
            time_passed: 0
        }
    }
    fn second_passed(&self, leds: &mut Leds<RP, RT, GP, GT, BP, BT>) -> States {
        if self.time_passed + 1 >= 3 {
            States::Blue(StateBlue::new(leds))
        } else {
            States::Green(Self {
                time_passed: self.time_passed + 1
            })
        }
    }
}

// ####################################################
// ## BLUE ############################################
// ####################################################

struct StateBlue {}
impl<
    RP: OutputPin, RT: Toggle<RP>,
    GP: OutputPin, GT: Toggle<GP>,
    BP: OutputPin, BT: Toggle<BP>
> State<RP, RT, GP, GT, BP, BT> for StateBlue {
    fn new(leds: &mut Leds<RP, RT, GP, GT, BP, BT>) -> Self {
        leds.set_all(PinState::Low, PinState::Low, PinState::High);
        Self {}
    }
    fn second_passed(&self, leds: &mut Leds<RP, RT, GP, GT, BP, BT>) -> States {
        States::Red(StateRed::new(leds))
    }
}
