#include "stm32f4xx_hal.h"
#include <string.h>
#include "globals.h"
#include "wifi.h"

/*
ESP      NET                    CPU
GPIO15 = ESP_RTS/USART1_CTS   = PA11
GPIO0  = GPIO0_ESP            = PA1
GPIO2  = GPIO2_ESP            = PA4
EN     = EN_ESP               = PA15

UART DOWNLOAD MODE : for flashing new firmware into the wifi module
GPIO15 : LOW 
GPIO0  : LOW 
GPIO2  : HIGH

FLASH BOOT MODE : normal use
GPIO15 : LOW
GPIO0  : HIGH
GPIO2  : HIGH

0C-ESP8266__WROOM_WiFi_Module_Datasheet__EN_v0.4
0B-ESP8266__Hardware_User_Guide__EN_v1.1.pdf
*/

// Configuration pour le flashage

// EN_ESP pin is active high
void enableWIFI(void)
{
	HAL_GPIO_WritePin(EN_ESP_GPIO_Port,EN_ESP_Pin,GPIO_PIN_SET);
}

void disableWIFI(void)
{
	HAL_GPIO_WritePin(EN_ESP_GPIO_Port,EN_ESP_Pin,GPIO_PIN_RESET);
}

// RST_ESP pin is active low
void enforceResetWIFI(void)
{
	HAL_GPIO_WritePin(RST_ESP_GPIO_Port,RST_ESP_Pin,GPIO_PIN_RESET);
}

void releaseResetWIFI(void)
{
	HAL_GPIO_WritePin(RST_ESP_GPIO_Port,RST_ESP_Pin,GPIO_PIN_SET);
}

void resetWIFI(void)
{
	enforceResetWIFI();
	waitBlocking(1000); // 1ms
	releaseResetWIFI();
}

void goWIFIUARTDownloadMode(void)
{
  enforceResetWIFI();	
	HAL_GPIO_WritePin(GPIO15_ESP_GPIO_Port,  GPIO15_ESP_Pin,GPIO_PIN_RESET); // set GPIO15_ESP low AKA PA11
	HAL_GPIO_WritePin(GPIO0_ESP_GPIO_Port,GPIO0_ESP_Pin,GPIO_PIN_RESET); // AKA PA1
// There is a pull up in the module on GPIO2 (r + blue the led to vcc)
// no need to drive it 
//	HAL_GPIO_WritePin(GPIO2_ESP_GPIO_Port,GPIO2_ESP_Pin,GPIO_PIN_SET);
	enableWIFI();
	waitBlocking(1000); // 1ms
	releaseResetWIFI();
}

void goWIFIFlashBootMode(void)
{
  enforceResetWIFI();	
	HAL_GPIO_WritePin(GPIO15_ESP_GPIO_Port,  GPIO15_ESP_Pin,GPIO_PIN_RESET); // set GPIO15_ESP low AKA A11
	HAL_GPIO_WritePin(GPIO0_ESP_GPIO_Port,GPIO0_ESP_Pin,GPIO_PIN_SET); // AKA PA1
// There is a pull up in the module on GPIO2 (r + blue the led to vcc)
// no need to drive it 
//	HAL_GPIO_WritePin(GPIO2_ESP_GPIO_Port,GPIO2_ESP_Pin,GPIO_PIN_SET);	
	enableWIFI();
	waitBlocking(1000); // 1ms
	releaseResetWIFI();
}


// renvoie tout ce qui arrive par UART2 vers UART1
// renvoie tout ce qui arrive par UART1 vers UART2
// Sortie sur Appui sur le Bouton Bleu

extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
//extern DMA_HandleTypeDef hdma_usart1_rx_;
//extern DMA_HandleTypeDef hdma_usart1_tx;
//extern DMA_HandleTypeDef hdma_usart2_tx;
//extern DMA_HandleTypeDef hdma_usart2_rx;
#ifdef DEBUG_WIFI_PARSER
// les commandes wifi sont envoyées sur la liaison série usb-cdc/usart2, et les réponses wifi sont aussi attendues de usb-cdc/usart2
// pour debug avec console sur pc
#define lhuart_Wifi huart2
#else
// les commandes sont envoyées sur usart1 connecté au module esp8266
// fonctionnement nominal
#define lhuart_Wifi huart1
#endif

