// FactoryTestをWebからダウンロードして書き込む
// 参考：https://lang-ship.com/blog/work/m5stickc-web-update-ota/

#include <WiFi.h>
#include <Update.h>

const char *ssid0 = "ics-ap"; // 802.11b/g (2.4GHz)only. 5GHz is not supported.
const char *password0 = "jikkenics";

WiFiClient client;
void execOTA(String host, int port, String bin);
void lcdprintln(String mes, uint16_t color, int y)
{
    if (y == 0)
        M5.Lcd.fillScreen(color);
    if (y < 999)
        M5.Lcd.setCursor(0, y, 4);
    M5.Lcd.setTextColor(BLACK, color);
    M5.Lcd.println(mes);
}
void start_WebOTA()
{
    // M5.begin();
    // M5.Lcd.setRotation(3);
    lcdprintln(" -- WebOTA --\n\n wifi connecting...", ORANGE, 0);

    WiFi.begin(ssid0, password0); // 接続開始
    while (WiFi.status() != WL_CONNECTED)
    { // 接続中...
        M5.Speaker.tone(2000);
        delay(200);
        M5.Speaker.tone(0,1);
        delay(1000);
        M5.Lcd.print(".");
    }
    // 接続完了！！
    M5.Speaker.tone(4000);
    lcdprintln("  Wifi Connected!\n  ", GREEN, 0);
    String gotip = WiFi.localIP().toString(); // m5デバイスのIPアドレス
    M5.Lcd.println(gotip);
    delay(1500);
    M5.Speaker.tone(0,1);

    String host = "cit.istlab.info";
    int port = 80;
    String bin = "/f/m5scp2_exp.ino.bin";
    execOTA(host, port, bin);
}

// Utility to extract header value from headers
String getHeaderValue(String header, String headerName)
{
    return header.substring(strlen(headerName.c_str()));
}
// OTA Logic
void execOTA(String host, int port, String bin)
{
    // Connection Succeed
    Serial.println("");
    Serial.println("Connected to Wi-Fi");
    long contentLength = 0;
    bool isValidContentType = false;
    lcdprintln("  Downloading...", YELLOW, 0);
    if (client.connect(host.c_str(), port))
    {
        Serial.println("Fetching Bin: " + String(bin));
        client.print(String("GET ") + bin + " HTTP/1.1\r\n" +
                     "Host: " + host + "\r\n" +
                     "Cache-Control: no-cache\r\n" +
                     "Connection: close\r\n\r\n");
        unsigned long timeout = millis();
        while (client.available() == 0)
        {
            if (millis() - timeout > 5000)
            {
                Serial.println("Client Timeout !");
                client.stop();
                return;
            }
        }
        while (client.available())
        {
            String line = client.readStringUntil('\n');
            line.trim();
            if (!line.length())
            {
                break;
            }
            if (line.startsWith("HTTP/1.1"))
            {
                if (line.indexOf("200") < 0)
                {
                    Serial.println("Got a non 200 status code from server. Exiting OTA Update.");
                    break;
                }
            }
            if (line.startsWith("Content-Length: "))
            {
                contentLength = atol((getHeaderValue(line, "Content-Length: ")).c_str());
                Serial.println("Got " + String(contentLength) + " bytes from server");
            }
            if (line.startsWith("Content-Type: "))
            {
                String contentType = getHeaderValue(line, "Content-Type: ");
                Serial.println("Got " + contentType + " payload.");
                if (contentType == "application/octet-stream")
                {
                    isValidContentType = true;
                }
            }
        }
    }
    else
    {
        Serial.println("Connection to " + String(host) + " failed. Please check your setup");
    }
    Serial.println("contentLength : " + String(contentLength) + ", isValidContentType : " + String(isValidContentType));
    if (contentLength && isValidContentType)
    {
        bool canBegin = Update.begin(contentLength);
        if (canBegin)
        {
            lcdprintln("  Downloaded.\n Upgrading...(wait 2-3 min.)", GREENYELLOW, 999);
            Serial.println("Begin OTA. This may take 2 - 5 mins to complete. Things might be quite for a while.. Patience!");
            size_t written = Update.writeStream(client);
            if (written == contentLength)
            {
                Serial.println("Written : " + String(written) + " successfully");
            }
            else
            {
                Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?");
            }
            if (Update.end())
            {
                M5.Speaker.tone(4000);
                lcdprintln("\n\n  OTA Done. \n shutdown now", GREENYELLOW, 0);

                delay(2000);
                M5.Speaker.tone(0,1);

                Serial.println("OTA done!");
                if (Update.isFinished())
                {
                    Serial.println("Update successfully completed. Rebooting.");
                    // ESP.restart();
                    M5.Power.powerOff();
                }
                else
                {
                    Serial.println("Update not finished? Something went wrong!");
                }
            }
            else
            {
                Serial.println("Error Occurred. Error #: " + String(Update.getError()));
            }
        }
        else
        {
            Serial.println("Not enough space to begin OTA");
            client.flush();
        }
    }
    else
    {
        Serial.println("There was no content in the response");
        client.flush();
    }
}