#include <asf.h>
#include <ctype.h>
#include "WiFi.h"
#include "Serial.h"
#include "string_func.h"
#include "utility.h"

//#define SSID       // insert your wifi SSID
//#define PASSWORD   // insert here your wifi network password

#ifndef SSID
#error network not defined
#endif

#ifndef PASSWORD
#error password not defined
#endif

static int cipmux = 0;

static enum State
{
	STATION = 1,AP = 2,AP_STATION = 3,
} wifi_state;


void WiFi_init(void)
{
	Serial_init();
	WiFi_restart();
	
	/* initialize AP mode and start TCP server */
	//WiFi_set_wifi_mode(2);
	//WiFi_set_IP_address("192.168.5.1");
	//WiFi_set_softAP_options("HI Pedini", "",5, 0);
	//WiFi_enable_multiple_connections(1);
	//WiFi_create_server(3301);
	
	/* initialize station mode, connect to router and open UDP socket */
	WiFi_set_wifi_mode(1);
	WiFi_join_access_point(SSID, PASSWORD);
	WiFi_enable_multiple_connections(0);
	WiFi_register_UDP_port(0,"0",0,1000,2);
}

/***** basic AT commands *****/

void WiFi_restart(void)
{
	Serial_write("AT+RST\r\n");
	Serial_find("ready\r\n");
}

void WiFi_set_baud_rate(int baud,int data_bits,int stop_bits,int parity,int flow_ctl)
{
	char buf[10];
	Serial_write("AT+UART_CUR=");
	Serial_write(inttoa(baud,buf));
	Serial_transmit(',');
	Serial_write(inttoa(data_bits,buf));
	Serial_transmit(',');
	Serial_write(inttoa(stop_bits,buf));
	Serial_transmit(',');
	Serial_write(inttoa(parity,buf));
	Serial_transmit(',');
	Serial_write(inttoa(flow_ctl,buf));
	Serial_write("\r\n");
	Serial_find("OK\r\n");
}

/***** WiFi related commands ******/

/*  set WiFi mode: 
 *   1 -> station mode
 *   2 -> softAP mode
 *   3 -> softAP + station mode
 */
void WiFi_set_wifi_mode(int mode)
{
	switch (mode)
	{
		case 1: Serial_write("AT+CWMODE_CUR=1\r\n");
			wifi_state = STATION;
			break;
		case 2: Serial_write("AT+CWMODE_CUR=2\r\n");
			wifi_state = AP;
			break;
		case 3: Serial_write("AT+CWMODE_CUR=3\r\n");
			wifi_state = AP_STATION;
			break;
	}
	Serial_find("OK\r\n");
}

void WiFi_list_access_points(char *list)
{
	//TODO with getline (gets/fgets) like function
}

/* join access point */
void WiFi_join_access_point(const char *ssid,const char *password)
{
	if (wifi_state != STATION && wifi_state != AP_STATION)
		return;
	Serial_write("AT+CWJAP_CUR=\"");
	Serial_write(ssid);
	Serial_write("\",\"");
	Serial_write(password);
	Serial_write("\"\r\n");
	Serial_find("OK\r\n");
}

/* quit access point */
void WiFi_quit_access_point(void)
{
	Serial_write("AT+CWQAP\r\n");
	Serial_find("OK\r\n");
}

/* set options as soft access point */
void WiFi_set_softAP_options(const char *ssid,const char *password,int channel_id,int encription)
{
	if (wifi_state != AP && wifi_state != AP_STATION)
		return;
	char buf[10];
	Serial_write("AT+CWSAP_CUR=\"");
	Serial_write(ssid);
	Serial_write("\",\"");
	Serial_write(password);
	Serial_write("\",");
	Serial_write(inttoa(channel_id,buf));
	Serial_transmit(',');
	Serial_write(inttoa(encription,buf));
	Serial_write("\r\n");
	Serial_find("OK\r\n");
}

void WiFi_list_stations(char *list)
{
	//TODO 
}

/***** TCP/IP related commands *****/

/*  set transfer mode 
 *   0 : normal mode
 *   1 : transparent mode
 */
void WiFi_set_IP_mode(int mode)
{
	switch (mode)
	{
		case 0: Serial_write("AT+CIPMODE=0\r\n");
				break;
		case 1: Serial_write("AT+CIPMODE=1\r\n");
				break;
	}
	Serial_find("OK\r\n");
}

void WiFi_set_IP_address(const char *ip)
{
	Serial_write("AT+CIPAP_CUR=\"");
	Serial_write(ip);
	Serial_write("\"");
	Serial_write("\r\n");
	Serial_find("OK\r\n");
}