//#define ldma_huart_Wifi_rx hdma_usart1_rx
//#define ldma_huart_Wifi_tx hdma_usart1_tx



	uint8_t from_pc_buffer[2];
	uint8_t from_wifi_buffer[2]; 

  enum WF_USART_MODE gWifiUsartMode = WF_UNDEFINED;

/* Two modes of operation
Mode 1 : pass thru
Mode 2 : udp stream processing
*/

/**
  * @brief  Rx Transfer completed callbacks.
  * @param  huart: pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
/*
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	switch(gWifiUsartMode)
	{
		case WF_UNDEFINED : break;
		case WF_PASSTHRU : 
		{
			if (huart==&lhuart_Wifi)
			{
				if (HAL_UART_Transmit_IT(&huart2,from_wifi_buffer,1)==HAL_BUSY)
					crash(CC_SERIAL_OVERFLOW_TX_PC);
				HAL_UART_Receive_IT(&lhuart_Wifi,from_wifi_buffer,1); // restart reception...
			}
			else if (huart==&huart2)
			{
				if (HAL_UART_Transmit_IT(&lhuart_Wifi,from_pc_buffer,1)==HAL_BUSY)
					crash(CC_SERIAL_OVERFLOW_TX_WIFI);					
				HAL_UART_Receive_IT(&huart2,from_pc_buffer,1);  // Restart reception
			}
			break;
		}
		case WF_PROCESSING : // process received data in DMA mode. In this case, interrupts only 3 levels. This is essentially to say, its geting fullXXX
		{
			if (huart==&lhuart_Wifi)
			{
			//
			}
			break;
		}
	}
}
*/
/*
// in theory, should disable interrupts during this processing
void myUART_ForceEnd_Receive_IT(UART_HandleTypeDef *huart)
{
  uint32_t tmp1 = huart->State; 
	
    tmp1 = huart->Instance->DR;
 
		huart->RxXferCount = 0;
		
  if((tmp1 == HAL_UART_STATE_BUSY_RX) || (tmp1 == HAL_UART_STATE_BUSY_TX_RX))
  {
    __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);

// Check if a transmit process is ongoing or not 
		if(huart->State == HAL_UART_STATE_BUSY_TX_RX) 
		{
			huart->State = HAL_UART_STATE_BUSY_TX;
		}
		else
		{
			// Disable the UART Parity Error Interrupt 
			__HAL_UART_DISABLE_IT(huart, UART_IT_PE);

			// Disable the UART Error Interrupt: (Frame error, noise error, overrun error) 
			__HAL_UART_DISABLE_IT(huart, UART_IT_ERR);

			huart->State = HAL_UART_STATE_READY;
		}	
 }
}
*/
void onePassThru(void)
{
	uint8_t c_from_wifi, c_from_pc;
	
	if (__HAL_UART_GET_FLAG(&lhuart_Wifi, UART_FLAG_RXNE) != RESET)
	{
					c_from_wifi = lhuart_Wifi.Instance->DR;
					if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE) != RESET)
						huart2.Instance->DR = c_from_wifi;
					else crash(CC_SERIAL_OVERFLOW_TX_PC);							
	}

	if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE) != RESET)
	{
					c_from_pc = huart2.Instance->DR;
					if (__HAL_UART_GET_FLAG(&lhuart_Wifi, UART_FLAG_TXE) != RESET)
						lhuart_Wifi.Instance->DR = c_from_pc;
					else crash(CC_SERIAL_OVERFLOW_TX_WIFI);							
	}	
}

uint16_t flushRxWifi(void)
{
	return lhuart_Wifi.Instance->DR;
}

/* USART1 init function with baudrate */
void MX_USART1_UART_Init_Bd(int baudrate)
{

  huart1.Instance = USART1;
  huart1.Init.BaudRate = baudrate;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  HAL_UART_Init(&huart1);

}
// En mode console, juste après boot, on sélectionne le mode par une touche clavier
// 1 seul caractere
// mode 'u' pour un boot du module wifi en mode flashage (upload)
// mode 'n' pour un boot du module wifi en mode normal  
// dans tous les cas, passe en mode usart pass thru jusqu'à la prochaine pression du bouton bleu.

