#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "pico/bootrom.h"
#include "pico/cyw43_arch.h"
#include "hardware/rtc.h"
#include "pico/util/datetime.h"
#include <time.h>
#include "lwip/apps/http_client.h"
#include "lwip/apps/sntp.h"
#include "LCD.h"
#include "LCD.c"
#include "audio.h"
#include "audio.c"
#include "sounds.c"
#include "Arial_round_16x24.c"

#define LED_PIN PICO_DEFAULT_LED_PIN
#define WIFI_TIMEOUT 10000

char* scanSerial(void);
char* s=NULL;
void printMenu(void);
static int scan_result(void *env, const cyw43_ev_scan_result_t *result);
void showIP(unsigned int ip,int x, int y);
void showTime(datetime_t t,int x,int y);
int menuState=0; 		
#define SCAN_LEN 50
char ssidname[SCAN_LEN]="";   //arrays for keeping the name and password
char ssidpass[SCAN_LEN]="";
void doNetScan(void);
unsigned int ipAdd=0;
datetime_t dt;	//for general usage
datetime_t tInit = {	//to set initial time/date
		.year  = 2022,
		.month = 01,
		.day   = 01,
		.dotw  = 6, // 0 is Sunday, so 6 is Saturday
		.hour  = 00,
		.min   = 00,
		.sec   = 00
};
//for doing HTTP requests
#define IPAPIHOST "ip-api.com"
#define IPAPIURL "/line?fields=city,offset"
httpc_state_t *connection = NULL;
err_t headers_done_fn(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len);
err_t recv_fn(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
void result_fn(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err);
httpc_connection_t httpconset = {
		.use_proxy = 0,
		.headers_done_fn = headers_done_fn,
		.result_fn = result_fn
};
#define HTTP_BUF_SIZE 1024
char httpbuf[HTTP_BUF_SIZE]="";
int tzoffset=0;
//NTP
void set_system_time(uint32_t sec);
int NTPok=0;

int main(){
    stdio_init_all();
    gpio_init(BLPIN);
    gpio_set_dir(BLPIN,GPIO_OUT);
    gpio_put(BLPIN,1);
	displaySetup();
	setbacklight(100);
    setrotation(1);  
    clear(BLACK);
	button red={2,280,88,35,"Red",0,1};//x,y,w,h,text,pressed,visible
	button green={98,280,88,35,"Green",0,1};//x,y,w,h,text,pressed,visible
	button blue={194,280,88,35,"Blue",0,1};//x,y,w,h,text,pressed,visible
	button erase={292,280,88,35,"Erase",0,1};//x,y,w,h,text,pressed,visible
	button light={390,280,88,35,"Light",0,1};//x,y,w,h,text,pressed,visible
	showarray(168,10,"Draw Demo",Arial_round_16x24,YELLOW,BLACK);
	drawbutton(&red);
	drawbutton(&green);
	drawbutton(&blue);
	drawbutton(&erase);
	drawbutton(&light);
	unsigned int draw=WHITE;
    int lastx=-1;
	int lasty=-1;
	int b=100;
	audioInit();
	audioSetRate(8000);
	cyw43_arch_init_with_country(CYW43_COUNTRY_WORLDWIDE);
	cyw43_arch_enable_sta_mode();	
	printMenu();
    rtc_init();    // Start the RTC
    rtc_set_datetime(&tInit);	
	while (true) {
		int x,y,p,wStat;
		x=touchx();
		y=touchy();
		if((x>-1)&&(y>-1)&&(lastx>-1)&&(lasty>-1)){line(lastx,lasty,x,y,draw);}
		lastx=x;
		lasty=y;
		checkpress(&red);
		if(red.lastchange==BUTTON_DOWN){draw=RED;}  //set draw colour on button down
		if(red.lastchange){drawbutton(&red);}        //redraw on change
		checkpress(&green);
		if(green.lastchange==BUTTON_DOWN){draw=GREEN;}
		if(green.lastchange){drawbutton(&green);}
		checkpress(&blue);
		if(blue.lastchange==BUTTON_DOWN){draw=BLUE;}
		if(blue.lastchange){drawbutton(&blue);}
		checkpress(&light);
		if(light.lastchange==BUTTON_DOWN){b=b+10;}  //change backlight
		if(light.lastchange){drawbutton(&light);}        //redraw on change
		checkpress(&erase);
		if(erase.lastchange==BUTTON_UP){drawbutton(&erase);clear(BLACK);}
		if(erase.lastchange){     //redraw all on screen erase
			drawbutton(&red);
			drawbutton(&green);
			drawbutton(&blue);
			drawbutton(&erase);
			drawbutton(&light);
			//reset_usb_boot(0,0);		//use to enter bootloader during testing
		}
		if(b>100){b=10;}
		setbacklight(b);
		if((erase.lastchange==BUTTON_DOWN)||(red.lastchange==BUTTON_DOWN)||(green.lastchange==BUTTON_DOWN)||(blue.lastchange==BUTTON_DOWN)||(light.lastchange==BUTTON_DOWN)){
			if(audioSpace()){
			  audioQueue(boop,sizeof(boop));
			  audioPlay(AUDIO_PLAY_MONO);
			}
		}
		if((erase.lastchange==BUTTON_UP)||(red.lastchange==BUTTON_UP)||(green.lastchange==BUTTON_UP)||(blue.lastchange==BUTTON_UP)||(light.lastchange==BUTTON_UP)){
			if(audioSpace()){
			  audioQueue(beep,sizeof(beep));
			  audioPlay(AUDIO_PLAY_MONO);
			}
		}
		//draw statuses
		//switch(cyw43_wifi_link_status(&cyw43_state,CYW43_ITF_STA)){
		switch(cyw43_tcpip_link_status(&cyw43_state,CYW43_ITF_STA)){		  
		  case CYW43_LINK_DOWN: 	showarray(10,60,"Link down     ",Arial_round_16x24,YELLOW,BLACK); break;
		  case CYW43_LINK_JOIN: 	showarray(10,60,"Connected     ",Arial_round_16x24,YELLOW,BLACK); break;
		  case CYW43_LINK_NOIP: 	showarray(10,60,"Connect, No IP",Arial_round_16x24,YELLOW,BLACK); break;
		  case CYW43_LINK_UP: 		showarray(10,60,"Connect, IP OK",Arial_round_16x24,YELLOW,BLACK); break;
		  case CYW43_LINK_FAIL: 	showarray(10,60,"Connect failed",Arial_round_16x24,YELLOW,BLACK); break;
		  case CYW43_LINK_NONET: 	showarray(10,60,"SSID not found",Arial_round_16x24,YELLOW,BLACK); break;
		  case CYW43_LINK_BADAUTH: 	showarray(10,60,"Auth failed   ",Arial_round_16x24,YELLOW,BLACK); break;
		  default: 					showarray(10,60,"Status unknown",Arial_round_16x24,YELLOW,BLACK); break;
		}
		if(cyw43_tcpip_link_status(&cyw43_state,CYW43_ITF_STA)==CYW43_LINK_NOIP){cyw43_cb_tcpip_set_link_up(&cyw43_state,CYW43_ITF_STA);}
		ipAdd=ip4_addr_get_u32(&(cyw43_state.dhcp_client.offered_ip_addr));
		showIP(ipAdd,10,90);	
		rtc_get_datetime(&dt);
		showarray(10,180,"RTC:",Arial_round_16x24,YELLOW,BLACK);
		showTime(dt,74,180);		
		if(NTPok){
			showarray(10,210,"RTC updated from NTP",Arial_round_16x24,GREEN,BLACK);
		}else{
			showarray(10,210,"Waiting for NTP     ",Arial_round_16x24,YELLOW,BLACK);
		}
		s=scanSerial();
		if(s){
			switch(menuState){
			    case 0:
					menuState=s[0]-'0';
					if(menuState<0){menuState=0;}
					if(menuState>8){menuState=0;}
					if(menuState==0){printMenu();}  //reprint
					if(menuState==1){
					  doNetScan();        //similar to ScanNetworks sketch
					  menuState=0;
					  printMenu();        
					}				
					if(menuState==2){printf("Enter SSID:\r\n");}				
					if(menuState==3){printf("Enter password:\r\n");}								
					if(menuState==4){
						printf("Connecting:\r\n");
						wStat=0;
						if(ssidpass==NULL){		//no password => no security
							wStat=cyw43_arch_wifi_connect_async(ssidname,NULL,CYW43_AUTH_OPEN);
						}else{
							wStat=cyw43_arch_wifi_connect_async(ssidname,ssidpass,CYW43_AUTH_WPA2_AES_PSK);
						}
						printf("Finished:%d\r\n",wStat);
						menuState=0;
						printMenu();  
					}						
					if(menuState==5){
						printf("Disconnect\r\n",wStat);
						//wStat=cyw43_arch_wifi_connect_async("_","_",CYW43_AUTH_OPEN);	//connect to nothing, doesn't seem reliable
						wStat=cyw43_wifi_leave(&cyw43_state,CYW43_ITF_STA);
						menuState=0;
						printMenu();        
					}
					if(menuState==6){
						printf("Fetching IPAPI\r\n");
						httpc_get_file_dns(IPAPIHOST,HTTP_DEFAULT_PORT,IPAPIURL,&httpconset, recv_fn, NULL, &connection);
						menuState=0;
						printMenu();        
					}
					break;
				case 2:
					strcpy(ssidname,s);
					printf("SSID set to:");
					printf(ssidname);
					printf("\r\n");
					menuState=0;
					printMenu();
					break;
				case 3:
					strcpy(ssidpass,s);
					printf("Password set to:");
					printf(ssidpass);
					printf("\r\n");					
					menuState=0;
					printMenu();
					break;



				
			}
		}
	}
    return 0;
}

char* scanSerial(void){
  static char b[SCAN_LEN]="";
  static int p=0;
  int d;
  int f=1;
  if(p==0){b[p]=0;} //if data has been read, reset
  while(f){
    d=getchar_timeout_us(0);
    if(d==PICO_ERROR_TIMEOUT){
		f=0;
	}else{
		putchar_raw(d);
		if(d>=' '){
		  if(p<SCAN_LEN-2){
			b[p]=d;
			p++;
			b[p]=0; //null term
		  }
		}else{
		  if(d==13){p=0;return b;}
		}
    }
  }
  return 0;
}

void printMenu(void){
  printf("\r\n");  
  printf("Menu:\r\n");
  printf("1: Scan networks\r\n");
  printf("2: Enter SSID\r\n");
  printf("3: Enter Password\r\n");
  printf("4: Start connection\r\n");
  printf("5: End connection\r\n");
  printf("6: Fetch IPAPI data\r\n");
}

void doNetScan(void){	
	cyw43_wifi_scan_options_t scan_options = {0};	//not actually used
	cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result);
	printf("Scanning\r\n");
	while(cyw43_wifi_scan_active(&cyw43_state)){}		//wait
	printf("Scan complete\r\n");
}

