// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov

//
// Demo text, binary and file upload
//

#include <Arduino.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
#include <RPAsyncTCP.h>
#include <WiFi.h>
#endif

#include <ESPAsyncWebServer.h>
#include <StreamString.h>
#include <LittleFS.h>

static AsyncWebServer server(80);

void setup() {
  Serial.begin(115200);

  if (!LittleFS.begin()) {
    LittleFS.format();
    LittleFS.begin();
  }

#ifndef CONFIG_IDF_TARGET_ESP32H2
  WiFi.mode(WIFI_AP);
  WiFi.softAP("esp-captive");
#endif

  // 1. Generate a Lorem_ipsum.txt file of about 20KB of text
  //
  // 3. Run: curl -v -F "data=@Lorem_ipsum.txt" http://192.168.4.1/upload/text
  //
  server.on(
    "/upload/text", HTTP_POST,
    [](AsyncWebServerRequest *request) {
      if (!request->_tempObject) {
        return request->send(400, "text/plain", "Nothing uploaded");
      }
      StreamString *buffer = reinterpret_cast<StreamString *>(request->_tempObject);
      Serial.printf("Text uploaded:\n%s\n", buffer->c_str());
      delete buffer;
      request->_tempObject = nullptr;
      request->send(200, "text/plain", "OK");
    },
    [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
      Serial.printf("Upload[%s]: start=%u, len=%u, final=%d\n", filename.c_str(), index, len, final);

      if (!index) {
        // first pass
        StreamString *buffer = new StreamString();
        size_t size = std::max(4094l, request->header("Content-Length").toInt());
        Serial.printf("Allocating string buffer of %u bytes\n", size);
        if (!buffer->reserve(size)) {
          delete buffer;
          request->abort();
        }
        request->_tempObject = buffer;
      }

      if (len) {
        reinterpret_cast<StreamString *>(request->_tempObject)->write(data, len);
      }
    }
  );

  // 1. Generate a Lorem_ipsum.txt file of about 20KB of text
  //
  // 3. Run: curl -v -F "data=@Lorem_ipsum.txt" http://192.168.4.1/upload/file
  //
  server.on(
    "/upload/file", HTTP_POST,
    [](AsyncWebServerRequest *request) {
      if (request->getResponse()) {
        // 400 File not available for writing
        return;
      }

      if (!LittleFS.exists("/my_file.txt")) {
        return request->send(400, "text/plain", "Nothing uploaded");
      }

      // sends back the uploaded file
      request->send(LittleFS, "/my_file.txt", "text/plain");
    },
    [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
      Serial.printf("Upload[%s]: start=%u, len=%u, final=%d\n", filename.c_str(), index, len, final);

      if (!index) {
        request->_tempFile = LittleFS.open("/my_file.txt", "w");

        if (!request->_tempFile) {
          request->send(400, "text/plain", "File not available for writing");
        }
      }
      if (len) {
        request->_tempFile.write(data, len);
      }
      if (final) {
        request->_tempFile.close();
      }
    }
  );

  //
  // Upload a binary file: curl -v -F "data=@file.mp3" http://192.168.4.1/upload/binary
  //
  server.on(
    "/upload/binary", HTTP_POST,
    [](AsyncWebServerRequest *request) {
      // response already set ?
      if (request->getResponse()) {
        // 400 No Content-Length
        return;
      }

      // nothing uploaded ?
      if (!request->_tempObject) {
        return request->send(400, "text/plain", "Nothing uploaded");
      }

      uint8_t *buffer = reinterpret_cast<uint8_t *>(request->_tempObject);
      // process the buffer

      delete buffer;
      request->_tempObject = nullptr;

      request->send(200, "text/plain", "OK");
    },
    [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
      Serial.printf("Upload[%s]: start=%u, len=%u, final=%d\n", filename.c_str(), index, len, final);

      // first pass ?
      if (!index) {
        size_t size = request->header("Content-Length").toInt();
        if (!size) {
          request->send(400, "text/plain", "No Content-Length");
        } else {
          Serial.printf("Allocating buffer of %u bytes\n", size);
          uint8_t *buffer = new (std::nothrow) uint8_t[size];
          if (!buffer) {
            // not enough memory
            request->abort();
          } else {
            request->_tempObject = buffer;
          }
        }
      }

      if (len) {
        memcpy(reinterpret_cast<uint8_t *>(request->_tempObject) + index, data, len);
      }
    }
  );

  server.begin();
}

// not needed
void loop() {
  delay(100);
}
