#include <cstdio>

#include "main.h"
#include "gpio.h"
#include "usart.h"

#include "gsm_thread.h"
#include "utils.h"


static constexpr GPIO_PinState GSM_POWER_LO_LEVEL = GPIO_PIN_SET;
static constexpr GPIO_PinState GSM_POWER_HI_LEVEL = GPIO_PIN_RESET;

static constexpr GPIO_PinState GSM_STATUS_MODULE_IS_ON = GPIO_PIN_SET;
static constexpr GPIO_PinState GSM_STATUS_MODULE_IS_OFF = GPIO_PIN_RESET;

static constexpr unsigned PWR_PULSE_DELAY_MS = 50U;								// See datasheet for A7672X
static constexpr ULONG PWR_STATUS_DELAY_MS = 10000U;							// See datasheet for A7672X (Typ. 7s)

static const char* const TAG = "GSM";
static uint16_t delay_ms = 5000U;


struct Gsm_req_resp_t
{
	const char* const prfx;
	const char* const pls;
	const char*	const req;
	const char* const eql;
	const char* const qstn;
	const char* const param;
	bool			  exec_once;
	bool			  is_exec;
	uint16_t		  delay_ms;
	const uint16_t	  repeat_num;
	uint16_t		  repeat_cnt;
};

static const char* const PRFX			  = "AT";
static const char* const PLS			  = "+";
static const char* const EQ				  = "=";
static const char* const QM				  = "?";
static const char* const CR				  = "\r";
static const char* const FMT			  = "%s%s%s%s%s%s%s";
static const char* const CPIN			  = "CPIN";
static const char* const CSQ			  = "CSQ";
static const char* const CGMI			  = "CGMI";
static const char* const CMGF			  = "CMGF";
static const char* const CMGS			  = "CMGS";
static const char* const CGNSSPWR		  = "CGNSSPWR";
static const char* const CGNSSPROD		  = "CGNSSPROD";
static const char* const CGNSSIPR		  = "CGNSSIPR";
static const char* const CGNSSPORTSWITCH  = "CGNSSPORTSWITCH";
static const char* const CGPSINFO		  = "CGPSINFO";
static const char* const CGNSSINFO		  = "CGNSSINFO";
static const char* const CGPSCOLD		  = "CGPSCOLD";
static const char* const CGNSSMODE		  = "CGNSSMODE";
static const char* const CWSTASCAN		  = "CWSTASCAN";
static const char* const BLEPOWER		  = "BLEPOWER";
static const char* const BLESTATUS		  = "BLESTATUS";
//static const char* const BTPOWER		  = "BTPOWER";
//static const char* const BTSCAN		  = "BTSCAN";
static const char* const BLESCAN		  = "BLESCAN";
static const char* const BLEADDR		  = "BLEADDR";
//static const char* const BTHOST		  = "BTHOST";
//static const char* const BTADDR		  = "BTADDR";
static const char* const BLEHOST		  = "BLEHOST";
static const char* const BLESREG		  = "BLESREG";
static const char* const BLESSAD		  = "BLESSAD";
static const char* const BLESSCAD		  = "BLESSCAD";
static const char* const BLESSDAD		  = "BLESSDAD";
static const char* const BLESSSTART		  = "BLESSSTART";
static const char* const BLESLSTART		  = "BLESLSTART";

