//! RT3267 Touch driver
//!
//! Ported from https://github.com/mmMicky/TouchLib/blob/main/src/ModulesFT3267.tpp

#![no_std]

// Register definitions
#[allow(dead_code)]
mod regs;

use embedded_hal::i2c::I2c;

pub enum Ft3265Gesture {
    Ft3267GestureNone = 0x00,
    Ft3267GestureMoveUp = 0x10,
    Ft3267GestureMoveLeft = 0x14,
    Ft3267GestureMoveDown = 0x18,
    Ft3267GestureMoveRight = 0x1c,
    Ft3267GestureZomeIn = 0x48,
    Ft3267GestureZomeOut = 0x49,
}

/// FT3267 Driver.
///
/// Use to interact with the FT3267.
#[derive(Debug)]
pub struct Ft3267 {
    address: u8,
    rotation: u8,
}

/// Touch point coordinate.
///
/// Returned by gFt3275.get_point().
#[derive(Debug)]
pub struct TouchPoint {
    /// Touch point id, used on multitouch to distinguish touch position.
    pub id: u8,
    /// Touch point X coordinate.
    pub x: u16,
    /// Touch point Y coordinate.
    pub y: u16,
}

impl Ft3267 {
    fn write_register<I2C: I2c>(
        &self,
        bus: &mut I2C,
        reg_addr: u8,
        reg_value: u8,
    ) -> Result<(), I2C::Error> {
        let buffer: [u8; 2] = [reg_addr, reg_value];

        bus.write(self.address, &buffer)
    }

    fn read_register<I2C: I2c>(
        &self,
        bus: &mut I2C,
        reg_addr: u8,
        buffer: &mut [u8],
    ) -> Result<(), I2C::Error> {
        let addr_buffer: [u8; 1] = [reg_addr];

        // TODO : Error handling
        bus.write_read(self.address, &addr_buffer, buffer)
    }

    /// Build a new FT3257 driver
    ///
    /// rotation: touch screen rotation
    pub fn new(rotation: u8) -> Self {
        Ft3267 {
            address: regs::FT3267_ADDR,
            rotation,
        }
    }

    /// Initialize the driver IC.
    pub fn init<I2C: I2c>(&self, bus: &mut I2C) -> Result<&Self, I2C::Error> {
        self.write_register(bus, regs::FT3267_ID_G_THGROUP, 70)?;

        // valid touching peak detect threshold
        self.write_register(bus, regs::FT3267_ID_G_THPEAK, 60)?;

        // Touch focus threshold
        self.write_register(bus, regs::FT3267_ID_G_THCAL, 16)?;

        // threshold when there is surface water
        self.write_register(bus, regs::FT3267_ID_G_THWATER, 60)?;

        // threshold of temperature compensation
        self.write_register(bus, regs::FT3267_ID_G_THTEMP, 10)?;

        // Touch difference threshold
        self.write_register(bus, regs::FT3267_ID_G_THDIFF, 20)?;

        // Delay to enter 'Monitor' status (s)
        self.write_register(bus, regs::FT3267_ID_G_TIME_ENTER_MONITOR, 2)?;

        // Period of 'Active' status (ms)
        self.write_register(bus, regs::FT3267_ID_G_PERIODACTIVE, 12)?;

        // Timer to enter 'idle' when in 'Monitor' (ms)
        self.write_register(bus, regs::FT3267_ID_G_PERIODMONITOR, 40)?;

        Ok(self)
    }

    /// Pool if the touch screen is touched.
    ///
    /// Returns the number of current touch points.
    pub fn pool<I2C: I2c>(&self, bus: &mut I2C) -> Result<u8, I2C::Error> {
        let mut raw_data: [u8; 1] = [0];
        self.read_register(bus, regs::FT3267_TOUCH_POINTS, &mut raw_data)?;
        Ok(raw_data[0] & 0x0f)
    }

    /// Query if the touch screen is touched. If touch screen
    /// is un-touched, return None. Return Some() with detected
    /// finger count if touched (supports multi-touch).
    pub fn count<I2C: I2c>(&self, bus: &mut I2C) -> Result<Option<u8>, I2C::Error> {
        let touch_count = self.pool(bus)?;

        if touch_count > 0 {
            Ok(Some(touch_count))
        } else {
            Ok(None)
        }
    }

    /// Get the coordinate of a touch point
    ///
    /// n touch point index.
    /// return touch point coordinate.
    pub fn get_point<I2C: I2c>(&self, bus: &mut I2C, n: u8) -> Result<TouchPoint, I2C::Error> {
        let mut buf: [u8; 4] = [0; 4];

        match n {
            0 => {
                self.read_register(bus, regs::FT3267_TOUCH1_XH, &mut buf)?;
            }
            1 => {
                self.read_register(bus, regs::FT3267_TOUCH2_XH, &mut buf)?;
            }
            2 => {
                self.read_register(bus, regs::FT3267_TOUCH3_XH, &mut buf)?;
            }
            3 => {
                self.read_register(bus, regs::FT3267_TOUCH4_XH, &mut buf)?;
            }
            _ => {}
        }

        let x = (((buf[0] & 0x0f) as u16) << 8) + buf[1] as u16;
        let y = (((buf[2] & 0x0f) as u16) << 8) + buf[3] as u16;

        let coord = if self.rotation == 0 {
            TouchPoint { id: n, x, y }
        } else {
            TouchPoint { id: n, x: y, y: x }
        };
        Ok(coord)
    }
}
