/**
 * @file	tunerstudio_io.cpp
 *
 * @date Mar 8, 2015
 * @author Andrey Belomutskiy, (c) 2012-2016
 */

#include "main.h"
#include "tunerstudio_io.h"
#include "console_io.h"
#include "engine.h"
#if EFI_SIMULATOR || defined(__DOXYGEN__)
#include "rusEfiFunctionalTest.h"
#endif

EXTERN_ENGINE;

extern LoggingWithStorage tsLogger;

#if EFI_PROD_CODE || defined(__DOXYGEN__)
#include "pin_repository.h"
#include "usbconsole.h"
#include "map_averaging.h"
extern SerialUSBDriver SDU1;
#define CONSOLE_DEVICE &SDU1

#define TS_SERIAL_UART_DEVICE &SD3

static SerialConfig tsSerialConfig = { 0, 0, USART_CR2_STOP1_BITS | USART_CR2_LINEN, 0 };

void startTsPort(void) {
#if EFI_USB_SERIAL || defined(__DOXYGEN__)
	if (isSerialOverUart()) {
		print("TunerStudio over USB serial");
		/**
		 * This method contains a long delay, that's the reason why this is not done on the main thread
		 */
		usb_serial_start();
	} else
#endif
	{
		if (boardConfiguration->useSerialPort) {

			print("TunerStudio over USART");
			mySetPadMode2("tunerstudio rx", engineConfiguration->binarySerialRxPin, PAL_MODE_ALTERNATE(TS_SERIAL_AF));
			mySetPadMode2("tunerstudio tx", engineConfiguration->binarySerialTxPin, PAL_MODE_ALTERNATE(TS_SERIAL_AF));

			tsSerialConfig.speed = boardConfiguration->tunerStudioSerialSpeed;

			sdStart(TS_SERIAL_UART_DEVICE, &tsSerialConfig);
		}
	}
}

#endif /* EFI_PROD_CODE */

BaseChannel * getTsSerialDevice(void) {
#if EFI_PROD_CODE || defined(__DOXYGEN__)
	if (isSerialOverUart()) {
		// if console uses UART then TS uses USB
		return (BaseChannel *) &SDU1;
	} else {
		return (BaseChannel *) TS_SERIAL_UART_DEVICE;
	}
#else
	return (BaseChannel *) TS_SIMULATOR_PORT;
#endif
}

void tunerStudioWriteData(ts_channel_s *tsChannel, const uint8_t * buffer, int size) {
        efiAssertVoid(getRemainingStack(chThdSelf()) > 64, "tunerStudioWriteData");
#if EFI_SIMULATOR || defined(__DOXYGEN__)
			logMsg("chSequentialStreamWrite [%d]\r\n", size);
#endif
	int transferred = chnWriteTimeout(tsChannel->channel, buffer, size, BINARY_IO_TIMEOUT);
#if EFI_SIMULATOR || defined(__DOXYGEN__)
			logMsg("transferred [%d]\r\n", transferred);
#endif
	if (transferred != size) {
#if EFI_SIMULATOR || defined(__DOXYGEN__)
			logMsg("!!! NOT ACCEPTED %d out of %d !!!", transferred, size);
#endif
		scheduleMsg(&tsLogger, "!!! NOT ACCEPTED %d out of %d !!!", transferred, size);
	}
}

/**
 * Adds size to the beginning of a packet and a crc32 at the end. Then send the packet.
 */
void tunerStudioWriteCrcPacket(ts_channel_s *tsChannel, const uint8_t command, const void *buf, const uint16_t size) {
	uint8_t *writeBuffer = tsChannel->writeBuffer;

	*(uint16_t *) writeBuffer = SWAP_UINT16(size + 1);   // packet size including command
	*(uint8_t *) (writeBuffer + 2) = command;
	tunerStudioWriteData(tsChannel, writeBuffer, 3);      // header

	// CRC on whole packet
	uint32_t crc = crc32((void *) (writeBuffer + 2), 1); // command part of CRC
	crc = crc32inc((void *) buf, crc, (uint32_t) (size)); // combined with packet CRC
	*(uint32_t *) (writeBuffer) = SWAP_UINT32(crc);

	if (size > 0) {
		tunerStudioWriteData(tsChannel, (const uint8_t*)buf, size);      // body
	}

	tunerStudioWriteData(tsChannel, writeBuffer, 4);      // CRC footer
}

void tsSendResponse(ts_channel_s *tsChannel, ts_response_format_e mode, const uint8_t * buffer, int size) {
	if (mode == TS_CRC) {
		tunerStudioWriteCrcPacket(tsChannel, TS_RESPONSE_OK, buffer, size);
	} else {
		if (size > 0)
			tunerStudioWriteData(tsChannel, buffer, size);
	}
}

