#include "microOneWire.h"
#include "LittleFS.h" // LittleFS is declared
#include "Adafruit_TinyUSB.h"
#include <Time.h>
#include <Arduino_JSON.h>


//#define Adafruit_USBD_CDC HardwareSerial

extern "C" void Debugprintf(const char * format, ...);

int OWPIN = 15;    // Pin for DS1904 RTC

int PWRPIN = 12;      // Clipper LTE Power Pin
int RESETPIN = 13;       // Clipper LTE Reset Pin

int useDS1904 = 0;
int useClipper = 0;

char APN[64] = "data.uk";
char LTEUser[64] = "user";
char LTEPassword[64] = "one2one";
char LTESerialPort[32] = "Serial6:115200";

HardwareSerial * LTESerial = nullptr;

char SMSDEST[16] = "07760401072";
char ConnectOut1[] = "skig.g8bpq.net:8015";
char ConnectOut2[] = "Skig.g8bpq.net:8015";

int SMSEvents = 0;

// Equates for SMSEvent bits

#define SMSonRestart 1
#define SMSonCallFailed 2

Adafruit_USBD_CDC USBSer1;
Adafruit_USBD_CDC USBSer2;
Adafruit_USBD_CDC USBSer3;

// Serial Port Support

// Serial1 (0,1) SerialPIO (2,3 4,5 6,7) Serial2 (8,9) SerialPIO (10,11)
SerialPIO * Serial3 = nullptr;
SerialPIO * Serial4 = nullptr;
SerialPIO * Serial5 = nullptr;
SerialPIO * Serial6 = nullptr;

extern "C" void BPQTimerLoop();
extern "C" void BPQInit();
extern "C" void BPQFastPoll();
extern "C" char * strlop(char * buf, char delim);
extern "C" void ConTermInput(int n, char *Msg);
extern "C" char * stristr (char *ch1, char *ch2);

extern "C" char NODECALLLOPPED[10];

File cfg;

const char dummyConfig[] = 
  "SIMPLE\r\n"
  "NODECALL=DUMMY\r\n"
  "NODEALIAS=RP2040\r\n"
  "PASSWORD=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n"
  ""
  "PORT\r\n"
  "TYPE=INTERNAL\r\n"
  "ENDPORT\r\n"
  ""
  "PORT\r\n"
  "TYPE=ASYNC\r\n"
  "ID=KISS Serial1\r\n"
  "COMPORT=Serial1\r\n"
  "SPEED=57600\r\n"
  "QUALITY=0\r\n"
  "ENDPORT\r\n";

const char boardConfig[] =
"{\n"
"    \"NodeCall\": \"GE8BPQ\",\n"
"    \"Consoles\": [\"Serial\", \"USBSer1\", \"Serial6\"],\n"
"    \"Dial\": {\n"
"        \"SerialPort\": \"Serial6:115200\",\n"
"        \"APN\": \"data.uk\",\n"
"        \"User\": \"user\",\n"
"        \"Password\": \"one2one\",\n"
"        \"SMSDEST\": \"07760401072\",\n"
"        \"SMSEvents\": [\"Restart\", \"CallFailed\"],\n"
"        \"Callback1\": \"nottm.g8bpq.net:8015\",\n"
"        \"Type\": {\"Board\": \"Clipper\", \"PWRPIN\": 12, \"RESETPIN\": 13}\n"
"       },\n" 
"   \"RTC\": {\"Chip\": \"DS1904\", \"Pins\": [15]}\n"
"}\n";



HardwareSerial * CONSOLE;
HardwareSerial * CONTERM[4];
HardwareSerial * DEBUG;

char Consolenames[3][10];

int PWRKEY = 12;      // Clipper LTE Power Pin
int RESET = 13;       // Clipper LTE Reset Pin

//char apn[] = "iot.1nce.net";

/*
For data / GPRS:

Name: 1pMobile
APN: data.uk
User: user
Password: one2one
*/
void objRec(JSONVar myObject) {
  Serial.println("{");
  for (int x = 0; x < myObject.keys().length(); x++) {
    if ((JSON.typeof(myObject[myObject.keys()[x]])).equals("object")) {
      Serial.print(myObject.keys()[x]);
      Serial.println(" : ");
      objRec(myObject[myObject.keys()[x]]);
    }
    else {
      Serial.print(myObject.keys()[x]);
      Serial.print(" : ");
      Serial.println(myObject[myObject.keys()[x]]);
    }
  }
  Serial.println("}");
}

