//libraries
//  LCD is in sketch folder
//  SD/SPI should be included in Arduino IDE
//  RTC uses RTClib by Adafruit (search rtclib in Library Manager) https://github.com/adafruit/RTClib
//  IR uses IRREMOTE library (search irremote in Library Manager) https://github.com/Arduino-IRremote/Arduino-IRremote
//  WiFi libraries are part of Arduino-Pico

#include "LCD.h"
#include "Arial_round_16x24.c"
#include <SPI.h>
#include "RTClib.h"
#include "audio.h"
#include "sounds.c"
#include <WiFi.h>
#include <WiFiUdp.h>
#include <HTTPClient.h>
#include <IRremote.hpp>

#define IR_RECEIVE_PIN 22

button red=  {  3,240, 90,35,"Red",0,1};//x,y,w,h,text,pressed,visible
button green={ 99,240, 90,35,"Green",0,1};//x,y,w,h,text,pressed,visible
button blue= {195,240, 90,35,"Blue",0,1};//x,y,w,h,text,pressed,visible
button erase={291,240, 90,35,"Erase",0,1};//x,y,w,h,text,pressed,visible
button files={387,240, 90,35,"Files",0,1};//x,y,w,h,text,pressed,visible
slider backl={140,280,200,35,200,0,1};//x,y,w,h,value,pressed,visible

unsigned long draw=WHITE;

RTC_DS3231 rtc;
int rtcStatus=0;
DateTime t; //used whenever a DateTime is needed
int UTCoffset=0;

#define IPAPI_URL "http://ip-api.com/line?fields=city,offset"
int httpCode;
HTTPClient http;
WiFiClient wificlient;
#define IPAPIBUFSIZE 100
char ipapibuf[IPAPIBUFSIZE];
int ioffset=0;

//serial menus
char* s=0;
int menuState=-1; //set this to 0 when serial is ready
char ssidname[50]="";   //arrays for keeping the name and password
char ssidpass[50]="";

#define CORE_IDLE 0
#define CORE_WIFI_CONNECT 1
#define CORE_WIFI_GET 2
#define CORE_WIFI_GOT 3
int coreComms=CORE_IDLE;        //simple messaging between cores to allow some things to run async

void setup() {
  int i;
  Serial.begin(115200);
  //while(!Serial && millis()<5000){}
  displaySetup(); //this inits LCD controller, touch and backlight PWM
  setBacklight(100);
  setrotation(1);
  clear(BLACK);
  showarray(168,10,"Draw Demo",Arial_round_16x24,YELLOW,BLACK);
  drawbutton(&red);
  drawbutton(&green);
  drawbutton(&blue);
  drawbutton(&erase);
  drawbutton(&files);
  drawslider(&backl);
  Wire1.setSDA(10);
  Wire1.setSCL(11);
  rtcStatus=rtc.begin(&Wire1);  //BackPack uses I2C1 on pins 10 and 11
  if(rtcStatus){
    showarray(10,210,"RTC OK",Arial_round_16x24,GREY,BLACK);
  }else{
    showarray(10,210,"No RTC",Arial_round_16x24,GREY,BLACK);
  }
  audioInit();
  audioSetRate(8000);
  IrReceiver.begin(IR_RECEIVE_PIN, 0);
}

