#include "main.h"
#include <stdio.h>
#include "CAN_data_load.h"
#include "stm32f1xx_hal_can.h"

CAN_TxHeaderTypeDef settings;
CAN_RxHeaderTypeDef properties;
CAN_DataStore storage;

/**
  * @brief  Setting of filter instance.
  * @param  sFilterConfig1 instance of CAN_FilterTypeDef
  * @param  instance of CAN_HandleTypeDef, chosen CAN interface
  * @retval None
  */
void CAN_SetFilterAutomotive(CAN_FilterTypeDef sFilterConfig,
		CAN_HandleTypeDef hcan) {
	/* filter setting */
	sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //set fifo assignment
	sFilterConfig.FilterIdHigh = 0x700 << 5; //the ID that the filter looks for 700-7FF (switch this for the other microcontroller)
	sFilterConfig.FilterIdLow = 0;
	sFilterConfig.FilterMaskIdHigh = 0x700 << 5; //7FF << 5;
	sFilterConfig.FilterMaskIdLow = 0;
	sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
	sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //set filter scale
	sFilterConfig.FilterBank = 0;
	sFilterConfig.SlaveStartFilterBank = 14; //0;
	sFilterConfig.FilterActivation = ENABLE;
	if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) == HAL_OK) {
		printf("\tFilter configuration successful\n");
	} else {
		printf("\tFilter configuration failed\n");
	}
}

/**
  * @brief  Setting a CAN message before transmit
  * @param  Length - number defining length of message
  * @param  Identifier - number defining identifier of message to transmit
  * @retval None
  */
void CAN_MsgSetup(uint32_t Length, uint32_t Identifier) {
	settings.RTR = CAN_RTR_DATA;
	settings.StdId = Identifier;
	settings.ExtId = 0;
	settings.IDE = CAN_ID_STD;
	settings.DLC = Length;
	settings.TransmitGlobalTime = DISABLE;
}

/**
  * @brief  Prints used fuel type
  * @retval None
  */
void WriteFuelType() {
	printf("Used fuel: ");
	switch (storage.FuelType) {
	case NOT_AVAILABLE:
		printf("Not available\n");
		break;
	case GASOLINE:
		printf("Gasoline\n");
		break;
	case METHANOL:
		printf("Methanol\n");
		break;
	case ETHANOL:
		printf("Ethanol\n");
		break;
	case DIESEL:
		printf("Diesel\n");
		break;
	case LPG:
		printf("LPG\n");
		break;
	case CNG:
		printf("CNG\n");
		break;
	case PROPANE:
		printf("Propane\n");
		break;
	case ELECTRIC:
		printf("Electric\n");
		break;
	default:
		printf("Other\n");
		break;
	}
}

/**
  * @brief  Transmits CAN message
  * @param  Data - data which we want send in message
  * @param  CAN_HandleTypeDef hcan - instance of CAN interface, we want to use
  * @retval None
  */
void CAN_TxMessage(uint8_t Data[8], CAN_HandleTypeDef hcan) {
	uint32_t TxMailBox = CAN_TX_MAILBOX0;
	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
	if (HAL_CAN_AddTxMessage(&hcan, &settings, Data, &TxMailBox) == HAL_OK) {
		//Message sent to TxMailBox
		if (HAL_CAN_IsTxMessagePending(&hcan, CAN_TX_MAILBOX0)) {
			HAL_Delay(2000);
			//Still pending in TxMailBox
			HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
		} else {
			//Already sent to CAN
			HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
			HAL_Delay(100);
		}
	} else {
		printf("Request send failed\n");
		HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
		HAL_Delay(100);
	}
}

/**
  * @brief  Prints CAN data.
  * @param  choose - number defining which data we want to print
  * @param  CAN_DataStore storage - storage instance with data for printing
  * @retval None
  */
void CAN_PrintOut(int choose, CAN_DataStore storage) {
	switch (choose) {
	case PRINT_DATA:
		printf(
				"Received data:\n| CANID: %03x | PID: %02x | DATA: %02x %02x %02x %02x %02x %02x %02x %02x |\n",
				storage.ID, storage.buffer[2], storage.buffer[0],
				storage.buffer[1], storage.buffer[2], storage.buffer[3],
				storage.buffer[4], storage.buffer[5], storage.buffer[6],
				storage.buffer[7]);
		break;
	case PRINT_VIN:
		printf("Vehicles VIN: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
				storage.VIN[0], storage.VIN[1], storage.VIN[2], storage.VIN[3],
				storage.VIN[4], storage.VIN[5], storage.VIN[6], storage.VIN[7],
				storage.VIN[8], storage.VIN[9], storage.VIN[10],
				storage.VIN[11], storage.VIN[12], storage.VIN[13],
				storage.VIN[14], storage.VIN[15], storage.VIN[16]);
		break;
	case PRINT_FUELTYPE:
		WriteFuelType();
		break;
	case PRINT_SITUATION:
		printf("Distance traveled since last code deleting: %d km\n",
				storage.DisTrav);
		printf("Vehicles speed: %d km/h\n", storage.VehSpeed);
		printf("Coolant temperature: %d C\n", storage.CoolTemp);
		printf("Engine run time: %d s\n", storage.RunTime);
		printf("Throttle position: %d %%\n", storage.ThrotPos);
		printf("Intake air temperature: %d C\n", storage.IAT);
		printf("Mass air flow rate: %d g/s\n", storage.MAF);
		printf("Intake manifold absolute pressure: %d kPa\n", storage.MAP);
		printf("Fuel tank level: %d %%\n", storage.FTLevel);
		printf("Engine speed: %d rpm\n", storage.EngSpeed);
		break;
	default:
		printf("Printing ERROR\n");
		break;
	}
}