void getBoardConfig()
{
  JSONVar myObject = JSON.parse(boardConfig);
  
  for (int x = 0; x < myObject.keys().length(); x++) 
  {
      Serial.print(x);
      Serial.println(myObject.keys()[x]);
  }

 // Serial.println(myObject()[0]);
 // Serial.println(myObject()[1]);
 // Serial.println(myObject()[2]);

  if (myObject.hasOwnProperty("NodeCall"))
    Serial.println((const char*) myObject["NodeCall"]);

  if (myObject.hasOwnProperty("Dial"))
  {
    JSONVar dialObject = myObject["Dial"];
    for (int x = 0; x < dialObject.keys().length(); x++) 
    {
      Serial.print(x);
      Serial.println(dialObject.keys()[x]);
    }

    if (dialObject.hasOwnProperty("APN"))
      strlcpy(APN, dialObject["APN"], 63);

    if (dialObject.hasOwnProperty("User"))
      strlcpy(LTEUser, dialObject["User"], 63);

    if (dialObject.hasOwnProperty("Password"))
      strlcpy(LTEPassword, dialObject["Password"], 63);

    if (dialObject.hasOwnProperty("SMSDEST"))
      strlcpy(SMSDEST, dialObject["SMSDEST"], 63);

    if (dialObject.hasOwnProperty("Callback1"))
      strlcpy(ConnectOut1, dialObject["Callback1"], 63);
 
    if (dialObject.hasOwnProperty("Callback2"))
      strlcpy(ConnectOut2, dialObject["Callback2"], 63);

    if (dialObject.hasOwnProperty("SMSEvents"))
    {
      JSONVar myArray = dialObject["SMSEvents"];

      Serial.print("myArray.length() = ");
      Serial.println(myArray.length());
      Serial.println();

      //     \"SMSEvents\": [\"Restart\", \"CallFailed\"],\n"
      
    }

    if (dialObject.hasOwnProperty("Type"))
    {
      JSONVar typeObject = dialObject["Type"];

      for (int x = 0; x < typeObject.keys().length(); x++) 
      {
        Serial.print(x);
        Serial.println(typeObject.keys()[x]);
      }
 
      if (typeObject.hasOwnProperty("Board"))
      {
        char Board[64];

       strlcpy(Board, typeObject["Board"], 60);

        if (strcmp(Board, "Clipper") == 0)
        {
          useClipper = 1;

          if (typeObject.hasOwnProperty("PWRPIN"))
            PWRPIN = typeObject["PWRPIN"];

          if (typeObject.hasOwnProperty("RESETPIN"))
            RESETPIN = typeObject["RESETPIN"];

          Debugprintf("Board is %s POWERPIN %d RESETPIN %d", Board, PWRPIN, RESETPIN);       
        }
      }
      Debugprintf(ConnectOut1);
    } 
  }

  if (myObject.hasOwnProperty("Consoles"))
  {
    JSONVar consObject = myObject["Consoles"];

    for (int n = 0; n < consObject.length(); n++)
    {
       strlcpy(Consolenames[n], consObject[n], 10);
       Debugprintf("Console %d = %s", n, Consolenames[n]);
    }
 // Serial.println(myObject["Consoles"][2]);
  }
}


