/*******************************************************************
File: touch.c
Date: 9-September-2020
Author: Peter Spindler
Description: Touch controller driver
********************************************************************/

#include "stm32f7xx_hal.h"
#include "main.h"
#include "touch.h"
#include "display.h"
#include <stdio.h>

#define TS_I2C_HANDLE    hi2c3
extern I2C_HandleTypeDef TS_I2C_HANDLE;

#define TS_I2C_ADDRESS   0x70

#define TOUCH_WIDTH    240
#define TOUCH_HEIGHT   240

uint16_t Touch_Calx1    =  20, Touch_Caly1    =  20;
uint16_t Touch_Calx2    = 220, Touch_Caly2    = 220;

// Default calibration values:
uint16_t Touch_Calx1Raw =  33, Touch_Caly1Raw =  72;
uint16_t Touch_Calx2Raw = 230, Touch_Caly2Raw = 190;

static uint8_t Touch_ReadReg( uint8_t Reg ) {
	uint8_t DataRx = 0;
	HAL_I2C_Mem_Read( &TS_I2C_HANDLE, TS_I2C_ADDRESS, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, &DataRx, 1, 1000);
	return DataRx;
}

static void Touch_ReadRegMulti( uint8_t Reg, uint8_t *DataRx, uint16_t DataSize ) {
	HAL_I2C_Mem_Read( &TS_I2C_HANDLE, TS_I2C_ADDRESS, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, DataRx, DataSize, 1000);
}

static void Touch_WriteReg( uint8_t Reg, uint8_t Value ) {
	HAL_I2C_Mem_Write( &TS_I2C_HANDLE, TS_I2C_ADDRESS, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, &Value, 1, 1000);
}

uint8_t Touch_IsPressed( void ) {
	GPIO_PinState Pin = HAL_GPIO_ReadPin( CTP_INT_GPIO_Port, CTP_INT_Pin );
	if( Pin == GPIO_PIN_RESET ) {
		uint8_t Status = Touch_ReadReg( 0x02 ) & 0x0f;
		if( Status > 0 ) {
			return 1;
		}
	}
	return 0;
}

uint8_t Touch_ReadPosRaw( uint16_t *x, uint16_t *y ) {
	uint8_t DataRx[2][4];
	uint8_t Status = Touch_ReadReg( 0x02 ) & 0x0f;
	if( Status == 1 ) {
		Touch_ReadRegMulti( 0x03, DataRx[0], 4 );
	} else if( Status == 2 ) {
		Touch_ReadRegMulti( 0x03, DataRx[0], 4 );
		Touch_ReadRegMulti( 0x09, DataRx[1], 4 );
	} else {
		*x = 0;
		*y = 0;
		return 0;
	}

	uint16_t xRaw = ((DataRx[0][0] & 0x0f) << 8) | DataRx[0][1];
	uint16_t yRaw = ((DataRx[0][2] & 0x0f) << 8) | DataRx[0][3];

	// Swap x-y:
	*x = yRaw;
	*y = xRaw;

	// Mirror x:
	if( *x <= TOUCH_WIDTH-1 ) {
		*x = TOUCH_WIDTH-1 - *x;
	} else {
		*x = 0;
	}
	return 1;
}



void Touch_SetCalibration( uint16_t x1Raw, uint16_t y1Raw, uint16_t x2Raw, uint16_t y2Raw ) {
	Touch_Calx1Raw = x1Raw;
	Touch_Caly1Raw = y1Raw;
	Touch_Calx2Raw = x2Raw;
	Touch_Caly2Raw = y2Raw;
}

void Touch_ConvertPos( uint16_t *x, uint16_t *y ) {
	int16_t x_, y_;
	x_ = (*x - Touch_Calx1Raw)*(Touch_Calx2-Touch_Calx1)/(Touch_Calx2Raw-Touch_Calx1Raw) + Touch_Calx1;
	y_ = (*y - Touch_Caly1Raw)*(Touch_Caly2-Touch_Caly1)/(Touch_Caly2Raw-Touch_Caly1Raw) + Touch_Caly1;
	if( x_ < 0   ) x_ = 0;
	if( x_ > TOUCH_WIDTH-1 ) x_ = TOUCH_WIDTH-1;
	if( y_ < 0   ) y_ = 0;
	if( y_ > TOUCH_HEIGHT-1 ) y_ = TOUCH_HEIGHT-1;
	*x = x_;
	*y = y_;
}

uint8_t Touch_ReadPos( uint16_t *x, uint16_t *y ) {
	uint8_t ret;
	ret = Touch_ReadPosRaw( x, y );
	Touch_ConvertPos( x, y );
	return ret;
}

