/*
  Communicate for one of the three Serials
  The port where the reception occurred is automatically selected, but can also be selected manually.

  Copyright (C) 2023 T.Uemitsu
*/
#pragma once

#include <Arduino.h>

class CSerialMulti {
private:
  bool noinit = true;
  int channel = 0;
public:
  // Force selection of Serial to use 0=Serial 1=Serial1 2=Serial2
  void forcesel(int n) {
    channel = n;
  }

  // Get the number of data received
  int available(void) {
    if (noinit) return 0;
    if (Serial.available() > 0) {
      channel = 0;
      return Serial.available();
    }
    if (Serial1.available() > 0) {
      channel = 1;
      return Serial1.available();
    }
    if (Serial2.available() > 0) {
      channel = 2;
      return Serial2.available();
    }
    return 0;
  }

  // Get the currently selected Serial
  Print *port(void) {
    if (noinit) return NULL;
    available();
    switch (channel) {
      case 1:
        return &Serial1;
        break;
      case 2:
        return &Serial2;
        break;
      default:
        return &Serial;
        break;
    }
  }

  // Sending formatted strings
  int printf(const char *format, ...) {
    if (noinit) return 0;
    static char buf[200];
    va_list args;
    va_start(args, format);
    int result = vsnprintf(buf, sizeof(buf), format, args);
    va_end(args);
    port()->print(buf);
    return result;
  }

  // Flush transmit buffer
  void flush(void) {
    if (noinit) return;
    port()->flush();
  }

  // Read from selected Serial
  int read(void) {
    if (noinit) return 0;
    if (available() > 0) {
      switch (channel) {
        case 0:
          return Serial.read();
          break;
        case 1:
          return Serial1.read();
          break;
        case 2:
          return Serial2.read();
          break;
      }
    }
    return -1;
  }

  // Restart application
  void restart(void) {
    end();
    delay(200);
    watchdog_enable(1, 1);
    while (1)
      ;
  }

  // Activate Bootloader.
  void usbboot(void) {
    end();
    delay(200);
    reset_usb_boot(0, 0);
  }

  // initialization
  void begin(uint32_t baud) {
    Serial.begin(baud);
    Serial.ignoreFlowControl();

    Serial1.setTX(12);
    Serial1.setRX(13);
    gpio_pull_up(13);
    Serial1.setFIFOSize(512);
    Serial1.begin(baud);

    Serial2.setTX(4);
    Serial2.setRX(5);
    Serial2.setFIFOSize(512);
    Serial2.begin(baud);

    channel = 0;
    noinit = false;
  }

  void end(void) {
    if (noinit) return;
    Serial.end();
    Serial1.end();
    Serial2.end();
  }
};