# SPDX-FileCopyrightText: Copyright (c) 2023 NeoStormer
#
# SPDX-License-Identifier: MIT
"""
`cst816`
================================================================================

MicroPython driver for the CST816 capacitive touch screen IC


* Author(s): NeoStormer
* Ported from CircuitPython to MicroPython by zefie

Implementation Notes
--------------------

**Hardware:**

* `CST816 High Performance Self-Capacitance Touchchip
  <https://www.buydisplay.com/download/ic/DS-CST816S_DS_V1.3.pdf>`_

"""

import time
from machine import I2C, Pin

# I2C ADDRESS
_CST816_ADDR = const(0x15)

# Register Addresses
_CST816_GestureID = const(0x01)
_CST816_FingerNum = const(0x02)
_CST816_XposH = const(0x03)
_CST816_XposL = const(0x04)
_CST816_YposH = const(0x05)
_CST816_YposL = const(0x06)

_CST816_ChipID = const(0xA7)
_CST816_ProjID = const(0xA8)
_CST816_FwVersion = const(0xA9)
_CST816_MotionMask = const(0xAA)

_CST816_BPC0H = const(0xB0)
_CST816_BPC0L = const(0xB1)
_CST816_BPC1H = const(0xB2)
_CST816_BPC1L = const(0xB3)

_CST816_IrqPluseWidth = const(0xED)
_CST816_NorScanPer = const(0xEE)
_CST816_MotionSlAngle = const(0xEF)
_CST816_LpScanRaw1H = const(0xF0)
_CST816_LpScanRaw1L = const(0xF1)
_CST816_LpScanRaw2H = const(0xF2)
_CST816_LpScanRaw2L = const(0xF3)
_CST816_LpAutoWakeTime = const(0xF4)
_CST816_LpScanTH = const(0xF5)
_CST816_LpScanWin = const(0xF6)
_CST816_LpScanFreq = const(0xF7)
_CST816_LpScanIdac = const(0xF8)
_CST816_AutoSleepTime = const(0xF9)
_CST816_IrqCtl = const(0xFA)
_CST816_AutoReset = const(0xFB)
_CST816_LongPressTime = const(0xFC)
_CST816_IOCtl = const(0xFD)
_CST816_DisAutoSleep = const(0xFE)

# Modes
_CST816_Point_Mode = const(1)
_CST816_Gesture_Mode = const(2)
_CST816_ALL_Mode = const(3)

# Gestures
_CST816_Gesture_None = const(0)
_CST816_Gesture_Up = const(1)
_CST816_Gesture_Down = const(2)
_CST816_Gesture_Left = const(3)
_CST816_Gesture_Right = const(4)
_CST816_Gesture_Click = const(5)
_CST816_Gesture_Double_Click = const(11)
_CST816_Gesture_Long_Press = const(12)


class CST816:
    """Driver for the CST816 Touchscreen connected over I2C."""

    def __init__(self, i2c, irq, mode=_CST816_Point_Mode):
        self.irq = irq
        self.irq.init(mode=self.irq.IN, pull=self.irq.PULL_UP)
        self.i2c = i2c
        self.event = None
        self.prev_x = 0
        self.prev_y = 0
        self.prev_touch = None
        self.x_point = 0
        self.y_point = 0
        self.x_dist = 0
        self.y_dist = 0
        self.mode = mode
        self.reset()
        time.sleep_ms(5)
        self.id = self._i2c_read(0xA7, 3);
        self.fwvers = f"{self.id[1]}.{self.id[2]}"
        self.id = self.id[0]
        self.reset()
        self.set_mode(self.mode)
	devices = self.i2c.scan()
	self.available = (_CST816_ADDR in devices)

    def _get_irq(self):
        return (self.irq.value() == 1)
    
    def _i2c_write(self, reg, value=None):
        """Write to I2C"""
        if value:
            self.i2c.writeto(_CST816_ADDR, bytes([reg, value]))
        else:
            self.i2c.writeto(_CST816_ADDR, bytes([reg]))


    def _i2c_read(self, reg, size=1):
        """Read from I2C"""
        data = bytearray(size)
        self._i2c_write(reg)
        self.i2c.readfrom_into(_CST816_ADDR, data)
        if size == 1:
            return data[0]
        else:
            return data

    def get_chip_id(self):
        """Check the Chip ID"""
        return self.id
    
    def get_model_name(self):
        """Return chip model name based on ID, or NoneType if not found"""
        chipid = self.id        
        if chipid == 0x20:
            return "CST716"
        elif chipid == 0xB4:
            return "CST816S"
        elif chipid == 0xB5:
            return "CST816T"
        elif chipid == 0xB6:
            return "CST816D"
        elif chipid == 0x11:
            return "CST826"
        elif chipid == 0x12:
            return "CST830"
        elif chipid == 0x13:
            return "CST836U"
        elif chipid == 0x00 or chipid == 0xFF:
            return None
        else:
            return f"Unknown (0x{bytearray([chipid]).hex()})"

    def reset(self):
        """Make the Chip Reset"""
        self._i2c_write(_CST816_DisAutoSleep, 0x00)
        time.sleep(0.1)
        self._i2c_write(_CST816_DisAutoSleep, 0x01)
        time.sleep(0.1)

    def read_revision(self):
        """Read Firmware Version"""
        return self.fwvers

    def wake_up(self):
        """Make the Chip Wake Up"""
        self._i2c_write(_CST816_DisAutoSleep, 0x00)
        time.sleep(0.01)
        self._i2c_write(_CST816_DisAutoSleep, 0x01)
        time.sleep(0.05)
        self._i2c_write(_CST816_DisAutoSleep, 0x01)

    def stop_sleep(self):
        """Make the Chip Stop Sleeping"""
        self._i2c_write(_CST816_DisAutoSleep, 0x01)

    def set_mode(self, mode):
        """Set the Behaviour Mode"""
        if mode == _CST816_Point_Mode:
            self._i2c_write(_CST816_IrqCtl, 0x41)
        elif mode == _CST816_Gesture_Mode:
            self._i2c_write(_CST816_IrqCtl, 0x11)
            self._i2c_write(_CST816_MotionMask, 0x01)
        else:
            self._i2c_write(_CST816_IrqCtl, 0x71)
        self.mode = mode

    def get_point(self):
        """Get the Pointer Position"""
        data_raw = self._i2c_read(0x01, 8);
        self.gestureID = data_raw[0];
        self.points = data_raw[1];
        self.event = data_raw[2] >> 6;
        self.x_point = ((data_raw[2] & 0xF) << 8) + data_raw[3];
        self.y_point = ((data_raw[4] & 0xF) << 8) + data_raw[5];
        return self

    def get_event(self):
        return self.event
    
    def get_gesture(self):
        """Get the Gesture made by the User"""
        return self.gestureID
    
    def get_touch(self):
        """Detect User Presence, are they touching the screen?"""
        return self._get_irq()

    def get_distance(self):
        """Get the Distance made Between Readings, only while touched"""
        touch_data = self.get_point()
        x = touch_data.x_point
        y = touch_data.y_point
        if self.prev_touch is False and self.get_touch() is True:
            self.x_dist = 0
            self.y_dist = 0
        else:
            self.x_dist = x - self.prev_x
            self.y_dist = y - self.prev_y
        self.prev_touch = self.get_touch()
        self.prev_x = x
        self.prev_y = y
        return self