void loop() {
  static int lastx,lasty;
  int x,y,p,i;
  x=touchx();
  y=touchy();
  if((x>-1)&&(y>-1&&(lastx>-1)&&(lasty>-1))){
    line(lastx,lasty,x,y,draw);
  }
  lastx=x;
  lasty=y;
  slidercheckpress(&backl);
  if(backl.pressed){
    drawslider(&backl);
      setBacklight((backl.value)/3+20);      //save it being fully off
  }
  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(&files);
  if(files.lastchange==BUTTON_DOWN){
  }
  if(files.lastchange){drawbutton(&files);}
  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(&files);
    drawslider(&backl);
  }
  t= rtc.now();
  showTime(t,140,210);
  t= time(0);    //RP2040 RTC updated by NTP
  showTime(t,140,180);  
  if((erase.lastchange==BUTTON_DOWN)||(red.lastchange==BUTTON_DOWN)||(green.lastchange==BUTTON_DOWN)||(blue.lastchange==BUTTON_DOWN)||(files.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)||(files.lastchange==BUTTON_UP)){
    if(audioSpace()){
      audioQueue(beep,sizeof(beep));
      audioPlay(AUDIO_PLAY_MONO);
    }
  }
  //monitor IR pin
  for(i=0;i<480;i++){
    if(digitalRead(22)){
      point(i,50,WHITE);
    }else{
      point(i,50,BLACK);      
    }
  }
  showarray(10,90,"IP:",Arial_round_16x24,GREY,BLACK);
  showIP(WiFi.localIP(),60,90);
  switch(WiFi.status()){
    case WL_IDLE_STATUS:    showarray(10,60,"WiFi idle            ",Arial_round_16x24,GREY,BLACK);break;
    case WL_NO_SSID_AVAIL:  showarray(10,60,"WiFi: no SSID        ",Arial_round_16x24,GREY,BLACK);break;
    case WL_SCAN_COMPLETED: showarray(10,60,"WiFi: scan complete  ",Arial_round_16x24,GREY,BLACK);break;
    case WL_CONNECTED:      showarray(10,60,"WiFi: connected      ",Arial_round_16x24,GREY,BLACK);break;
    case WL_CONNECT_FAILED: showarray(10,60,"WiFi: connect failed ",Arial_round_16x24,GREY,BLACK);break;
    case WL_CONNECTION_LOST:showarray(10,60,"WiFi: connection lost",Arial_round_16x24,GREY,BLACK);break;
    case WL_DISCONNECTED:   showarray(10,60,"WiFi: disconnected   ",Arial_round_16x24,GREY,BLACK);break;
    default:                showarray(10,60,"WiFi status unknown. ",Arial_round_16x24,GREY,BLACK);break;
  }
  if(NTP.waitSet(100)){   //this returns 0 for OK
    showarray(10,180,"No NTP",Arial_round_16x24,GREY,BLACK);
  }else{
    showarray(10,180,"NTP OK",Arial_round_16x24,GREY,BLACK);
  }
  if((menuState==-1) && Serial){  //wait until serial port is open
    delay(10);
    menuState=0;
    printMenu();
  }
  s=scanSerial(); //this function returns a string when a complete line is received
  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){Serial.println("Enter SSID:");}
        if(menuState==3){Serial.println("Enter Password:");}
        if(menuState==4){
          Serial.println("Connecting.");
          coreComms=CORE_WIFI_CONNECT;
          menuState=0;
          printMenu();        
        }
        if(menuState==5){
          Serial.println("Stopping.");
          WiFi.end();
          Serial.println("Stopped.");
          menuState=0;
          printMenu();        
        }
        if(menuState==6){Serial.println("Enter UTC offset in minutes:");}
        if(menuState==7){
          t=time(0);                            //RP2040 RTC updated by NTP
          rtc.adjust(t+TimeSpan(UTCoffset*60)); //TimeSpan works in seconds
          Serial.println("RTC set.");
          menuState=0;
          printMenu();        
        }
        if(menuState==8){
          Serial.println("Fetching IPAPI data");      //see https://ip-api.com/docs/api:newline_separated
          http.begin(wificlient,IPAPI_URL);     //URL
          httpCode=http.GET();              //fetch
          Serial.print("Return code:");
          Serial.println(httpCode);
          if(httpCode==200){
            Serial.println(http.getString());
            for(i=0;i<IPAPIBUFSIZE;i++){
              ipapibuf[i]=http.getString()[i];
            }
            for(i=0;i<http.getString().length()-1;i++){   
              if(ipapibuf[i]<' '){                        //find newline
                ioffset=i+1;                              //mark location of field
                ipapibuf[i]=0;                            //turn newlines into nulls to separate fields
              }
            }
            showarray(10,120,"                    ",Arial_round_16x24,GREY,BLACK);    //clear a line
            showarray(10,120,ipapibuf,Arial_round_16x24,GREY,BLACK);                  //and display
            UTCoffset=atoi(&ipapibuf[ioffset])/60;                                    //update UTC
            Serial.print("UTC offset:");
            Serial.println(UTCoffset);
          }          
          menuState=0;
          printMenu();        
        }
        
        break;
      case 2:
        strcpy(ssidname,s);
        Serial.print("SSID set to:");
        Serial.println(ssidname);
        menuState=0;
        printMenu();
        break;
      case 3:
        strcpy(ssidpass,s);
        Serial.print("Password set to:");
        Serial.println(ssidpass);
        menuState=0;
        printMenu();
        break;
      case 6:
        UTCoffset=atoi(s);
        Serial.print("UTC offset:");
        Serial.println(UTCoffset);
        menuState=0;
        printMenu();
        break;
      default:      //default to main menu
        menuState=0;
        printMenu();
        break;
    }
  }
  if(IrReceiver.decode()){  // an IR code has been received
    switch(IrReceiver.decodedIRData.protocol){
      case NEC:   showarray(10,150,"NEC      ",Arial_round_16x24,GREY,BLACK);  break;
      case SHARP: showarray(10,150,"SHARP    ",Arial_round_16x24,GREY,BLACK);  break;
      case RC5:   showarray(10,150,"RC5      ",Arial_round_16x24,GREY,BLACK);  break;
      case RC6:   showarray(10,150,"RC6      ",Arial_round_16x24,GREY,BLACK);  break;
      case SONY:  showarray(10,150,"SONY     ",Arial_round_16x24,GREY,BLACK);  break;
      default:    showarray(10,150,"other    ",Arial_round_16x24,GREY,BLACK);  break;
    }
    char ss[20]="";
    snprintf(ss, sizeof(ss), "0x%04X 0x%04X",IrReceiver.decodedIRData.address,IrReceiver.decodedIRData.command);    
    showarray(154,150,ss,Arial_round_16x24,GREY,BLACK);
    IrReceiver.resume();      //reset for next
  }
}

