#include <ArduinoJson.h>
#include <esp_log.h>

#define CONFIG_Serial.printfEFAULT_LEVEL_VERBOSE  (1)
#define CONFIG_Serial.printfEFAULT_LEVEL          (5)
#define CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE  (1)
#define CONFIG_LOG_MAXIMUM_LEVEL          (5)
#define CONFIG_LOG_COLORS                 (1)
#define CONFIG_LOG_TIMESTAMP_SOURCE_RTOS  (1) 

#define JSON_KW_STATUS                          "status"
#define JSON_KW_MESSAGE                         "message"
#define JSON_KW_DATA                            "data"
#define JSON_KW_WORD                            "word"
#define JSON_KW_WORDS                           "words"
#define JSON_KW_ELEMENTS                        "elements"
#define JSON_KW_POWERUP_WORDS                   "power_up_words"
#define JSON_KW_TARGET_DISTANCE                 "target_distance"

typedef enum {
  API_OK = (0),
  API_WIFI_DISCONNECTED,
  API_HOST_CONNECT_FAILED,
  API_HEADER_TIMEOUT,
  API_RESPONSE_TIMEOUT,
  API_RESPONSE_PARSE_ERROR,
  API_RESPONSE_STATUS_ERROR,
} ApiReturnType_e;

void LocalPrintApiError(ApiReturnType_e ret);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello, ESP32-S3!");
  esp_log_level_set("", ESP_LOG_VERBOSE);

  ApiReturnType_e ret = API_OK;
  String response = "{\"status\":200,\"message\":\"Lấy dữ liệu game word race thành công\",\"data\":{\"words\":[{\"word\":\"musical\",\"elements\":[\"mu\",\"cal\",\"si\"]},{\"word\":\"push\",\"elements\":[\"p\",\"sh\",\"u\"]},{\"word\":\"net\",\"elements\":[\"n\",\"e\",\"t\"]},{\"word\":\"method\",\"elements\":[\"od\",\"th\",\"me\"]},{\"word\":\"toxic\",\"elements\":[\"t\",\"o\",\"xic\"]},{\"word\":\"twin\",\"elements\":[\"w\",\"t\",\"in\"]},{\"word\":\"tedious\",\"elements\":[\"te\",\"ous\",\"di\"]},{\"word\":\"avoid\",\"elements\":[\"a\",\"oid\",\"v\"]},{\"word\":\"raise\",\"elements\":[\"r\",\"a\",\"ise\"]},{\"word\":\"angle\",\"elements\":[\"n\",\"a\",\"gle\"]},{\"word\":\"belt\",\"elements\":[\"e\",\"b\",\"lt\"]},{\"word\":\"arrival\",\"elements\":[\"val\",\"ri\",\"ar\"]},{\"word\":\"drama\",\"elements\":[\"d\",\"ama\",\"r\"]},{\"word\":\"period\",\"elements\":[\"ri\",\"od\",\"pe\"]},{\"word\":\"deserve\",\"elements\":[\"de\",\"rve\",\"se\"]},{\"word\":\"size\",\"elements\":[\"s\",\"i\",\"ze\"]},{\"word\":\"royal\",\"elements\":[\"yal\",\"r\",\"o\"]},{\"word\":\"remind\",\"elements\":[\"re\",\"mi\",\"nd\"]},{\"word\":\"raw\",\"elements\":[\"r\",\"a\",\"w\"]},{\"word\":\"sour\",\"elements\":[\"o\",\"ur\",\"s\"]},{\"word\":\"shave\",\"elements\":[\"ave\",\"s\",\"h\"]},{\"word\":\"style\",\"elements\":[\"yle\",\"t\",\"s\"]},{\"word\":\"terrible\",\"elements\":[\"rr\",\"te\",\"ible\"]},{\"word\":\"manual\",\"elements\":[\"nu\",\"al\",\"ma\"]},{\"word\":\"medium\",\"elements\":[\"me\",\"di\",\"um\"]},{\"word\":\"note\",\"elements\":[\"te\",\"o\",\"n\"]},{\"word\":\"traffic\",\"elements\":[\"fic\",\"af\",\"tr\"]},{\"word\":\"proud\",\"elements\":[\"r\",\"p\",\"oud\"]},{\"word\":\"theater\",\"elements\":[\"ea\",\"th\",\"ter\"]},{\"word\":\"spell\",\"elements\":[\"s\",\"p\",\"ell\"]},{\"word\":\"catch\",\"elements\":[\"a\",\"c\",\"tch\"]},{\"word\":\"stir\",\"elements\":[\"t\",\"s\",\"ir\"]},{\"word\":\"dirty\",\"elements\":[\"rty\",\"d\",\"i\"]},{\"word\":\"assume\",\"elements\":[\"as\",\"su\",\"me\"]},{\"word\":\"brain\",\"elements\":[\"b\",\"ain\",\"r\"]},{\"word\":\"switch\",\"elements\":[\"it\",\"sw\",\"ch\"]},{\"word\":\"seat\",\"elements\":[\"e\",\"at\",\"s\"]},{\"word\":\"port\",\"elements\":[\"o\",\"rt\",\"p\"]},{\"word\":\"doubt\",\"elements\":[\"o\",\"d\",\"ubt\"]},{\"word\":\"mouse\",\"elements\":[\"m\",\"o\",\"use\"]}],\"power_up_words\":[{\"word\":\"ceremony\",\"elements\":[\"mony\",\"ce\",\"re\"]},{\"word\":\"tranquil\",\"elements\":[\"quil\",\"tr\",\"an\"]},{\"word\":\"spring\",\"elements\":[\"ng\",\"ri\",\"sp\"]},{\"word\":\"represent\",\"elements\":[\"rep\",\"ent\",\"res\"]},{\"word\":\"shampoo\",\"elements\":[\"am\",\"sh\",\"poo\"]},{\"word\":\"gorgeous\",\"elements\":[\"rg\",\"go\",\"eous\"]},{\"word\":\"massage\",\"elements\":[\"age\",\"ss\",\"ma\"]},{\"word\":\"frequent\",\"elements\":[\"eq\",\"uent\",\"fr\"]},{\"word\":\"museum\",\"elements\":[\"se\",\"mu\",\"um\"]},{\"word\":\"firework\",\"elements\":[\"work\",\"fi\",\"re\"]}],\"target_distance\":10}}";

  JsonDocument doc;
  uint16_t words_num = 0;
  uint16_t el_num = 0;
  DeserializationError error = deserializeJson(doc, response);

  if (DeserializationError::Ok == error)
  {
    /* Validate reponse */
    JsonVariant statusCodeVariant = doc[JSON_KW_STATUS];
    JsonVariant messageVariant = doc[JSON_KW_MESSAGE];
    JsonVariant dataVariant = doc[JSON_KW_DATA];
    if (! statusCodeVariant.isNull() && ! messageVariant.isNull() && ! dataVariant.isNull())
    {
      uint16_t statusCode = statusCodeVariant.as<uint16_t>();

      if (200 == statusCode)
      {
        JsonDocument data_doc = dataVariant;

        /* Target distance */
        JsonVariant targetVariant = data_doc[JSON_KW_TARGET_DISTANCE];
        JsonVariant wordsVariant = data_doc[JSON_KW_WORDS];
        JsonVariant powerupVariant = data_doc[JSON_KW_POWERUP_WORDS];
        if (! targetVariant.isNull() && ! wordsVariant.isNull() && ! powerupVariant.isNull())
        {
          Serial.printf("Target distance: %d\n", data_doc[JSON_KW_TARGET_DISTANCE].as<int>());

          for (byte i = 0; i < 2; i++)
          {
            JsonArray word_array = ((i == 0)? wordsVariant.as<JsonArray>() : powerupVariant.as<JsonArray>());

            for(JsonVariant v : word_array) {
              JsonVariant wordVariant = v[JSON_KW_WORD];
              JsonVariant elementvariant = v[JSON_KW_ELEMENTS];
              if ( ! wordVariant.isNull() && ! elementvariant.isNull()) {
                Serial.printf("\n%s: ", wordVariant.as<String>().c_str());

                JsonArray char_array = elementvariant.as<JsonArray>();
                el_num = 0;
                for (JsonVariant chars : char_array) {
                  Serial.printf("  %s", chars.as<String>().c_str());
                  el_num++;
                }

                if (el_num == 3) {
                  words_num++;
                } else {
                  Serial.printf("  - Current word is invalid! Number of elements: %d", el_num);
                }
              } else {
                continue;
              }
            }
          }
        } else { ret = API_RESPONSE_PARSE_ERROR; }
      } else {
        Serial.printf("Response status code error (%d)", statusCode);
        ret = API_RESPONSE_STATUS_ERROR;
      }
    } else { ret = API_RESPONSE_PARSE_ERROR; }
  } else { ret = API_RESPONSE_PARSE_ERROR; }

  LocalPrintApiError(ret);
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(10); // this speeds up the simulation
}

void LocalPrintApiError(ApiReturnType_e ret)
{
  esp_log_level_set("", ESP_LOG_VERBOSE);
  ESP_LOGE("", "This is a error log.");
  switch (ret)
  {
    case API_OK: break;
    case API_WIFI_DISCONNECTED:
      Serial.printf("API_WIFI_DISCONNECTED"); break;
    case API_HOST_CONNECT_FAILED:
      Serial.printf("API_HOST_CONNECT_FAILED"); break;
    case API_HEADER_TIMEOUT:
      Serial.printf("API_HEADER_TIMEOUT"); break;
    case API_RESPONSE_TIMEOUT:
      Serial.printf("API_RESPONSE_TIMEOUT"); break;
    case API_RESPONSE_PARSE_ERROR:
      Serial.printf("API_RESPONSE_PARSE_ERROR"); break;
    case API_RESPONSE_STATUS_ERROR:
      Serial.printf("API_RESPONSE_STATUS_ERROR"); break;
    default: break;
  }
}