//! Panel emulator: a host mipidsi `Interface` that decodes the REAL command/pixel stream
//! (CASET/RASET/RAMWR + send_pixels/send_repeated_pixel) into a framebuffer, then renders
//! pm-ui *through mipidsi* and saves a PNG. Unlike the plain uisim (which draws to its own
//! buffer and never exercises mipidsi's pixel path), this reproduces addressing/stride/count
//! bugs — the kind causing the 1/4 + stripes on hardware. `cargo run --bin panelsim`.

use core::convert::Infallible;
use embedded_graphics::pixelcolor::raw::RawU16;
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::prelude::*;
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::{ErrorType, OutputPin};
use mipidsi::interface::Interface;
use mipidsi::models::ST7796;
use mipidsi::options::{ColorInversion, ColorOrder, Orientation};
use mipidsi::Builder;

const W: usize = 320;
const H: usize = 480;

struct Panel {
    fb: Vec<Rgb565>,
    xs: usize,
    xe: usize,
    ys: usize,
    ye: usize,
    cx: usize,
    cy: usize,
    pixels_written: u64,
}
impl Panel {
    fn new() -> Self {
        Panel { fb: vec![Rgb565::BLACK; W * H], xs: 0, xe: W - 1, ys: 0, ye: H - 1, cx: 0, cy: 0, pixels_written: 0 }
    }
    fn put(&mut self, c: Rgb565) {
        if self.cx < W && self.cy < H {
            self.fb[self.cy * W + self.cx] = c;
        }
        self.pixels_written += 1;
        self.cx += 1;
        if self.cx > self.xe {
            self.cx = self.xs;
            self.cy += 1;
        }
    }
}
fn be(a: &[u8]) -> usize {
    ((a[0] as usize) << 8) | a[1] as usize
}
impl Interface for Panel {
    type Word = u8;
    type Error = Infallible;
    fn send_command(&mut self, cmd: u8, args: &[u8]) -> Result<(), Infallible> {
        match cmd {
            0x2A if args.len() >= 4 => {
                self.xs = be(&args[0..2]);
                self.xe = be(&args[2..4]);
            }
            0x2B if args.len() >= 4 => {
                self.ys = be(&args[0..2]);
                self.ye = be(&args[2..4]);
            }
            0x2C => {
                self.cx = self.xs;
                self.cy = self.ys;
            }
            _ => {}
        }
        Ok(())
    }
    fn send_pixels<const N: usize>(&mut self, pixels: impl IntoIterator<Item = [u8; N]>) -> Result<(), Infallible> {
        for px in pixels {
            let v = ((px[0] as u16) << 8) | px[1] as u16;
            self.put(Rgb565::from(RawU16::new(v)));
        }
        Ok(())
    }
    fn send_repeated_pixel<const N: usize>(&mut self, pixel: [u8; N], count: u32) -> Result<(), Infallible> {
        let v = ((pixel[0] as u16) << 8) | pixel[1] as u16;
        let c = Rgb565::from(RawU16::new(v));
        for _ in 0..count {
            self.put(c);
        }
        Ok(())
    }
}

struct NoPin;
impl ErrorType for NoPin {
    type Error = Infallible;
}
impl OutputPin for NoPin {
    fn set_low(&mut self) -> Result<(), Infallible> {
        Ok(())
    }
    fn set_high(&mut self) -> Result<(), Infallible> {
        Ok(())
    }
}
struct NoDelay;
impl DelayNs for NoDelay {
    fn delay_ns(&mut self, _: u32) {}
}

fn main() {
    let mut delay = NoDelay;
    let mut display = Builder::new(ST7796, Panel::new())
        .reset_pin(NoPin)
        .display_size(W as u16, H as u16)
        .color_order(ColorOrder::Bgr)
        .invert_colors(ColorInversion::Inverted)
        .orientation(Orientation::new().flip_horizontal())
        .init(&mut delay)
        .unwrap();

    pm_ui::draw_ui(&mut display).unwrap();

    let (panel, _m, _r) = display.release();
    println!("pixels written by mipidsi: {} (full screen = {})", panel.pixels_written, W * H);

    let img = image::RgbImage::from_fn(W as u32, H as u32, |x, y| {
        let c = panel.fb[(y as usize) * W + x as usize];
        let r = (c.r() << 3) | (c.r() >> 2);
        let g = (c.g() << 2) | (c.g() >> 4);
        let b = (c.b() << 3) | (c.b() >> 2);
        image::Rgb([r, g, b])
    });
    img.save("panelsim.png").unwrap();
    println!("wrote panelsim.png");
}
