/*
 * ILI9341.cpp
 *
 *  Created on: Feb 11, 2024
 *      Author: Gabriel
 */

#include "ILI9341.hpp"
#include <cstring>

ILI9341::ILI9341(SPI_Peripheral* hSpi, GPIO_Pin* hNssPin, GPIO_Pin* hResetPin, GPIO_Pin* hDcrsPin, PWM_Pin* hLedPwm) :
	hSpi(hSpi), hNssPin(hNssPin), hResetPin(hResetPin), hDcrsPin(hDcrsPin), hLedPwm(hLedPwm) {

}

ILI9341::~ILI9341() {

}

// Public methods

int32_t ILI9341::init() {
	int32_t errors = 0;
	// Init pins
	errors += hNssPin->init();
	errors += hResetPin->init();
	errors += hDcrsPin->init();
	errors += hLedPwm->init();
	// Reset LCD
	errors += hResetPin->reset();
	vTaskDelay(1);
	errors += hResetPin->set();
	// Start SPI Command
	uint32_t id;
	errors += readReg24(0x04, &id);
	if(id != 0x858552){
		// Wrong ID
		return -1;
	}
	uint8_t idn[3];
	errors += readReg8(0xDA, idn);
	errors += readReg8(0xDB, idn+1);
	errors += readReg8(0xDC, idn+2);
	writeBuffer(0x11, nullptr, 0);
	vTaskDelay(5);
	uint8_t buf[720];
	uint8_t pasetBuf[4];
	for(uint16_t i = 0; i<320; i++){
		pasetBuf[0] = (i>>8);
		pasetBuf[1] = i;
		pasetBuf[2] = 319>>8;
		pasetBuf[3] = 319;
		writeBuffer(0x2B, pasetBuf, 4);
		for(uint32_t j = 0; j<720; j++){
			buf[j] = (j%64)<<2;
		}
		writeBuffer(0x2C, buf, 720);
	}
	writeBuffer(0x29, nullptr, 0);
	uint32_t status;
	errors += readReg32(0x09, &status);
	errors += hLedPwm->setDuty(1);
	return errors;
}

// Private methods

int32_t ILI9341::readReg8(uint8_t command, uint8_t* reg) {
	int32_t errors = 0;
	errors += hSpi->take(100);
	errors += hDcrsPin->reset();
	errors += hNssPin->reset();
	txBuffer[0] = command;
	errors += hSpi->trx(txBuffer, rxBuffer, 2);
	errors += hNssPin->set();
	errors += hSpi->give();
	if(reg != nullptr){
		*reg = rxBuffer[1];
	}
	return errors;
}

int32_t ILI9341::readReg24(uint8_t command, uint32_t* reg) {
	int32_t errors = 0;
	errors += hSpi->take(100);
	errors += hDcrsPin->reset();
	errors += hNssPin->reset();
	txBuffer[0] = command;
	errors += hSpi->trx(txBuffer, rxBuffer, 5);
	errors += hNssPin->set();
	errors += hSpi->give();
	if(reg != nullptr){
		*reg = (((uint32_t)rxBuffer[1])<<17) + (((uint32_t)rxBuffer[2])<<9) + (((uint32_t)rxBuffer[3])<<1) + (((uint32_t)rxBuffer[4])>>7);
	}
	return errors;
}

int32_t ILI9341::readReg32(uint8_t command, uint32_t* reg) {
	int32_t errors = 0;
	errors += hSpi->take(100);
	errors += hDcrsPin->reset();
	errors += hNssPin->reset();
	txBuffer[0] = command;
	errors += hSpi->trx(txBuffer, rxBuffer, 6);
	errors += hNssPin->set();
	errors += hSpi->give();
	if(reg != nullptr){
		*reg = (((uint32_t)rxBuffer[1])<<25) + (((uint32_t)rxBuffer[2])<<17) + (((uint32_t)rxBuffer[3])<<9) + (((uint32_t)rxBuffer[4])<<1) + (((uint32_t)rxBuffer[5])>>7);
	}
	return errors;
}

int32_t ILI9341::writeBuffer(uint8_t command, uint8_t* buffer, uint32_t length) {
	int32_t errors = 0;
	txBuffer[0] = command;
	if(length>0){
		memcpy(txBuffer+1, buffer, length);
	}
	errors += hSpi->take(100);
	errors += hDcrsPin->reset();
	errors += hNssPin->reset();
	errors += hSpi->transmit(txBuffer, 1);
	if(length>0){
		errors += hDcrsPin->set();
		errors += hSpi->transmit(txBuffer+1, length);
		errors += hDcrsPin->reset();
	}
	errors += hNssPin->set();
	errors += hSpi->give();
	return errors;
}