//static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart);

uint8_t str_stimulate_auto_baud[]="AAAT\r\n";

void wifiPathThru(void)
{
	uint8_t str0[]="\r\n\r\nPass thru USB mode\r\n";
	uint8_t str1[]="Press 'u' for booting the ESP8266 in upgrade mode (for upgrading the flash of the ESP8266)\r\nPress 'n' for booting the ESP8266 in standard mode\r\n";
	uint8_t str2[]="\r\nYou are now in direct connection with the ESP8266 serial link, thru your USB port @ 115200 bauds\r\n";
	uint8_t str3[]="To leave this mode, press again the blue button, or reset the board (black button)\r\n";
	uint8_t str4[]="Flash Upgrade mode selected.\r\nNow type a few keys in order to have the module to set its auto baudrate to 115200 bauds\r\nThen disconnect your terminal from the serial port, and use the FlashDownloadTool\r\n";
	uint8_t str5[]="Normal Mode Selected\r\n";
	uint8_t str_booting_module[]="BOOTING MODULE\r\n";
	uint8_t response[1];
	uint8_t responded;
	float start;

	
	// code basique sans DMA
	
	
/*	
	// data venant du wifi
	HAL_DMA_Start(&hdma_usart1_rx, USART1->DR,(uint32_t) from_wifi_buffer , 1);
	// data venant du PC
	HAL_DMA_Start(&hdma_usart2_rx, USART2->DR,(uint32_t) from_pc_buffer , 1);
	HAL_DMA_Start(&hdma_usart1_tx, (uint32_t) from_pc_buffer, USART1->DR , 1);
*/	
	while(blueButtonIsDepressed()); // attente relache bouton
	waitBlocking(60000); //60ms
	
	responded = 0;


	do
	{
		HAL_UART_Transmit(&huart2,str0,sizeof(str0)-1,200);
		HAL_UART_Transmit(&huart2,str1,sizeof(str1)-1,200);

		response[0] = 0;

		HAL_UART_Receive(&huart2,response,1,10000); // 10 sec timout
		
		if (response[0]=='u')
		{
			responded = 1;
		}
		else if (response[0]=='n')
		{
			responded = 1;
		}
	} while(!responded);

	HAL_UART_Transmit(&huart2,str2,sizeof(str2)-1,200);
	HAL_UART_Transmit(&huart2,str3,sizeof(str3)-1,200);

		

	if (response[0]=='u')
	{	
		
		HAL_UART_Transmit(&huart2,str4,sizeof(str4)-1,200);
		HAL_UART_Transmit(&huart2,str_booting_module,sizeof(str_booting_module)-1,200);
		goWIFIUARTDownloadMode();
		

	}

	if (response[0]=='n')
	{	
		HAL_UART_Transmit(&huart2,str5,sizeof(str5)-1,200);
		HAL_UART_Transmit(&huart2,str_booting_module,sizeof(str_booting_module)-1,200);
		goWIFIFlashBootMode();
	}
	
	MX_USART1_UART_Init_Bd(74880);
	start = getTime();
	while(getTime()-start<2)
	{
		onePassThru();
		onePassThru();
	}
	MX_USART1_UART_Init_Bd(115200);
	
	HAL_UART_Transmit(&lhuart_Wifi,str_stimulate_auto_baud,sizeof(str_stimulate_auto_baud)-1,200);
	
	gWifiUsartMode = WF_PASSTHRU;
	
	while(!blueButtonIsDepressed()) // ressort quand bouton pressé
	{
		onePassThru();
	}	
	
	gWifiUsartMode = WF_PROCESSING;	
		
}


