#![cfg_attr(not(test), no_std)]
#![warn(missing_docs)]
#[macro_use]
extern crate alloc;
use core::marker::PhantomData;
use alloc::string::String;
mod command;
pub mod interface;
pub mod memory_converter_settings;
mod pixel_serializer;
mod register;
use memory_converter_settings::MemoryConverterSetting;
use pixel_serializer::{convert_color_to_pixel_iterator, PixelSerializer};
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    Interface(interface::Error),
}
impl From<interface::Error> for Error {
    fn from(e: interface::Error) -> Self {
        Error::Interface(e)
    }
}
#[derive(Debug, Clone)]
pub struct DevInfo {
    pub panel_width: u16,
    pub panel_height: u16,
    pub memory_address: u32,
    pub firmware_version: String,
    pub lut_version: String,
}
#[derive(Debug, PartialEq, Eq)]
pub struct AreaImgInfo {
    pub area_x: u16,
    pub area_y: u16,
    pub area_w: u16,
    pub area_h: u16,
}
pub enum WaveformMode {
    Init = 0,
    DirectUpdate = 1,
    GrayscaleClearing16 = 2,
    GL16 = 3,
    GLR16 = 4,
    GLD16 = 5,
    A2 = 6,
    DU4 = 7,
}
pub struct Run;
pub struct PowerDown;
pub struct Off;
pub struct IT8951<IT8951Interface, State> {
    interface: IT8951Interface,
    dev_info: Option<DevInfo>,
    marker: core::marker::PhantomData<State>,
}
impl<IT8951Interface: interface::IT8951Interface> IT8951<IT8951Interface, Off> {
    pub fn new(interface: IT8951Interface) -> Self {
        IT8951 {
            interface,
            dev_info: None,
            marker: PhantomData {},
        }
    }
    pub fn init(mut self, vcom: u16) -> Result<IT8951<IT8951Interface, Run>, Error> {
        self.interface.reset()?;
        let mut it8951 = IT8951::<IT8951Interface, PowerDown> {
            interface: self.interface,
            dev_info: self.dev_info,
            marker: PhantomData {},
        }
        .sys_run()?;
        let dev_info = it8951.get_system_info()?;
        it8951.write_register(register::I80CPCR, 0x0001)?;
        if vcom != it8951.get_vcom()? {
            it8951.set_vcom(vcom)?;
        }
        it8951.dev_info = Some(dev_info);
        it8951.reset()?;
        Ok(it8951)
    }
    pub fn attach(interface: IT8951Interface) -> Result<IT8951<IT8951Interface, Run>, Error> {
        let mut it8951 = IT8951 {
            interface,
            dev_info: None,
            marker: PhantomData {},
        }.sys_run()?;
        
        it8951.dev_info = Some(it8951.get_system_info()?);
        Ok(it8951)
    }
}
impl<IT8951Interface: interface::IT8951Interface> IT8951<IT8951Interface, Run> {
    pub fn get_dev_info(&self) -> DevInfo {
        self.dev_info.clone().unwrap()
    }
    pub fn enhance_driving_capability(&mut self) -> Result<(), Error> {
        self.write_register(0x0038, 0x0602)?;
        Ok(())
    }
    pub fn reset(&mut self) -> Result<(), Error> {
        self.clear_frame_buffer(0xF)?;
        self.display(WaveformMode::Init)?;
        Ok(())
    }
    fn clear_frame_buffer(&mut self, raw_color: u16) -> Result<(), Error> {
        let dev_info = self.get_dev_info();
        let width = dev_info.panel_width;
        let height = dev_info.panel_height;
        let mem_addr = dev_info.memory_address;
        let data_entry = raw_color << 12 | raw_color << 8 | raw_color << 4 | raw_color;
        for w in 0..height {
            self.load_image_area(
                mem_addr,
                MemoryConverterSetting {
                    endianness: memory_converter_settings::MemoryConverterEndianness::LittleEndian,
                    bit_per_pixel:
                        memory_converter_settings::MemoryConverterBitPerPixel::BitsPerPixel4,
                    rotation: memory_converter_settings::MemoryConverterRotation::Rotate0,
                },
                &AreaImgInfo {
                    area_x: 0,
                    area_y: w,
                    area_w: width,
                    area_h: 1,
                },
                &vec![data_entry; width as usize / 4],
            )?;
        }
        Ok(())
    }
    pub fn load_image(
        &mut self,
        target_mem_addr: u32,
        image_settings: MemoryConverterSetting,
        data: &[u16],
    ) -> Result<(), Error> {
        self.set_target_memory_addr(target_mem_addr)?;
        self.interface.write_command(command::IT8951_TCON_LD_IMG)?;
        self.interface.write_data(image_settings.into_arg())?;
        self.interface.write_multi_data(data)?;
        self.interface
            .write_command(command::IT8951_TCON_LD_IMG_END)?;
        Ok(())
    }
    pub fn load_image_area(
        &mut self,
        target_mem_addr: u32,
        image_settings: MemoryConverterSetting,
        area_info: &AreaImgInfo,
        data: &[u16],
    ) -> Result<(), Error> {
        self.set_target_memory_addr(target_mem_addr)?;
        self.interface.write_command_with_args(
            command::IT8951_TCON_LD_IMG_AREA,
            &[
                image_settings.into_arg(),
                area_info.area_x,
                area_info.area_y,
                area_info.area_w,
                area_info.area_h,
            ],
        )?;
        self.interface.write_multi_data(data)?;
        self.interface
            .write_command(command::IT8951_TCON_LD_IMG_END)?;
        Ok(())
    }
    fn set_target_memory_addr(&mut self, target_mem_addr: u32) -> Result<(), Error> {
        self.write_register(register::LISAR + 2, (target_mem_addr >> 16) as u16)?;
        self.write_register(register::LISAR, target_mem_addr as u16)?;
        Ok(())
    }
    pub fn memory_burst_read(
        &mut self,
        memory_address: u32,
        data: &mut [u16],
    ) -> Result<(), Error> {
        let args = [
            memory_address as u16,
            (memory_address >> 16) as u16,
            data.len() as u16,
            (data.len() >> 16) as u16,
        ];
        self.interface
            .write_command_with_args(command::IT8951_TCON_MEM_BST_RD_T, &args)?;
        self.interface
            .write_command(command::IT8951_TCON_MEM_BST_RD_S)?;
        self.interface.read_multi_data(data)?;
        self.interface
            .write_command(command::IT8951_TCON_MEM_BST_END)?;
        Ok(())
    }
    pub fn memory_burst_write(&mut self, memory_address: u32, data: &[u16]) -> Result<(), Error> {
        let args = [
            memory_address as u16,
            (memory_address >> 16) as u16,
            data.len() as u16,
            (data.len() >> 16) as u16,
        ];
        self.interface
            .write_command_with_args(command::IT8951_TCON_MEM_BST_WR, &args)?;
        self.interface.write_multi_data(data)?;
        self.interface
            .write_command(command::IT8951_TCON_MEM_BST_END)?;
        Ok(())
    }
    pub fn display_area(
        &mut self,
        area_info: &AreaImgInfo,
        mode: WaveformMode,
    ) -> Result<(), Error> {
        self.wait_for_display_ready()?;
        let args = [
            area_info.area_x,
            area_info.area_y,
            area_info.area_w,
            area_info.area_h,
            mode as u16,
        ];
        self.interface
            .write_command_with_args(command::USDEF_I80_CMD_DPY_AREA, &args)?;
        Ok(())
    }
    pub fn display_area_buf(
        &mut self,
        area_info: &AreaImgInfo,
        mode: WaveformMode,
        target_mem_addr: u32,
    ) -> Result<(), Error> {
        self.wait_for_display_ready()?;
        let args = [
            area_info.area_x,
            area_info.area_y,
            area_info.area_w,
            area_info.area_h,
            mode as u16,
            target_mem_addr as u16,
            (target_mem_addr >> 16) as u16,
        ];
        self.interface
            .write_command_with_args(command::USDEF_I80_CMD_DPY_BUF_AREA, &args)?;
        Ok(())
    }
    pub fn display(&mut self, mode: WaveformMode) -> Result<(), Error> {
        let dev_info = self.get_dev_info();
        let width = dev_info.panel_width;
        let height = dev_info.panel_height;
        self.display_area(
            &AreaImgInfo {
                area_x: 0,
                area_y: 0,
                area_w: width,
                area_h: height,
            },
            mode,
        )?;
        Ok(())
    }
    fn wait_for_display_ready(&mut self) -> Result<(), Error> {
        while Ok(0) != self.read_register(register::LUTAFSR) {}
        Ok(())
    }
    pub fn sleep(mut self) -> Result<IT8951<IT8951Interface, PowerDown>, Error> {
        self.interface.write_command(command::IT8951_TCON_SLEEP)?;
        Ok(IT8951 {
            interface: self.interface,
            dev_info: self.dev_info,
            marker: PhantomData {},
        })
    }
    pub fn standby(mut self) -> Result<IT8951<IT8951Interface, PowerDown>, Error> {
        self.interface.write_command(command::IT8951_TCON_STANDBY)?;
        Ok(IT8951 {
            interface: self.interface,
            dev_info: self.dev_info,
            marker: PhantomData {},
        })
    }
    fn get_system_info(&mut self) -> Result<DevInfo, Error> {
        self.interface
            .write_command(command::USDEF_I80_CMD_GET_DEV_INFO)?;
        self.interface.wait_while_busy()?;
        let mut buf = [0x0000; 20];
        self.interface.read_multi_data(&mut buf)?;
        Ok(DevInfo {
            panel_width: buf[0],
            panel_height: buf[1],
            memory_address: ((buf[3] as u32) << 16) | (buf[2] as u32),
            firmware_version: self.buf_to_string(&buf[4..12]),
            lut_version: self.buf_to_string(&buf[12..20]),
        })
    }
    fn buf_to_string(&self, buf: &[u16]) -> String {
        buf.iter()
            .filter(|&&raw| raw != 0x0000)
            .fold(String::new(), |mut res, &raw| {
                if let Some(c) = char::from_u32((raw & 0xFF) as u32) {
                    res.push(c);
                }
                if let Some(c) = char::from_u32((raw >> 8) as u32) {
                    res.push(c);
                }
                res
            })
    }
    fn get_vcom(&mut self) -> Result<u16, Error> {
        self.interface.write_command(command::USDEF_I80_CMD_VCOM)?;
        self.interface.write_data(0x0000)?;
        let vcom = self.interface.read_data()?;
        Ok(vcom)
    }
    fn set_vcom(&mut self, vcom: u16) -> Result<(), Error> {
        self.interface.write_command(command::USDEF_I80_CMD_VCOM)?;
        self.interface.write_data(0x0001)?;
        self.interface.write_data(vcom)?;
        Ok(())
    }
    fn read_register(&mut self, reg: u16) -> Result<u16, Error> {
        self.interface.write_command(command::IT8951_TCON_REG_RD)?;
        self.interface.write_data(reg)?;
        let data = self.interface.read_data()?;
        Ok(data)
    }
    fn write_register(&mut self, reg: u16, data: u16) -> Result<(), Error> {
        self.interface.write_command(command::IT8951_TCON_REG_WR)?;
        self.interface.write_data(reg)?;
        self.interface.write_data(data)?;
        Ok(())
    }
}
impl<IT8951Interface: interface::IT8951Interface> IT8951<IT8951Interface, PowerDown> {
    pub fn sys_run(mut self) -> Result<IT8951<IT8951Interface, Run>, Error> {
        self.interface.write_command(command::IT8951_TCON_SYS_RUN)?;
        Ok(IT8951 {
            interface: self.interface,
            dev_info: self.dev_info,
            marker: PhantomData {},
        })
    }
}
use embedded_graphics::{pixelcolor::Gray4, prelude::*, primitives::Rectangle};
impl<IT8951Interface: interface::IT8951Interface> DrawTarget for IT8951<IT8951Interface, Run> {
    type Color = Gray4;
    type Error = Error;
    fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
        let raw_color = color.luma() as u16;
        self.clear_frame_buffer(raw_color)
    }
    fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
    where
        I: IntoIterator<Item = Self::Color>,
    {
        let iter = convert_color_to_pixel_iterator(*area, self.bounding_box(), colors.into_iter());
        let pixel = PixelSerializer::new(area.intersection(&self.bounding_box()), iter);
        for (area_img_info, buffer) in pixel {
            let dev_info = self.get_dev_info();
            self.load_image_area(
                dev_info.memory_address,
                MemoryConverterSetting {
                    endianness: memory_converter_settings::MemoryConverterEndianness::LittleEndian,
                    bit_per_pixel:
                        memory_converter_settings::MemoryConverterBitPerPixel::BitsPerPixel4,
                    rotation: memory_converter_settings::MemoryConverterRotation::Rotate0,
                },
                &area_img_info,
                &buffer,
            )?;
        }
        Ok(())
    }
    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
    where
        I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
    {
        let dev_info = self.get_dev_info();
        let width = dev_info.panel_width as i32;
        let height = dev_info.panel_height as i32;
        for Pixel(coord, color) in pixels.into_iter() {
            if (coord.x >= 0 && coord.x < width) || (coord.y >= 0 || coord.y < height) {
                let data: u16 = (color.luma() as u16) << ((coord.x % 4) * 4);
                self.load_image_area(
                    dev_info.memory_address,
                    MemoryConverterSetting {
                        endianness:
                            memory_converter_settings::MemoryConverterEndianness::LittleEndian,
                        bit_per_pixel:
                            memory_converter_settings::MemoryConverterBitPerPixel::BitsPerPixel4,
                        rotation: memory_converter_settings::MemoryConverterRotation::Rotate0,
                    },
                    &AreaImgInfo {
                        area_x: coord.x as u16,
                        area_y: coord.y as u16,
                        area_w: 1,
                        area_h: 1,
                    },
                    &[data],
                )?;
            }
        }
        Ok(())
    }
}
impl<IT8951Interface: interface::IT8951Interface> OriginDimensions
    for IT8951<IT8951Interface, Run>
{
    fn size(&self) -> Size {
        let dev_info = self.dev_info.as_ref().unwrap();
        Size::new(dev_info.panel_width as u32, dev_info.panel_height as u32)
    }
}