void Touch_Calibrate( void ) {
	uint16_t x, y, xTouch, yTouch;
	uint16_t x1=0, y1=0, x2=0, y2=0;
	uint8_t i=0, iPressed, Pressed;

	Display_PrintString( 0, 150, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "Press" );
	Display_PrintString( 0, 180, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "top/left");
	Display_FillCircle( Touch_Calx1, Touch_Caly1, 10, LCD_COLOR_WHITE );
	Pressed = 0;
	while( 1 ) {
		if( Touch_IsPressed() ) {
			if( Touch_ReadPosRaw( &x, &y ) ) {
				Display_Printf( 00, 210, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "%3d %3d ", x, y );
				xTouch = x;
				yTouch = y;
				Pressed = 1;
			}
		}

		// Abort with user button down:
		if( HAL_GPIO_ReadPin( GPIOA, GPIO_PIN_0 ) == GPIO_PIN_SET ) {
			if( Pressed ) {
				x1 = xTouch;
				y1 = yTouch;
			}
			break;
		}
	}

	// Wait for user button up:
	while( 1 ) {
		HAL_Delay(50);
		if( HAL_GPIO_ReadPin( GPIOA, GPIO_PIN_0 ) == GPIO_PIN_RESET ) {
			HAL_Delay(50);
			break;
		}
	}

	Display_Clear( LCD_COLOR_BLACK );
	Display_PrintString( 0, 0, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "Press" );
	Display_PrintString( 0, 30, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "buttom/right");
	Display_FillCircle( Touch_Calx2, Touch_Caly2, 10, LCD_COLOR_WHITE );

	Pressed = 0;
	while( 1 ) {
		if( Touch_IsPressed() ) {
			if( Touch_ReadPosRaw( &x, &y ) ) {
				Display_Printf( 0, 60, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "%3d %3d ", x, y );
				xTouch = x;
				yTouch = y;
				Pressed = 1;
			}
		}

		// Abort with user button down:
		if( HAL_GPIO_ReadPin( GPIOA, GPIO_PIN_0 ) == GPIO_PIN_SET ) {
			if( Pressed ) {
				x2 = xTouch;
				y2 = yTouch;
			}
			break;
		}
	}

	Display_Clear( LCD_COLOR_BLACK );
	Display_PrintString( 0, 0, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "Done" );
	Display_Printf( 0, 30, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "x1: %3d", x1 );
	Display_Printf( 0, 60, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "y1: %3d", y1 );
	Display_Printf( 0, 90, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "x2: %3d", x2 );
	Display_Printf( 0, 120, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "y2: %3d", y2 );

	// Stop if any calibration point not pressed:
	if( x1 == 0 || y1 == 0 || x2 == 0 || y2 == 0 ) {
		Display_PrintString( 0, 150, LCD_COLOR_WHITE, LCD_COLOR_BLACK, &FontBig, "Error!" );
		while( 1 );
	}

	// Wait for used button up:
	while( 1 ) {
		HAL_Delay(50);
		if( HAL_GPIO_ReadPin( GPIOA, GPIO_PIN_0 ) == GPIO_PIN_RESET ) {
			HAL_Delay(50);
			break;
		}
	}

	// Wait for user button down:
	while( 1 ) {
		if( HAL_GPIO_ReadPin( GPIOA, GPIO_PIN_0 ) == GPIO_PIN_SET ) {
			break;
		}
	}

	// Use calibration values and test with six buttons:
	Touch_SetCalibration( x1, y1, x2, y2 );

	struct {
		uint16_t x, y;
		uint16_t Width, Height;
	} Rect[6];

	x = 0;
	y = 0;
	for( i = 0; i < 6; i++ ) {
		Rect[i].x = x;
		Rect[i].y = y;
		Rect[i].Width = 110;
		Rect[i].Height = 70;
		x += 120;
		if( x+110 >= 240 ) {
			x =  0;
			y += 75;
		}
	}

	Display_Clear( LCD_COLOR_BLACK );

	for( i = 0; i < 6; i++ ) {
		Display_DrawRect( Rect[i].x, Rect[i].y, Rect[i].Width, Rect[i].Height, LCD_COLOR_WHITE );
	}

	uint8_t State = 0;
	while( 1 ) {
		if( State == 0 && Touch_ReadPos( &x, &y ) ) {
			iPressed = 0xff;
			for( i = 0; i < 6; i++ ) {
				if( x >= Rect[i].x && x <= Rect[i].x+Rect[i].Width &&
						y >= Rect[i].y && y <= Rect[i].y+Rect[i].Height ) {
					Display_FillRect( Rect[i].x, Rect[i].y, Rect[i].Width, Rect[i].Height, LCD_COLOR_WHITE );
					State = 1;
					iPressed = i;
					break;
				}
			}
		} else if( State == 1 && !Touch_IsPressed() ) {
			if( iPressed != 0xff ) {
				Display_FillRect( Rect[iPressed].x, Rect[iPressed].y, Rect[iPressed].Width, Rect[iPressed].Height, LCD_COLOR_BLACK );
				Display_DrawRect( Rect[i].x, Rect[i].y, Rect[i].Width, Rect[i].Height, LCD_COLOR_WHITE );
			}
			State = 0;
		}
	}

	while( 1 );
}



void Touch_Init( void ) {
	uint8_t ID;
	ID = Touch_ReadReg(0xA8);
	if( ID != 0xcd ) {
		printf("Error: Touch ID is %x, expected %x\n", ID, 0xcd );
		return;
	}

	Touch_WriteReg( 0xA4, 0 ); // Disable interrupt
}