HardwareSerial * CreateSerial(char * Name)
{
  HardwareSerial * Device = nullptr;

  // Serial,  Serial1 and Serial 2 are always defined. Serial is normally a USB device
  
  if (strcmp(Name, "Serial") == 0)
    return (HardwareSerial *)&Serial;      
  
  if (strcmp(Name, "Serial1") == 0)
    return &Serial1;
  if (strcmp(Name, "Serial2") == 0)
    return &Serial2;
 
  else if (strcmp(Name, "Serial3") == 0)
  {
    if (Serial3 == nullptr)
      Serial3 = new SerialPIO(2, 3, 256);

    return Serial3;
  }
   else if (strcmp(Name, "Serial4") == 0) 
   {
    if (Serial4 == nullptr)
      Serial4 = new SerialPIO(4, 5, 256);
    
    return Serial4;
  }
  else if (strcmp(Name, "Serial5") == 0)  
  {
    if (Serial5 == nullptr)
      Serial5 = new SerialPIO(6, 7, 256);

    return Serial5;
  }
  else if (strcmp(Name, "Serial6") == 0)  
  {
    if (Serial6 == nullptr)
      Serial6 = new SerialPIO(10, 11, 256);
    
    return Serial6;
  }
 
  //else if (strcmp(Name, "USBSer2") == 0)
  //{
  //if (Serial4 == nullptr)
   //   Serial4 = new SerialPIO(4, 5, 256);
    //  Handles[i] = Serial4;
//  {
 //   USBHandles[i] = &USBSer2;
 //   isUSB[i] = 1;
 
  else
  {
    DEBUG->printf("Unknown port name %s\n", Name);
    return 0;
  }
}


extern "C" void initClipper()
{
  int ret = 0;
  int n = 10;
  char Line[512];
  char * ptr;

  if (LTESerial == nullptr)
  {
    if (LTESerialPort[0])
    {
      char Name[32];
      int Speed;
      
      strcpy(Name, LTESerialPort);

      Speed = atoi(strlop(Name, ':'));

      Debugprintf("LTE %s %d", Name, Speed);

      if (Name[0] && Speed)
      {
        LTESerial = CreateSerial(Name);

        if (LTESerial)
          LTESerial->begin(Speed);
      }
      else 
      {
        Debugprintf("Bad LTE Port or Speed %s", LTESerialPort);
      }
    
      if (LTESerial == nullptr)
      {
        Debugprintf("No Serial Port for LTE Modem");
        useClipper = 0;
        return;
      }

    }

    else
    {
      // Default to Serial6

      if (Serial6 == nullptr)
        Serial6 = new SerialPIO(10, 11, 256);

      Serial6->begin(115200);

      LTESerial = Serial6;

    }

  }
  
  Debugprintf("LTESerial Timeout %d", LTESerial->getTimeout());
  
  pinMode(PWRKEY, OUTPUT);
  pinMode(RESET, OUTPUT);

  digitalWrite(RESET, HIGH);

  digitalWrite(PWRKEY, HIGH);
  delay(1);
  digitalWrite(PWRKEY, LOW);
  delay(1);
  digitalWrite(PWRKEY, HIGH);

  CONTERM[2] = LTESerial;    // LTE Modem

  //AT+CGAUTH=1,2,"one2one","user"
  //AT+CGDCONT=1,"IP","data.uk"

  LTESerial->println("ATE0\r\n");         // echo off
  
  LTEReadAll(Line, 511, 1000);          // ATE0 Response
  LTESerial->println("AT+CIPMODE=1");    // TCP Transparent mode
  LTEReadAll(Line, 511, 1000);          // CIP Response
  LTESerial->println("AT+NETOPEN");     // Need to wait for NETOPEN response
  Debugprintf("Waiting for NETOPEN Response");
 
  LTEReadAll(Line, 511, 5000);        // Get OK from NETOPEN
  LTEReadAll(Line, 511, 5000);        // Should be NETOPEN reponse
 
   Debugprintf("Checking NETOPEN Response");
  if (strstr(Line, "+NETOPEN:"))
    Debugprintf("NETOPEN Ok");

  LTESerial->println("AT+IPADDR");
  LTESerial->println("AT+CCLK?");
  LTESerial->println("AT+CMGF=1");     // SMS Text Mode
  LTEReadAll(Line, 511, 5000);

  if (ptr = strstr(Line, "+CCLK:"))
  {
    // "24/11/13,17:22:51
    
    time_t tSet;
    struct tm tm;
    struct timeval now;

    ptr += 8;
  
    tm.tm_year = 10 * (ptr[0] - '0') + ptr[1] - '0' + 100;
    tm.tm_mon = 10 * (ptr[3] - '0') + ptr[4] - '0' - 1;
    tm.tm_mday = 10 * (ptr[6] - '0') + ptr[7] - '0';
    tm.tm_hour = 10 * (ptr[9] - '0') + ptr[10] - '0';
    tm.tm_min = 10 * (ptr[12] - '0') + ptr[13] - '0';
    tm.tm_sec = 10 * (ptr[15] - '0') + ptr[16] - '0';

    now.tv_sec = mktime(&tm);
    now.tv_usec=0;

    settimeofday(&now, NULL);
  }
  if (ptr = strstr(Line, "+IPADDR:"))
  {
    strlop(ptr, 13);
    Debugprintf("IP Address %s", ptr + 9);
  }

}