/* WIFI AT logic and choices 
ELECTROLAB:PROJETS:PERSO:2015:LEDTUBE

En mode SoftAP
Un seul client autorisé à la fois.
Bitrate sur la liaison série, il va falloir tester jusqu'où il peut aller au maximum, avec le risque que ce soit limité à 115200.

Au minimum, une interface WIFI basique UDP/IP RAW sera offerte pour y injecter des images et envoyer des commandes.
Utilise le module ESP-12-Q basé sur le ESP8266.
Le système ne peut recevoir que les choses suivantes:
**des images en RAW en une seule trame UDP pour les images fixes (si possible)
**une commande d'initialisation de la translation (nombre flottant 32 bits dans [0..1]. Si en dehors de [0..1], le système prendra le modulo flottant).
Par UDP
**un vecteur vitesse de translation horizontale (nombre flottant 32 bits). En tours par seconde. Peut être positif ou négatif.
Par UDP

Donc, connection softAP, 
adresse module wifi configuré en 192.168.0.1
DHCP activé
Une fois le mobile connecté en wifi, il peut envoyer des trames UDP sur le port 5125 (ELE)
Les trames doivent toutes avoir le préfix "LEDTUBE:" 

Commande init translation, exprimée en unité de tour : 
POS:%f
exemple : "LEDTUBE:POS:+0.2445$$"

Doit être dans [0,1[, des valeurs négatives dont autorisées dans certaines limites. En dehors des bornes fixées dans le code, la pos est forcée à 0.
Sachant que la pos est ramenée de toute façon dans [0,1[.   (]-1,0] == [0,1[)
Modifier POS ne modifie pas SPD.

Commande init vitesse : 
SPD:%f
exemple : "LEDTUBE:SPD:+0.1100$$"
exprimée en tour par seconde, peut être négatif.
doit être dans [-1,1].
Une vitesse positive produit une rotation vers la droite.
Modifier la vitesse à la volée ne produit pas de saccade.

Commande image : 
RAW:binary...
exemple : "LEDTUBE:RAW:RGBRGBRGBRGB...(60*20x)"

Le premier RGB reçu est le pixel en haut à gauche de l'image. Les suivants sont ceux de la même ligne en allant vers la droite. Puis on descend de ligne en ligne.

Max UDP RX Size ???

*/

/* Liste des commandes pour configurer correctement le module WIFI 
Supposé déjà configuré manuellement pour tourner à 115200 bauds.

Attente de la string indicative de la fin du boot du module wifi
Toutes les commandes doivent être terminées par \r\n
L'ordre des commandes est important

// Mode softAP Enable
AT+CWMODE_CUR=2
response OK

// CWSAP is to be done avec the enable.
// Open wifi, one station max. Show SSID
AT+CWSAP_CUR="LEDTUBE","12345678",channel,0,1,0
response OK,ERROR

//Enable SoftAP,DHCP
AT+CWDHCP_CUR=0,1
response OK

// Set IP Address
AT+CWDHCPS_CUR=1,3,"192.168.0.2","192.168.0.10"
response OK

// Set IP Address of softAP
AT+CIPAP_CUR="192.168.1.0","192.168.0.1","255.255.255.0"
response OK

// Wait for a connection

// Register UDP port for reception of UDP frames, single connection
// remote IP, remote port(not used), local udp port,udp mode 0
// udp mode 0 required for transparent UDP transmission
AT+CIPSTART="UDP","192.168.1.1",5125,5125,0
response OK,ERROR

// Pass Thru mode
AT+CIPMODE=1
response OK (XXX CHECK)

et BASTA

Need to send "+++" to exit pass thru mode.

*/

#define NB_WF_CMD_STARTUP 8
	
char *wifiS[NB_WF_CMD_STARTUP] = {
"ATE0", // do not echo, just reply !
"AT+CWMODE_CUR=2", // soft ap
"AT+CIPMUX=0", //
"AT+CWSAP_CUR=\"LEDTUBE\",\"\",4,0", 
// "AT+CWDHCPS_CUR=1,3,\"192.168.1.1\",\"192.168.1.2\"",
"AT+CWDHCP_CUR=0,1", // softAP DHCP enable 
"AT+CIPAP_CUR=\"192.168.1.0\",\"192.168.1.0\",\"255.255.255.0\"",
"AT+CIPSTART=\"UDP\",\"192.168.1.1\",5125,5125,0", //response : CONNECT\r\nOK\r\n
"AT+CIPMODE=1"	
};