/* enable multiple connections (must be 1 before starting TCP server) */
void WiFi_enable_multiple_connections(int enable)
{
	if (enable)
	{
		Serial_write("AT+CIPMUX=1\r\n");
		cipmux = 1;	
	}
	else
	{
		Serial_write("AT+CIPMUX=0\r\n");
		cipmux = 0;	
	}
	Serial_find("OK\r\n");
}

void WiFi_establish_TCP_connection(int connection_id,const char *ip,int port)
{
	if (cipmux == 0)
	{
		Serial_write("AT+CIPSTART=\"");
		Serial_write("TCP");
		Serial_write("\",\"");
		Serial_write(ip);
		Serial_write("\",");
		char buf[10];
		Serial_write(inttoa(port,buf));
	}
	else
	{
		Serial_write("AT+CIPSTART=");
		Serial_transmit(connection_id + '0');
		Serial_write(",\"");
		Serial_write("TCP");
		Serial_write("\",\"");
		Serial_write(ip);
		Serial_write("\",");
		char buf[10];
		Serial_write(inttoa(port,buf));
	}
	Serial_write("\r\n");
	Serial_find("OK\r\n");
}

void WiFi_register_UDP_port(int connection_id,const char *remote_ip,int remote_port,int local_port,int mode)
{
	if (cipmux == 0)
	{
		Serial_write("AT+CIPSTART=\"");
		Serial_write("UDP");
		Serial_write("\",\"");
		Serial_write(remote_ip);
		Serial_write("\",");
		char buf[10];
		Serial_write(inttoa(remote_port,buf));
		Serial_write(",");
		Serial_write(inttoa(local_port,buf));
		Serial_write(",");
		Serial_write(inttoa(mode,buf));
	}
	else
	{
		Serial_write("AT+CIPSTART=");
		Serial_transmit(connection_id + '0');
		Serial_write(",\"UDP\",\"");
		Serial_write(remote_ip);
		Serial_write("\",");
		char buf[10];
		Serial_write(inttoa(remote_port,buf));
		Serial_write(",");
		Serial_write(inttoa(local_port,buf));
		Serial_write(",");
		Serial_write(inttoa(mode,buf));
	}	
	Serial_write("\r\n");
	Serial_find("OK\r\n");
}

void WiFi_close_connection(int connection_id)
{
	Serial_write("AT+CIPCLOSE=");
	Serial_transmit(connection_id + '0');
	Serial_write("\r\n");
	Serial_find("OK\r\n");
}

/* start TCP server on port "port" (CIPMUX must be 1) */
int WiFi_create_TCP_server(int port)
{
	if (cipmux == 0)
		return WIFI_FAIL;
	Serial_write("AT+CIPSERVER=1,");
	char port_string[10];
	Serial_write(inttoa(port,port_string));
	Serial_write("\r\n");
	Serial_find("OK\r\n");
	return WIFI_SUCCESS;
}

/* stop TCP server */
void WiFi_delete_server(void)
{
	Serial_write("AT+CIPSERVER=0\r\n");
	Serial_find("OK\r\n");
}

void WiFi_send_data(int connection_id,const char data[])
{
	Serial_write("AT+CIPSEND=");
	Serial_transmit(connection_id + '0');
	Serial_transmit(',');
	int length = strlength(data);
	char buf[10];
	Serial_write(inttoa(length,buf));
	Serial_write("\r\n");
	Serial_find(">");
	Serial_write(data);
	Serial_find("SEND OK\r\n");
}

int WiFi_receive_data(char data[],int limit) 
{
	if (cipmux == 0)
	{
		if (!Serial_find_timeout("+IPD,", 100))
			return 0;
		int bytes_received = 0;
		char c;
		while (isdigit(c = Serial_receive()))
			bytes_received = c - '0' + bytes_received * 10;
		int i = 0;
		while (--limit > 0 && i < bytes_received)
			data[i++] = Serial_receive();
		data[i] = '\0';
	}
	else
	{
		if (!Serial_find_timeout("+IPD,", 100))
			return 0;
		int connection_id = Serial_receive() - '0';
		(void)Serial_receive();
		int bytes_received = 0;
		char c;
		while (isdigit(c = Serial_receive()))
			bytes_received = c - '0' + bytes_received * 10;
		int i = 0;
		while (--limit > 0 && i < bytes_received)
			data[i++] = Serial_receive();
		data[i] = '\0';
	}
	return 1;   // return if received AND connection?
}
