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

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

enum States {
    Red(StateRed),
    Orange(StateOrange),
    Green(StateGreen),
    BridgeOpen(StateBridgeOpen)
}

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

pub struct StateMachine<'a,
    RP: OutputPin, RT: Toggle<RP>,
    GP: OutputPin, GT: Toggle<GP>,
    OP: OutputPin, OT: Toggle<OP>,
    BP: OutputPin, BT: Toggle<BP>
> {
    state: States,
    leds: &'a mut Leds<'a, RP, RT, GP, GT, OP, OT, BP, BT>
}
impl<'a,
    RP: OutputPin, RT: Toggle<RP>,
    GP: OutputPin, GT: Toggle<GP>,
    OP: OutputPin, OT: Toggle<OP>,
    BP: OutputPin, BT: Toggle<BP>
> StateMachine<'a, RP, RT, GP, GT, OP, OT, BP, BT> 
{
    pub fn new(leds: &'a mut Leds<'a, RP, RT, GP, GT, OP, OT, 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::Orange(state) => state.second_passed(self.leds),
            States::Green(state) => state.second_passed(self.leds),
            States::BridgeOpen(state) => state.second_passed(self.leds),
        }
    }
    pub fn button_press(&mut self) {
        self.state = match &self.state {
            States::Red(state) => state.button_press(self.leds),
            States::Orange(state) => state.button_press(self.leds),
            States::Green(state) => state.button_press(self.leds),
            States::BridgeOpen(state) => state.button_press(self.leds),
        }
    }
    pub fn button_release(&mut self) {
        self.state = match &self.state {
            States::Red(state) => state.button_release(self.leds),
            States::Orange(state) => state.button_release(self.leds),
            States::Green(state) => state.button_release(self.leds),
            States::BridgeOpen(state) => state.button_release(self.leds),
        }
    }
}

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

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

    fn button_press(&self, leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> States {
        States::BridgeOpen(StateBridgeOpen::new(leds))
    }
    fn button_release(&self, _leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> States {
        States::Red(Self {
            time_passed: self.time_passed
        })
    }
}

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

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

    fn button_press(&self, _leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> States {
        States::Green(Self {
            time_passed: self.time_passed
        })
    }
    fn button_release(&self, _leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> States {
        States::Green(Self {
            time_passed: self.time_passed
        })
    }
}

// ####################################################
// ## ORANGE ############################################
// ####################################################

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

    fn button_press(&self, _leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> States {
        States::Orange(Self {})
    }
    fn button_release(&self, _leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> States {
        States::Orange(Self {})
    }
}

// ####################################################
// ## BRIDGE_OPEN #####################################
// ####################################################

struct StateBridgeOpen {
    time_passed: u32
}
impl<
    RP: OutputPin, RT: Toggle<RP>,
    GP: OutputPin, GT: Toggle<GP>,
    OP: OutputPin, OT: Toggle<OP>,
    BP: OutputPin, BT: Toggle<BP>
> State<RP, RT, GP, GT, OP, OT, BP, BT> for StateBridgeOpen {
    fn new(leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> Self {
        leds.set_all(PinState::Low, PinState::Low, PinState::Low, PinState::High);
        Self {
            time_passed: 0
        }
    }
    fn second_passed(&self, leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> States {
        if self.time_passed + 1 == 2 {
            let _ = leds.blue.low();
            States::BridgeOpen(Self {
                time_passed: self.time_passed + 1
            })
        } else if self.time_passed + 1 >= 3 {
            let _ = leds.blue.high();
            States::BridgeOpen(Self {
                time_passed: 0
            })
        } else {
            States::BridgeOpen(Self {
                time_passed: self.time_passed + 1
            })
        }
    }

    fn button_press(&self, _leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> States {
        States::BridgeOpen(Self {
            time_passed: self.time_passed
        })
    }
    fn button_release(&self, leds: &mut Leds<RP, RT, GP, GT, OP, OT, BP, BT>) -> States {
        States::Red(StateRed::new(leds))
    }
}