extern "C" void sendSMS(char * Number, char *text)
{
  char Line[256];
  
  LTESerial->printf("AT+CMGS=\"%s\"\r\n", Number);

  delay(3000);

  Debugprintf("%d", LTESerial->read());
  delay(100);
  Debugprintf("%d", LTESerial->read());
  delay(100);
  Debugprintf("%d", LTESerial->read());
  delay(100);
  Debugprintf("%d", LTESerial->read());

 // int ret = waitforResponse(Line, 255, 1000);
  //if (ret)
  {
 //   Debugprintf("Got %d %s", ret, Line);

  //  if (memcmp(Line, ">\r\n", 3) == 0)
     LTESerial->printf("%s\r\n%c", text, 26);        // ctrl/z on end
  }
}

uint32_t DS1904getClock()
{
  uint32_t Time;

  oneWire_reset(OWPIN);

  oneWire_write(0xcc, OWPIN);    // Skip addr
  oneWire_write(0x66, OWPIN);   // read RTC

  Time = oneWire_read(OWPIN);      // read and discard control byte
  Time = oneWire_read(OWPIN);
  Time |= oneWire_read(OWPIN) << 8;
  Time |= oneWire_read(OWPIN) << 16;
  Time |= oneWire_read(OWPIN) << 24;
  
  return Time;
}

extern "C" void DS1904setClock(uint32_t Time)
{
  oneWire_reset(OWPIN);

  oneWire_write(0xcc, OWPIN);    // Skip addr
  oneWire_write(0x99, OWPIN);   // write RTC - this is the write code
  oneWire_write(0xAC, OWPIN);  //This is the control byte.  AC in hex = 10101100


  oneWire_write(Time & 0xff, OWPIN); 
  oneWire_write((Time >> 8) & 0xff, OWPIN);  
  oneWire_write((Time >> 16) & 0xff, OWPIN); 
  oneWire_write((Time >> 24) & 0xff, OWPIN);

  oneWire_reset(OWPIN);
}
 

extern "C" void picocreateDefaultcfg()
{
  cfg = LittleFS.open("bpq32.cfg", "w");

  if (cfg)
  {
    cfg.write(dummyConfig, strlen(dummyConfig));
    cfg.close();
  }
}
 

void setup() 
{
  // put your setup code here, to run once:

  LittleFS.begin();

  Serial.begin(115200);

  int n = 10;

USBSer1.begin(115200);
USBSer2.begin(115200);
USBSer3.begin(115200);

  while (!Serial && n--)
    delay(1000);

  Serial.printf("Serial Timeout %d", Serial.getTimeout());

  Serial.println("Starting");


  // check to see if multiple CDCs are enabled
  if (CFG_TUD_CDC < 2 )
  {
    Serial.printf("To use multiple USB Serial ports CFG_TUD_CDC must be at least 2, current value is %u\n", CFG_TUD_CDC);
    Serial.println("Config file is located in arduino15/packages/rp2040/hardware/rp2040/4.2.0/libraries/Adafruit_TinyUSB_Arduino/src/arduino/ports/rp2040/tusb_config_rp2040.h");
    Serial.println("Continuing with one usb serial device");
  }


  // These may be changed from board.cfg but sensible to set here so Debugprintf works in config processing

  CONSOLE = (HardwareSerial *)&Serial;
  DEBUG = (HardwareSerial *)&Serial; 
  
  getBoardConfig();
 
  digitalWrite(OWPIN, LOW);         
  pinMode(OWPIN, INPUT);

  CONTERM[0] = (HardwareSerial *)&Serial;
  CONTERM[1] = (HardwareSerial *)&Serial;
 // CONTERM[1] = &USBSer1;
  
  struct timeval now;
  int rc;

//  now.tv_sec=DS1904getClock();
 // now.tv_usec=0;

 // rc=settimeofday(&now, NULL);

  //CONSOLE->printf("Starting. Last reset caused by %d", rp2040.

//UNKNOWN_RESET, PWRON_RESET, RUN_PIN_RESET, SOFT_RESET, WDT_RESET, DEBUG_RESET, GLITCH_RESET, BROWNOUT_RESET};

  rp2040.wdt_begin(8300);

  if (useClipper)
    initClipper();

  BPQInit();
  CONSOLE->print("Init Complete RTC Time ");
  CONSOLE->println(DS1904getClock());

//  sendSMS("07760401072", "picoNode restarted");


}