void setup1(void){
  // don't need to do anything
}

void loop1(void){
  //this is the worst blocking function, so do it on the other core
  if(coreComms==CORE_WIFI_CONNECT){
    WiFi.begin(ssidname,ssidpass);
    NTP.begin("pool.ntp.org", "time.nist.gov");   //recommended NTP servers
    coreComms=CORE_IDLE;    //done
  }
}

void  doNetScan(){
  int i,n;
  Serial.println("Starting scan.");
  n=WiFi.scanNetworks();
  if(n==0){Serial.println("No networks found");return;}
  for(i=0;i<n;i++){
    Serial.printf("%32s %4d\n", WiFi.SSID(i), WiFi.RSSI(i));
  }
}

void printMenu(void){
  Serial.println();  
  Serial.println("Menu:");
  Serial.println("1: Scan networks");
  Serial.println("2: Enter SSID");
  Serial.println("3: Enter Password");
  Serial.println("4: Start connection");
  Serial.println("5: End connection");
  Serial.println("6: Set UTC offset");
  Serial.println("7: Set RTC from NTP");
  Serial.println("8: Fetch IPAPI data");
}

#define SCAN_LEN 50
char* scanSerial(void){
  static char b[SCAN_LEN]="";
  static int p=0;
  int d;
  if(p==0){b[p]=0;} //if data has been read, reset
  while(Serial.available()){
    d=Serial.read();
    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 showTime(DateTime 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.minute();
  s[14]=(n/10)%10+'0';
  s[15]=(n/1)%10+'0';
  s[16]=':';
  n=t.second();
  s[17]=(n/10)%10+'0';
  s[18]=(n/1)%10+'0';  
  s[19]=0;
  showarray(x,y,s,Arial_round_16x24,GREY,BLACK);  
}

void showIP(IPAddress ip,int x, int y){
  char s[16]="";
  int n;
  n=ip[0];
  s[0]=(n/100)%10+'0';
  s[1]=(n/10)%10+'0';
  s[2]=(n/1)%10+'0';
  s[3]=':';
  n=ip[1];
  s[4]=(n/100)%10+'0';
  s[5]=(n/10)%10+'0';
  s[6]=(n/1)%10+'0';
  s[7]=':';
  n=ip[2];
  s[8]=(n/100)%10+'0';
  s[9]=(n/10)%10+'0';
  s[10]=(n/1)%10+'0';
  s[11]=':';
  n=ip[3];
  s[12]=(n/100)%10+'0';
  s[13]=(n/10)%10+'0';
  s[14]=(n/1)%10+'0';
  s[15]=0;
  showarray(x,y,s,Arial_round_16x24,GREY,BLACK);  
}
