use display_interface_spi::SPIInterfaceNoCS;
use embedded_graphics::{
    pixelcolor::Rgb565,
    prelude::*,
    primitives::{PrimitiveStyle, Rectangle},
};
use esp_idf_hal::{
    delay::FreeRtos,
    gpio::{AnyIOPin, Gpio16, Gpio17, Gpio18, Gpio19, Gpio23, Gpio4, PinDriver},
    spi::{
        config::{Config, DriverConfig},
        Dma, SpiDeviceDriver, SpiDriver, SPI2,
    },
};
use mipidsi::Builder;
use std::{error::Error, fmt::Debug};

fn main() -> anyhow::Result<()> {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_svc::sys::link_patches();
    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();
    log::info!("System reached main()");

    esp_idf_hal::sys::link_patches();

    let width = 320;
    let height = 240;

    //    let peri = Peripherals::take()?;

    let rst = PinDriver::input_output_od(unsafe { Gpio23::new() })?;
    let dc = PinDriver::input_output_od(unsafe { Gpio16::new() })?;

    let sclk = unsafe { Gpio18::new() };
    let spi = unsafe { SPI2::new() };
    let sdo = unsafe { Gpio19::new() };

    let dma_setup = DriverConfig {
        dma: Dma::Channel1(width * height * 2 + 8),
        ..Default::default()
    };

    let spi = SpiDriver::new(spi, sclk, sdo, None::<AnyIOPin>, &dma_setup)?;

    let mut bl = PinDriver::input_output_od(unsafe { Gpio4::new() })?;
    bl.set_high()?;

    let cs = unsafe { Gpio17::new() };

    let spi = SpiDeviceDriver::new(spi, Some(cs), &Config::new())?;

    let di = SPIInterfaceNoCS::new(spi, dc);

    let mut delay = esp_idf_hal::delay::Ets;
    let mut display;
    let mut frame = 0;
    display = Builder::st7789(di)
        .init(&mut delay, Some(rst))
        .map_err(|_| Box::<dyn Error>::from("display init"))
        .unwrap();

    loop {
        frame = frame + 1;
        let top_left = Point::new(frame & 0x7F, frame & 0x7F);
        let size = Size::new(50, 50);

        let square =
            Rectangle::new(top_left, size).into_styled(PrimitiveStyle::with_fill(Rgb565::BLUE));

        square.draw(&mut display).unwrap();
        log::info!("frame: {}", frame);
        FreeRtos::delay_ms(100);
    }
}