static constexpr unsigned GSM_CMD_COUNT = 22U;
static Gsm_req_resp_t rr[GSM_CMD_COUNT] =	{					//	Once
	{ PRFX, PLS,  CPIN,				"",	QM, "",						false,	false, 25000U,	0	, 0 },
	{ PRFX, PLS,  CPIN,				EQ,	"", "5196",					true,	false, 30000U,	0	, 0 },
  //{ PRFX, PLS,  CGMI,				"",	"", "",						false,	false, 5000U,	0	, 0 },
  //{ PRFX, PLS,  CMGF,				"",	QM, "",						false,	false, 5000U,	0	, 0 },
  //{ PRFX, PLS,  CMGF,				EQ,	"", "1",					true,	false, 5000U,	0	, 0 },
  //{ PRFX, PLS,  CSQ,				"",	"", "",						false,	false, 5000U,	10	, 0 },
  //{ PRFX, PLS,  CGNSSIPR,			EQ,	QM, "",						false,	false, 5000U,	0	, 0 },
  //{ PRFX, PLS,  CGNSSIPR,			EQ,	"", "230400",				true,	false, 5000U,	0	, 0 },
	{ PRFX, PLS,  CGNSSPWR,			"",	QM, "",						false,	false, 5000U,	0	, 0 },
	{ PRFX, PLS,  CGNSSPWR,			EQ,	"", "1",					true,	false, 30000U,	0	, 0 },
	{ PRFX, PLS,  CGNSSPROD,		"",	"", "",						false,	false, 5000U,	0	, 0 },
	{ PRFX, PLS,  CGNSSPORTSWITCH,	"",	QM, "",						false,	false, 5000U,	0	, 0 },
	{ PRFX, PLS,  CGNSSPORTSWITCH,	EQ,	"", "1,0",					true,	false, 5000U,	0	, 0 },
	{ PRFX, PLS,  CSQ,				"",	"", "",						false,	false, 5000U,	0   , 0 },
  //{ PRFX, PLS,  CMGS,				EQ,	"", "+4917647653312",		true,	false, 20U,		1,  , 0	},
  //{ "",	"",	  "",				"",	"", "Test message!\032",	true,	false, 5000U,	1,  , 0	},
	{ PRFX, PLS,  CGPSINFO,			"",	"", "",						false,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  CGNSSINFO,		"",	"", "",						false,	false, 5000U,	0   , 0 },
  //{ PRFX, PLS,  CGPSCOLD,			"",	"", "",						true,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  CGNSSMODE,		"",	QM, "",						false,	false, 5000U,	0   , 0 },
  //{ PRFX, PLS,  CWSTASCAN,		EQ,	"", "1",					false,	false, 5000U,	0   , 0 },
  //{ PRFX, PLS,  CWSTASCAN,		"",	QM, "",						false,	false, 5000U,	0   , 0 },
  //{ PRFX, PLS,  CWSTASCAN,		"",	"", "",						false,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLEADDR,			"",	QM, "",						false,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLESTATUS,		"",	QM, "",						false,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLEHOST,			"",	QM, "",						false,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLEPOWER,			"",	QM, "",						false,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLEPOWER,			EQ,	"", "1",					true,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLESREG,			"",	"", "",						true,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLESSAD,			EQ,	"", "0,\"1802\",30,1,0",	true,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLESSCAD,			EQ,	"", "0,\"2A06\",4,38,3",	true,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLESSDAD,			EQ,	"", "0,\"2902\",4,0",		true,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLESSSTART,		EQ,	"", "0,0",					true,	false, 5000U,	0   , 0 },
	{ PRFX, PLS,  BLESLSTART,		EQ,	"", "0",					true,	false, 5000U,	0   , 0 },
											};

//static constexpr unsigned INPUT_BUF_SIZE = 512U;
static constexpr unsigned OUTPUT_BUF_SIZE = 64U;
//alignas(4) static char input_buf[INPUT_BUF_SIZE]  __attribute__((section(".RAM1")));
alignas(4) static char obuf[OUTPUT_BUF_SIZE]	  __attribute__((section(".RAM1")));
//static unsigned input_buf_num;


//------------------------------------------------------------------------------

void TurnGSMModuleOn(void);

//------------------------------------------------------------------------------