extern "C" int sendtoDialSession(char * Line, int rxed);

int LTEReadAll(char * Line, int len, int mS)
{
  // reads characters until they stop or for mS
  int start = millis();
  int end = millis() + mS;
  int ret = 0, c;

  rp2040.wdt_begin(8300);

  // wait for first

  while (end > millis())
  {
    c = LTESerial->read();

    if (c != -1)
    {
      // Got first

      Line[ret++] = c;

      // now read until no more available

      while(1)
      {
        delayMicroseconds(200);
        BPQFastPoll();
        c = LTESerial->read();

        if (c == -1 || ret == len)
        {
          Line[ret] = 0;
          return (ret);
        }
        Line[ret++] = c;
      }
    }
    BPQFastPoll(); 
  }
  
  Line[ret] = 0;
  return 0;
}

int tcpConnected = 0;
int tcpConnecting = 0;

extern "C" void tcpCall(char * host, int port)
{
  // AT+CIPOPEN=0,"TCP","NOTTM.g8bpq.net", 8015

  LTESerial->printf("AT+CIPOPEN=0,\"TCP\",\"%s\", %d\r\n", host, port);
  
  tcpConnecting = 1;
}

void pollLTE()
{
  // See if any input from LTE modem.

  if (useClipper == 0)
    return;

  char Line[512];
  int Len, ret;

  // if notification of a text message, get message and action

  // if attached to a terminal (dial command) send to user

  // if attached to a ConTerm, send to Node.

  // Otherwise write to debug log

  if (LTESerial->available())
  {
    ret = LTESerial->readBytesUntil('\n', Line, 511);
    Line[ret] = 0;
    
    if (ret)
    {
      // Check for Text Message (+CMT: "+447760401072","","24/11/11,16:38:25+0")

      if (memcmp(Line, "+CMT: ",  6) == 0)
      {
        int ret = LTEReadAll(Line, 511, 500);
        char host [256];
         
        Debugprintf("SMS %d, %s", ret, Line);

        // Get host to call

        if (stristr(Line, (char *)"Callback2"))
          strcpy(host, ConnectOut2);
        else
          strcpy(host, ConnectOut1);
        
        Debugprintf("Calling %s", host);

        char * port = strlop(host, ':');
        
        tcpCall(host, atoi(port));
        return;
      }

      if (tcpConnecting)
      {
        // look for CONNECT 115200 or CONNECT FAIL or ERROR
        Debugprintf(" look for CONNECT 115200 or CONNECT FAIL");
        if (strstr(Line, "CONNECT FAIL"))
          tcpConnecting = 0;
        else if (strstr(Line, "CONNECT 115200"))
        {
          tcpConnecting = 0;
          tcpConnected = 1;
          Debugprintf(NODECALLLOPPED);
          LTESerial->printf("%s\r", NODECALLLOPPED);
        }
        else if (strstr(Line, "ERROR"))
        {
          // try reinitialising

          tcpConnecting = 0;
          tcpConnected = 0;
          initClipper();
        }

      }

      if (tcpConnected)
      {
        // send to node command handler

         ConTermInput(2, Line);
         return;
      }
      

      if (sendtoDialSession(Line, ret))
        return;

      // Default 
      Debugprintf("%s",Line);  
    }
  }
}

uint32_t nextTimerRun = 0;

void loop() 
{
  rp2040.wdt_begin(8300);
  delay(1);

  BPQFastPoll();   // Run every mS to reduce chance of serial port overrun 

  pollLTE();
  
  if (millis() > nextTimerRun)    // Runtimer every 100 mS
  {
    nextTimerRun = millis() + 100;
    BPQTimerLoop();
  }
}