//this inpired by wifi-scan demo
static int scan_result(void *env, const cyw43_ev_scan_result_t *result) {
    if (result) {printf("%-32s rssi: %4d\r\n",result->ssid, result->rssi);}
    return 0;
}

void showIP(unsigned int ip,int x, int y){
	unsigned int col;
	int xx=x;
	int d=4;
	int oct;
	char c;
	if(ip){col=GREEN;}else{col=YELLOW;}
	showarray(xx,y,"IP:",Arial_round_16x24,col,BLACK);
	xx=xx+fontwidth(Arial_round_16x24)*3;
	while(d--){
		oct=ip&0xFF;
		c=(oct/100)+'0';
		showchar(xx,y,c,Arial_round_16x24,col,BLACK);
		xx=xx+fontwidth(Arial_round_16x24);
		c=((oct/10)%10)+'0';
		showchar(xx,y,c,Arial_round_16x24,col,BLACK);
		xx=xx+fontwidth(Arial_round_16x24);
		c=(oct%10)+'0';
		showchar(xx,y,c,Arial_round_16x24,col,BLACK);
		xx=xx+fontwidth(Arial_round_16x24);
		if(d){		//skip on last
			showchar(xx,y,':',Arial_round_16x24,col,BLACK);
			xx=xx+fontwidth(Arial_round_16x24);
		}
		ip=ip>>8;
	}
}