VOID gsmThread(ULONG initial_input)
{
	(void)initial_input;
	//extern DMA_HandleTypeDef hdma_usart3_tx;

	TurnGSMModuleOn();

	tx_thread_sleep(10000);

	while(1)
	{
		//printf("Waiting %ums...\n", 1000U);
		tx_thread_sleep(1000);
		//while(!input_buf_num) tx_thread_sleep(10);
		/*if (input_buf_num)
		{
			//printf("%lu\n", tx_time_get());
			printf("Got %2u bytes: [", input_buf_num);
			for (auto i = 0U; i < input_buf_num; i++)
				if (input_buf[i] >= ' ')	printf("%c", input_buf[i]);
				else printf(" %c", ' ');
			printf("]\n");
			input_buf_num = 0;
			//printf("%lu\n", tx_time_get());
		}*/

		static unsigned idx;
		unsigned i = idx % GSM_CMD_COUNT;
		if (!(rr[i].exec_once && rr[i].is_exec))
		{
			sprintf(obuf, FMT, rr[i].prfx, rr[i].pls, rr[i].req, rr[i].eql, rr[i].qstn, rr[i].param, CR);

			size_t len = strlen(obuf);
			if (len)
			{
				printf("Sending %02u bytes: {", len);
				for (auto j = 0U; j < len; j++) printf("%c", obuf[j] >= 0x20 ? obuf[j]: '_');
				printf("}\n");


				HAL_UART_Transmit_IT(&huart3, (uint8_t*)obuf, strlen(obuf));
				if (rr[i].exec_once) rr[i].is_exec = true;

				//printf("Waiting %ums...\n", rr[i].delay_ms);
				tx_thread_sleep(rr[i].delay_ms);
			}
		}

		if (rr[i].repeat_num)
		{
			if (++rr[i].repeat_cnt >= rr[i].repeat_num)
			{
				rr[i].repeat_cnt = 0;
				idx++;
			}
		}
		else idx++;
		//printThreadStackInfo(TAG);
	}
}

//------------------------------------------------------------------------------

void gsmUnitSentData(unsigned BytesNum)
{
	// Putting data into queue
	//printf("%lu\n", tx_time_get());
	//memcpy(input_buf, ibuf, BytesNum);
	//input_buf_num = BytesNum;
	//printf("%lu\n", tx_time_get());
	//HAL_UARTEx_ReceiveToIdle_IT(&huart3, (uint8_t*)ibuf, INPUT_BUF_SIZE);
}

//------------------------------------------------------------------------------

void TurnGSMModuleOn(void)
{
	// Activating power for GSM-unit
	HAL_GPIO_WritePin(POWER_4V_EN_GPIO_Port, POWER_4V_EN_Pin, GPIO_PIN_SET);

	// 4V power supply stabilization time
	tx_thread_sleep(1000U);

	// Checking whether STATUS pin for some reason is already set
	if (HAL_GPIO_ReadPin(GSM_STATUS_GPIO_Port, GSM_STATUS_Pin) == GSM_STATUS_MODULE_IS_OFF)
	{
		printf("GSM-module is OFF\n");

		constexpr int ATTEMPTS = 5;
		if ([]	  // Lambda checks if we exited after all attempts or earlier
			{
        		for (auto i = 1; i <= ATTEMPTS; i++)
        		{
        			printf("Trying to turn GSM-module ON...\n");

        			HAL_GPIO_WritePin(GSM_PWR_GPIO_Port, GSM_PWR_Pin, GSM_POWER_LO_LEVEL);
        			tx_thread_sleep(PWR_PULSE_DELAY_MS);
        			HAL_GPIO_WritePin(GSM_PWR_GPIO_Port, GSM_PWR_Pin, GSM_POWER_HI_LEVEL);

        			if ([]	  // Lambda checks if we exited after delay or before it
        				{
        					auto current_time = tx_time_get();
        					while (tx_time_get() - current_time < PWR_STATUS_DELAY_MS)
        					{
        						tx_thread_sleep(100U);
        						if (HAL_GPIO_ReadPin(GSM_STATUS_GPIO_Port, GSM_STATUS_Pin) == GSM_STATUS_MODULE_IS_ON)
        						{
        							printf("GSM-module is ON. Start-up time is approx. %lums\n", tx_time_get() - current_time);
        							return true;
        						}
        					}

        					return false;
        				}())
        			{
        				printf("GSM-module is turned ON! (Attempt %d from %d)\n", i, ATTEMPTS);
        				return true;
        			}
        			else printf("GSM-module is NOT turned ON! (Attempt %d from %d)\n", i, ATTEMPTS);
				}

				return false;
			}())
		{
			printf("GSM-module was successfullly turned ON\n");
		}
		else
		{
			printf("GSM-module cannot be turned ON\n");

			// Dectivating power for GSM-unit
			HAL_GPIO_WritePin(POWER_4V_EN_GPIO_Port, POWER_4V_EN_Pin, GPIO_PIN_SET);

			/*

			Inform other threads!!!

			*/
		}
	}
	else printf("GSM-module is already ON\n");
}

//------------------------------------------------------------------------------
