mod command;

/// Width of E2266CS0Cx in pixels
pub const WIDTH: u32 = 152;
/// Height of E2266CS0Cx in pixels
pub const HEIGHT: u32 = 296;
/// Default Background Color (white)
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
const IS_BUSY_LOW: bool = true;

use crate::buffer_len;
use embedded_hal::{
    blocking::{delay::*, spi::Write},
    digital::v2::*,
};
use std::io::repeat;

use command::Command;

use crate::color::Color;

use crate::traits::*;

use crate::interface::DisplayInterface;

mod graphics;

/// E2266CS0Cx driver
///
pub struct E2266CS0Cx<SPI, CS, BUSY, DC, RST, DELAY> {
    /// SPI
    interface: DisplayInterface<SPI, CS, BUSY, DC, RST, DELAY>,
    /// Color
    background_color: Color,
}

impl<SPI, CS, BUSY, DC, RST, DELAY> E2266CS0Cx<SPI, CS, BUSY, DC, RST, DELAY>
where
    SPI: Write<u8>,
    CS: OutputPin,
    BUSY: InputPin,
    DC: OutputPin,
    RST: OutputPin,
    DELAY: DelayMs<u8>,
{
    fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
        self.interface.reset(delay, 10);

        // soft-reset
        self.interface
            .cmd_with_data(spi, Command::PanelSetting, &[0x0E])?;

        delay.delay_ms(5);

        let temperature = 25 as i8; // We assume this for now, we should probably measure this.

        // Set temperature
        self.interface
            .cmd_with_data(spi, Command::InputTemperature, &temperature.to_ne_bytes())?;

        // Activate temperature
        self.interface
            .cmd_with_data(spi, Command::ActiveTemperature, &[0x02])?;

        // Set PanelSettings
        self.interface
            .cmd_with_data(spi, Command::PanelSetting, &[0xCF, 0x8D])?;
        Ok(())
    }
}

impl<SPI, CS, BUSY, DC, RST, DELAY> PervasiveDisplay<SPI, CS, BUSY, DC, RST, DELAY>
    for E2266CS0Cx<SPI, CS, BUSY, DC, RST, DELAY>
where
    SPI: Write<u8>,
    CS: OutputPin,
    BUSY: InputPin,
    DC: OutputPin,
    RST: OutputPin,
    DELAY: DelayMs<u8>,
{
    type DisplayColor = Color;
    fn width(&self) -> u32 {
        WIDTH
    }

    fn height(&self) -> u32 {
        HEIGHT
    }

    fn new(
        spi: &mut SPI,
        cs: CS,
        busy: BUSY,
        dc: DC,
        rst: RST,
        delay: &mut DELAY,
    ) -> Result<Self, SPI::Error> {
        let interface = DisplayInterface::new(cs, busy, dc, rst);

        let mut epd = E2266CS0Cx {
            interface,
            background_color: DEFAULT_BACKGROUND_COLOR,
        };

        epd.init(spi, delay)?;

        Ok(epd)
    }

    fn power_off(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> {
        self.wait_until_idle();
        self.interface.cmd(spi, Command::PowerOff)?;
        Ok(())
    }

    fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
        self.wait_until_idle();
        self.init(spi, delay)?;
        Ok(())
    }

    fn update_frame(
        &mut self,
        spi: &mut SPI,
        buffer: &[u8],
        _delay: &mut DELAY,
    ) -> Result<(), SPI::Error> {
        self.wait_until_idle();

        // Transmit display data
        self.interface
            .cmd_with_data(spi, Command::DataTransmission1, buffer)?;

        // Transmit WIDTH * HEIGHT bytes of 0x00
        self.interface.cmd(spi, Command::DataTransmission2)?;
        self.interface.data_x_times(
            spi,
            0x00,
            buffer_len(WIDTH as usize, HEIGHT as usize) as u32,
        )?;
        Ok(())
    }

    fn display_frame(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> {
        self.wait_until_idle();
        // Power on DC/DC converter
        self.interface.cmd(spi, Command::PowerOn)?;

        self.wait_until_idle();

        // Refresh the Display
        self.interface.cmd(spi, Command::DisplayRefresh)?;

        self.wait_until_idle();
        Ok(())
    }

    fn update_and_display_frame(
        &mut self,
        spi: &mut SPI,
        buffer: &[u8],
        delay: &mut DELAY,
    ) -> Result<(), SPI::Error> {
        self.update_frame(spi, buffer, delay)?;
        self.display_frame(spi, delay)?;
        Ok(())
    }

    fn clear_frame(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> {
        self.wait_until_idle();

        // clear the ram with the background color
        let color = self.background_color.get_byte_value();

        self.update_frame(
            spi,
            &[0x00; buffer_len(WIDTH as usize, HEIGHT as usize)],
            _delay,
        )?;
        Ok(())
    }

    fn set_background_color(&mut self, background_color: Color) {
        self.background_color = background_color;
    }

    fn background_color(&self) -> &Color {
        &self.background_color
    }

    fn is_busy(&self) -> bool {
        self.interface.is_busy(IS_BUSY_LOW)
    }
}

impl<SPI, CS, BUSY, DC, RST, DELAY> E2266CS0Cx<SPI, CS, BUSY, DC, RST, DELAY>
where
    SPI: Write<u8>,
    CS: OutputPin,
    BUSY: InputPin,
    DC: OutputPin,
    RST: OutputPin,
    DELAY: DelayMs<u8>,
{
    fn wait_until_idle(&mut self) {
        let _ = self.interface.wait_until_idle(IS_BUSY_LOW);
    }
}