extern "C" void OutputDebugString(char * Mess)
{
  CONSOLE->print(Mess);
}

extern "C" int WritetoConsoleLocal(char * buff)
{
	return CONSOLE->printf("%s", buff);
}

extern "C" void Debugprintf(const char * format, ...)
{
	char Mess[10000];
	va_list(arglist);
	va_start(arglist, format);
	vsprintf(Mess, format, arglist);
	strcat(Mess, "\r\n");
	OutputDebugString(Mess);

	return;
}

extern "C" void Contermprint(int n, char * Msg)
{
  CONTERM[n]->print(Msg);
	return;
}


extern "C" uint64_t GetTickCount()
{
	return millis();
}


extern "C" void Sleep(int mS)
{
  delay(mS);
}

char * xxConfig;
int n;
char * rest;

extern "C" char * strlop(char * buf, char delim);

extern "C" int cfgOpen(char * FN, char * Mode)
{
   CONSOLE->printf("Open cfg %s\r\n", FN);

  cfg = LittleFS.open(FN, Mode);
  if (!cfg)
 {
     CONSOLE->println("file open failed");
    return 0;
 }

 xxConfig = (char *)malloc(8192);
 int n = cfg.read((unsigned char *)xxConfig, 8190);

  xxConfig[n] = 0;

  rest = xxConfig;

  cfg.close();

 return 1;

}
extern "C" int cfgClose()
{
  free(xxConfig);
  return 0;
}

extern "C" int cfgRead(unsigned char * Buffer, int Len)
{
  // return a line at a time

  if (rest == 0 || rest[0] == 0)
    return 0;

  char * p = strlop(rest, '\r');

  strcpy((char *)Buffer, rest);

  int n = strlen(p);

  rest = p;

  if (rest && rest[0] == '\n')
    rest++;

 return n;
}


extern "C" int ConTermKbhit(int n)
{
  return CONTERM[n]->read();
}


extern "C" int getFreeHeap()
{
  return rp2040.getFreeHeap();
}

// Serial Port Support

// Serial1 (0,1) SerialPIO (2,3 4,5 6,7) Serial2 (8,9) SerialPIO (10,11)

#define maxHandles 8

HardwareSerial * Handles[maxHandles] = {0};


extern "C" int picoOpenSerial(char * Name, int speed)
{
  // Find a free handle

  int i;
  
  for (i = 0; i < maxHandles; i++)
  {
    if (Handles[i] == 0)
      break;
  }

  if (i == 8)
  {
    DEBUG->println("No Serial Handles free");
    return 0;
  }

  if (strcmp(Name, "Serial1") == 0)
  {
    Serial1.setFIFOSize(256);
    Handles[i] = &Serial1;
  }
  else if (strcmp(Name, "Serial2") == 0)
  {
    Serial2.setFIFOSize(256);
    Handles[i] = &Serial2;
  }
  else if (strcmp(Name, "Serial3") == 0)
  {
    if (Serial3 == nullptr)
      Serial3 = new SerialPIO(2, 3, 256);
    Handles[i] = Serial3;
  }
   else if (strcmp(Name, "Serial4") == 0) 
   {
    if (Serial4 == nullptr)
      Serial4 = new SerialPIO(4, 5, 256);
    Handles[i] = Serial4;
  }
  else if (strcmp(Name, "Serial5") == 0)  
  {
    if (Serial5 == nullptr)
      Serial5 = new SerialPIO(6, 7, 256);
    Handles[i] = Serial5;
  }
  else if (strcmp(Name, "Serial6") == 0)  
  {
    if (Serial6 == nullptr)
      Serial6 = new SerialPIO(10, 11, 256);
    
    Handles[i] = Serial6;
  }
 
//  else if (strcmp(Name, "USBSer2") == 0)
//    Handles[i] = &USBSer2;
  
//  else if (strcmp(Name, "USBSer3") == 0)
 //   Handles[i] = &USBSer3;
 
  else
  {
    DEBUG->printf("Unknown port name %s\n", Name);
    return 0;
  }
  
  Handles[i]->setTimeout(0);
  Handles[i]->begin(speed);

  return i + 1;
}

