use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported

use embedded_graphics::{
    mono_font::MonoTextStyleBuilder,
    pixelcolor::BinaryColor::On as Black,
    pixelcolor::BinaryColor::{self, Off as White},
    prelude::*,
    primitives::{Circle, Line, PrimitiveStyleBuilder},
    text::{Baseline, Text, TextStyleBuilder},
};
use epd_waveshare::{epd2in9_v2::*, graphics::DisplayRotation, prelude::*};
use esp_idf_hal::delay::Ets;
use esp_idf_hal::gpio::*;
use esp_idf_hal::spi::{config::Config, SpiDeviceDriver};
use esp_idf_hal::{peripherals::Peripherals, spi::SpiDriverConfig};

// This is a remake of the example from epd_waveshare (https://github.com/caemor/epd-waveshare/blob/main/examples/epd4in2.rs).
// The idea behind this is to make the example work with Wokwi simulated e-paper module (and to make the example clearer for newbies).
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_sys::link_patches();
    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();

    let peripherals = Peripherals::take().unwrap();
    let spi = peripherals.spi2;
    let sclk = peripherals.pins.gpio18;
    let serial_out = peripherals.pins.gpio23; // SDO
    let cs = PinDriver::output(peripherals.pins.gpio5)?;
    let busy_in = PinDriver::input(peripherals.pins.gpio4)?;
    let dc = PinDriver::output(peripherals.pins.gpio22)?;
    let rst = PinDriver::output(peripherals.pins.gpio21)?;

    let config = Config::new().baudrate(112500.into());
    let mut device = SpiDeviceDriver::new_single(
        spi,
        sclk,
        serial_out,
        Option::<Gpio2>::None,
        Option::<AnyIOPin>::None,
        &SpiDriverConfig::default(),
        &config,
    )?;

    // We cannot use the general purpose Delay struct from esp_idf_hal since it's only implemented for u16 and u32, whereas Epd2in9 uses u8
    // TODO PR a generic Delay struct (or simply add u8 etc...)
    let mut delay = Ets;

    // Setup EPD
    let mut epd = Epd2in9::new(&mut device, cs, busy_in, dc, rst, &mut delay)?;
    println!("Init done!");

    let mut display = Display2in9::default();

    println!("Clearing display");
    display.clear(BinaryColor::Off)?;

    println!("Drawing text");
    draw_text(&mut display, "Hello World!", 5, 50);
    epd.update_frame(&mut device, display.buffer(), &mut delay)?;
    epd.display_frame(&mut device, &mut delay)?;

    println!("Going to sleep");
    epd.sleep(&mut device, &mut delay)?;

    Ok(())
}

fn draw_text(display: &mut Display2in9, text: &str, x: i32, y: i32) {
    let style = MonoTextStyleBuilder::new()
        .font(&embedded_graphics::mono_font::ascii::FONT_6X10)
        .text_color(White)
        .background_color(Black)
        .build();

    let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();

    let _ = Text::with_text_style(text, Point::new(x, y), style, text_style).draw(display);
}
esp:VIN
esp:GND.2
esp:D13
esp:D12
esp:D14
esp:D27
esp:D26
esp:D25
esp:D33
esp:D32
esp:D35
esp:D34
esp:VN
esp:VP
esp:EN
esp:3V3
esp:GND.1
esp:D15
esp:D2
esp:D4
esp:RX2
esp:TX2
esp:D5
esp:D18
esp:D19
esp:D21
esp:RX0
esp:TX0
esp:D22
esp:D23
epd1:BUSY
epd1:RST
epd1:DC
epd1:CS
epd1:CLK
epd1:DIN
epd1:VCC
epd1:GND