use embedded_hal::digital::v2::{InputPin, OutputPin};
use rppal::gpio::{IoPin, Mode, PullUpDown};
use void;

/**
 * Raspberry pi does not have open drain pins so we have to emulate it.
 */
pub struct OpenPin {
    iopin: IoPin,
    mode: Mode,
}

impl OpenPin {
    pub fn new(mut pin: IoPin) -> OpenPin {
        pin.set_mode(Mode::Output);
        pin.set_high();
        pin.set_pullupdown(PullUpDown::Off);
        OpenPin {
            iopin: pin,
            mode: Mode::Output,
        }
    }

    pub fn switch_input(&mut self) {
        if self.mode != Mode::Input {
            self.mode = Mode::Input;
            self.iopin.set_mode(Mode::Input);
        }
    }

    pub fn switch_output(&mut self) {
        if self.mode != Mode::Output {
            self.mode = Mode::Output;
            self.iopin.set_mode(Mode::Output);
        }
    }
}

// Current rppal implementation does not support embedded_hal::gpio::v2 pins API.
impl InputPin for OpenPin {
    type Error = void::Void;

    fn is_high(&self) -> Result<bool, Self::Error> {
        Ok(self.iopin.is_high())
    }

    /// Is the input pin low?
    fn is_low(&self) -> Result<bool, Self::Error> {
        Ok(self.iopin.is_low())
    }
}

// Current rppal implementation does not support embedded_hal::gpio::v2 pins API.
impl OutputPin for OpenPin {
    type Error = void::Void;

    fn set_low(&mut self) -> Result<(), Self::Error> {
        self.switch_output();
        self.iopin.set_low();
        Ok(())
    }

    fn set_high(&mut self) -> Result<(), Self::Error> {
        self.iopin.set_high();
        self.switch_input();
        Ok(())
    }
}