extern "C" int picoWriteDial(char * Buffer, int Len)
{
  // Need to check available. Looks like it only returns 1/0 not actual space so have to check for each byte 

  for (int n = 0; n < Len; n++)
  {
    while(LTESerial->availableForWrite() == 0)
    {
      BPQFastPoll(); 
    }
    LTESerial->write(Buffer[n]);
  }
  return n;
}
extern "C" int picoCloseSerial(int Chan)
{
  return 0;
}

extern "C" int picoWriteSerial(int Chan, char * Buffer, int Len)
{
  if (Chan == 0)
    return 0;
    
  Chan--;

  // Need to check available. Looks like it only returns 1/0 not actual space so have to check for each byte 

  for (int n = 0; n < Len; n++)
  {
    while(Handles[Chan]->availableForWrite() == 0)
    {
      BPQFastPoll(); 
    }
    Handles[Chan]->write(Buffer[n]);
  }
  return Len;
}

extern "C" int picoReadSerial(int Chan, char * Buffer, int Len)
{
  int ptr = 0;
  int ret;

  if (Chan == 0)
    return 0;

  Chan--;

  if (Handles[Chan]->available() && ptr < Len)
  {
    ret = Handles[Chan]->read();
    Buffer[ptr++] = ret;
  }
 
  return ptr;
}

extern "C" int picoSerialGetLine(int Chan, char * Line, int maxline)
{
  int ret = 0;

 if (Chan == 0)
    return 0;

  Chan--;

  if (Handles[Chan]->available())
  {
     ret = Handles[Chan]->readBytesUntil('\n', Line, maxline);
     Line[ret] = 0;
  }
  return ret;
}



extern "C" int DoRoutes();
extern "C" int DoNodes();

extern "C" int SaveNodes ()
{
	cfg = LittleFS.open("BPQNODES.dat", "w");

  if (!cfg)
  {
    CONSOLE->println("BPQNODES create failed");
    return 0;
  }

  DoRoutes();
  DoNodes();
  
  cfg.close();
	return (0);
}

extern "C" void writeNodesLine(char * line)
{
  cfg.write(line, strlen(line));
}

File root;
File file;

extern "C" void OpenDirectory()
{
  root = LittleFS.open("/", "r");
  file = root.openNextFile();
}

extern "C" void GetNextDirEntry(char * line)
{
  if (file)
  {
    if (file.isDirectory())
      sprintf(line, "  DIR : %s", file.name());
    else 
      sprintf(line, ("  FILE: %s\tSIZE: %d"), file.name(), file.size());
    
    file = root.openNextFile();
    return;
  }
   
  line[0] = 0;
  file.close();
  root.close();
  return;
}




extern "C" int picoDelete(char * from)
{
  return LittleFS.remove(from);
}



extern "C" int picoRename(char * from, char * to)
{
  return LittleFS.rename(from, to);
}


extern "C" File * picoOpenFile(char * FN)
{
  if(!LittleFS.exists(FN))
    return 0;

  File * file = 0;
  file = new(File);

  *file = LittleFS.open(FN, "r");

  return file;
}

extern "C" int picoGetLine(File * file, char * Line, int maxline)
{
  int n = file->readBytesUntil('\n', Line, maxline);
  Line[n] = 0;

  return n;
}

extern "C" void picoCloseFile(File * file)
{
  file->close();
  delete file;
}

extern "C" int GetNextFileBlock(File * file, char * line)
{
  int n = file->read((unsigned char *)line, 128);
  line[n] = 0;

  return n;
}


extern "C" File * picoCreateFile(char * FN)
{
  File * file = 0;
  file = new(File);

  *file = LittleFS.open(FN, "w");

  return file;
}




extern "C" void picofprintf(File * file, const char * format, ...)
{
	char Mess[1000];
	va_list(arglist);
	va_start(arglist, format);
	int n = vsprintf(Mess, format, arglist);

  file->write(Mess, n);
}
  
extern"C" void picoWriteLine(File * file, char * Data, int len)
{
  file->write(Data, len);
}
  
extern "C" int createandwritefile(char * FN, char * Data, int len)
{
  File file = LittleFS.open(FN, "w");

  if (file)
  {
    file.write(Data, len);
    file.close();

    return 1;
  }
  
  return 0;     // open failed
}