char *expectedWifiReplieS[] = {
"ATE0\r\r\n\r\nOK\r\n",
"\r\nOK\r\n",
"\r\nOK\r\n",
"\r\nOK\r\n",
"\r\nOK\r\n",
"\r\nOK\r\n",
"CONNECT\r\n\r\nOK\r\n",
"\r\nOK\r\n"
};

/*
AT+CWMODE_CUR=2 // soft ap
AT+CIPMUX=0
AT+CWSAP_CUR="LEDTUBE","",4,0 // channel 4 rather ok at electrolab
//AT+CWDHCPS_CUR=1,3,"192.168.1.1","192.168.1.2"  // marche pas 
AT+CWDHCP_CUR=0,1   // softAP DHCP enable
AT+CIPAP_CUR="192.168.1.0","192.168.1.0","255.255.255.0"
AT+CIPSTART="UDP","192.168.1.1",5125,5125,0   //response : CONNECT\r\nOK\r\n
AT+CIPMODE=1
*/

unsigned char tmpStr[256];


// on USART1

uint8_t sendWIFIString(int cmdNumber) // blocking
{
	int len;
	HAL_StatusTypeDef halStatus;
	
	len = strlen(wifiS[cmdNumber]);
	
	// add cr lf at end of command
	strcpy((char*) tmpStr,wifiS[cmdNumber]);
	tmpStr[len] = '\r';
	tmpStr[len+1] = '\n';
	tmpStr[len+2] = 0;
	len+=2;
	
	halStatus = HAL_UART_Transmit(&lhuart_Wifi, tmpStr, len, 1000); // blocking
	
	switch(halStatus)
	{
		case HAL_ERROR:
		case HAL_BUSY:
		case HAL_TIMEOUT: 
			return 0;
		case HAL_OK : 
			return 1;
		default : crash(CC_UNKNOWN_HAL_STATUS_CODE);
						break;
		
	}
	
	return 1;
}


enum WF_CODES getWIFI_OKorERROR(const char *expectedString)
{
	HAL_StatusTypeDef halStatus;
	
	tmpStr[0] = 0;
	tmpStr[strlen(expectedString)] = 0;
	
	halStatus = HAL_UART_Receive(&lhuart_Wifi, tmpStr, strlen(expectedString), 5000);
	
	switch(halStatus)
	{
		case HAL_ERROR:
		case HAL_BUSY:
		case HAL_TIMEOUT: 
			return WF_HAL_ERROR;
		case HAL_OK : 
			break;
		default : 
			crash(CC_UNKNOWN_HAL_STATUS_CODE);
			break;
	}	
	
	if (strcmp(expectedString,(char*) tmpStr)!=0)
		return WF_ERROR;
	
	return WF_OK;
}



uint8_t startupWIFI(void)
{
	int i;
	
	goWIFIFlashBootMode(); // return to normal boot		
	myWait1ms(2000);
	HAL_UART_Transmit(&lhuart_Wifi,str_stimulate_auto_baud,sizeof(str_stimulate_auto_baud)-1,200);
	myWait1ms(200);
	flushRxWifi();
	for (i=0;i<NB_WF_CMD_STARTUP;i++)
	{
		if (!sendWIFIString(i))
			return 0;
		if (getWIFI_OKorERROR(expectedWifiReplieS[i])!=WF_OK)
			return 0;
	}
	gWifiUsartMode = WF_PROCESSING;
	return 1;
}


// Une machine d'état analyse le stream arrivant pour en extraire les commandes
// cf serial.c
// dma stream
 
//  __IO uint32_t CR;     /*!< DMA stream x configuration register      */
//  __IO uint32_t NDTR;   /*!< DMA stream x number of data register     */
//  __IO uint32_t PAR;    /*!< DMA stream x peripheral address register */
//  __IO uint32_t M0AR;   /*!< DMA stream x memory 0 address register   */
//  __IO uint32_t FCR;    /*!< DMA stream x FIFO control register       */