void showTime(datetime_t t,int x,int y){
  char s[30]="";
  int n;
  n=t.day;
  s[0]=(n/10)%10+'0';
  s[1]=(n/1)%10+'0';
  s[2]='/';
  n=t.month;
  s[3]=(n/10)%10+'0';
  s[4]=(n/1)%10+'0';
  s[5]='/';
  n=t.year;
  s[6]=(n/1000)%10+'0';
  s[7]=(n/100)%10+'0';
  s[8]=(n/10)%10+'0';
  s[9]=(n/1)%10+'0';
  s[10]=' ';
  n=t.hour;
  s[11]=(n/10)%10+'0';
  s[12]=(n/1)%10+'0';
  s[13]=':';
  n=t.min;
  s[14]=(n/10)%10+'0';
  s[15]=(n/1)%10+'0';
  s[16]=':';
  n=t.sec;
  s[17]=(n/10)%10+'0';
  s[18]=(n/1)%10+'0';  
  s[19]=0;
  showarray(x,y,s,Arial_round_16x24,YELLOW,BLACK);  
}

//these are callbacks used by HTTP calls from httpc_get_file_dns
err_t headers_done_fn(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len){
    printf("Headers of %d bytes report %d bytes of content.\r\n",hdr_len,content_len);	
    return ERR_OK;
}