/**
  * @brief  Receives responses for data requests
  * @param  instance of CAN_HandleTypeDef, chosen CAN interface
  * @param  RxFifo - chosen RxFIFO register for receiving
  * @param  buffer - array for saving received data
  * @retval None
  */
void CAN_RxData(CAN_HandleTypeDef hcan, uint32_t RxFifo, uint16_t buffer[]) {
	uint8_t buf[8];
	if (HAL_CAN_GetRxFifoFillLevel(&hcan, RxFifo) != 0) {
		if (HAL_CAN_GetRxMessage(&hcan, RxFifo, &properties, buf) == HAL_OK) {
			buffer[0] = properties.StdId;
			for (int var = 0; var < 8; ++var) {
				buffer[var + 1] = buf[var];
			}
		} else {
			printf("Any received message\n");
			Error_Handler();
		}
	} else {
		printf("Nothing received here\n");
	}
}

/**
  * @brief  Requests data about fuel type and saves received response
  * @param  instance of CAN_HandleTypeDef, chosen CAN interface
  * @retval None
  */
void CAN_GetFuelType(CAN_HandleTypeDef hcan) {
	CAN_MsgSetup(8, 0x7DF);
	uint8_t Data[8] = { 0x02, 0x01, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00 };
	uint16_t buffer[9];
	CAN_TxMessage(Data, hcan);
	HAL_Delay(100);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.FuelType = buffer[4];
}

/**
  * @brief  Requests data about VIN number and saves received response
  * @param  instance of CAN_HandleTypeDef, chosen CAN interface
  * @retval None
  */
void CAN_GetVIN(CAN_HandleTypeDef hcan) {
	CAN_MsgSetup(8, 0x7DF);
	uint8_t Data[8] = { 0x02, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
	uint16_t buffer[9];
	CAN_TxMessage(Data, hcan);
	while (HAL_CAN_GetRxFifoFillLevel(&hcan, CAN_RX_FIFO0) != 0) {
		CAN_RxData(hcan, CAN_RX_FIFO0, buffer); // first byte in 2nd and 3rd response are sequence bytes (21, 22), they show position
		storage.ID = buffer[0];
		for (int var = 0; var < 8; ++var) {
			storage.buffer[var] = buffer[var + 1];
		}
		if ((buffer[1] == 0x10) && (buffer[2] == 0x14)) {
			storage.VIN[0] = (char) buffer[6];
			storage.VIN[1] = (char) buffer[7];
			storage.VIN[2] = (char) buffer[8];
		} else if (buffer[1] == 0x21) {
			for (int var = 2; var < 9; ++var) {
				storage.VIN[var + 1] = (char) buffer[var];
			}
		} else if (buffer[1] == 0x22) {
			for (int var = 10; var < 17; ++var) {
				storage.VIN[var] = (char) buffer[var - 8];
			}
		}
		if ((buffer[3] == 0x49) && (buffer[4] == 0x02)) {
			uint8_t Data[8] = { 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // request for send rest of VIN
			CAN_MsgSetup(8, 0x7E0);
			CAN_TxMessage(Data, hcan);
		}
	}
}

/**
  * @brief  Requests data about various parameter (described in function) and saves received response
  * @param  instance of CAN_HandleTypeDef, chosen CAN interface
  * @retval None
  */
void CAN_GetSituation(CAN_HandleTypeDef hcan) {
	uint16_t buffer[9];
	/* distance traveled since codes were deleted */
	uint8_t distReq[8] = { 0x02, 0x01, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00 };
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.DisTrav = (buffer[4] * 256) + buffer[5];
	/* incorrect output from some reason */

	/* vehicle speed */
	distReq[2] = 0x0d;
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.VehSpeed = buffer[4];

	/* coolant temperature */
	distReq[2] = 0x05;
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.CoolTemp = buffer[4] - 40;

	/* engine run time	 */
	distReq[2] = 0x1f;
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.RunTime = 256 * buffer[4] + buffer[5];

	/* throttle position  */
	distReq[2] = 0x11;
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.ThrotPos = (buffer[4] * 100) / 255;

	/* Intake air temperature */
	distReq[2] = 0x0f;
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.IAT = buffer[4] - 40;

	/* mass air flow sensor */
	distReq[2] = 0x10;
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.MAF = (256 * buffer[4] + buffer[5]) / 100;

	/* Intake manifold absolute pressure */
	distReq[2] = 0x0b;
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.MAP = buffer[4];

	/* fuel tank level */
	distReq[2] = 0x2f;
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.FTLevel = (buffer[4] * 100) / 255;

	/* engine speed */
	distReq[2] = 0x0c;
	CAN_MsgSetup(8, 0x7DF);
	CAN_TxMessage(distReq, hcan);
	CAN_RxData(hcan, CAN_RX_FIFO0, buffer);
	storage.EngSpeed = (256 * buffer[4] + buffer[5]) / 4;
}

/**
  * @brief  Getter of data storage
  * @retval CAN_DataStore instance with saved data
  */
CAN_DataStore CAN_GetStorage() {
	return storage;
}