err_t recv_fn(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err){
    printf("Received %d bytes.\r\n",p->len);	
    int i;
	int j;
	j=p->len;
	if(j>HTTP_BUF_SIZE-1){j=HTTP_BUF_SIZE-1;}
	for(i=0;i<j;i++){httpbuf[i]=((char*)(p->payload))[i];}
	httpbuf[j]=0;		//ensure null term
	return ERR_OK;
}

void result_fn(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err){
    int i, xx;
	printf("HTTP finished:%d\r\n",srv_res);	
	switch(httpc_result){
		case HTTPC_RESULT_OK: printf("OK\r\n"); break;
		case HTTPC_RESULT_ERR_UNKNOWN : printf("Unknown error\r\n"); break;
		case HTTPC_RESULT_ERR_CONNECT : printf("Connection to server failed\r\n"); break;
		case HTTPC_RESULT_ERR_HOSTNAME : printf("Failed to resolve hostname\r\n"); break;
		case HTTPC_RESULT_ERR_CLOSED  : printf("Connection unexpectedly closed\r\n"); break;
		case HTTPC_RESULT_ERR_TIMEOUT  : printf("Servere timeout\r\n"); break;
		case HTTPC_RESULT_ERR_SVR_RESP  : printf("Server error\r\n"); break;
		case HTTPC_RESULT_ERR_MEM  : printf("Memory error\r\n"); break;
		case HTTPC_RESULT_LOCAL_ABORT  : printf("Local abort\r\n"); break;
		case HTTPC_RESULT_ERR_CONTENT_LEN  : printf("Content length mismatch\r\n"); break;
	}
	printf("-----\r\n");	
	printf(httpbuf);
	printf("\r\n-----\r\n");	
	xx=10;
	i=0;
	while(xx<300){	//show up to end of first line
		if(httpbuf[i]<' '){break;}
		showchar(xx,120,httpbuf[i],Arial_round_16x24,WHITE,BLACK);
		xx=xx+fontwidth(Arial_round_16x24);	
		i++;		
	}
	i++;	//skip past line break
	tzoffset=atoi(&httpbuf[i]);	
	printf("Time zone offset set to %d seconds.\r\n",tzoffset);	
	//enable NTP to update
	sntp_setoperatingmode(SNTP_OPMODE_POLL);
	sntp_init();	
}

void set_system_time(uint32_t sec){
    time_t epoch = sec+tzoffset;
    datetime_t dt;
    struct tm *time = gmtime(&epoch);
	dt.year = (int16_t) (1900 + time->tm_year),
    dt.month = (int8_t) (time->tm_mon + 1),
    dt.day = (int8_t) time->tm_mday,
    dt.hour = (int8_t) time->tm_hour,
    dt.min = (int8_t) time->tm_min,
    dt.sec = (int8_t) time->tm_sec,
    dt.dotw = (int8_t) time->tm_wday,
    rtc_set_datetime(&dt);
	NTPok=1;
}
