/*=================================
  Openweathermapから温度取得
  制作日時:2020/08
  製作者:KK
  =================================*/

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

#include <SD.h>

#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

// For the Adafruit shield, these are the default.
#define TFT_DC 13
#define TFT_CS 5
// CS Pin of SD card
#define SDCS 17

// LED I/O port
#define LED 2

// LCD情報
#define LCD_X 240
#define LCD_Y 320

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// =====================================================
// 個別設定
// =====================================================
// Wifi ID or Pass
const char *ssid = "wifi IDを入力";
const char *password = "wifi パスワードを入力";

// OpenWeather API
// 3時間天気情報を取得する
// endpoint内のIDの部分を変更すると、取得先が変更できます
// ID:1856215(長野市)
const String endpoint = "http://api.openweathermap.org/data/2.5/forecast?id=1856215&units=metric&APPID=";
const String key = "各自のAPI-keyを入力";
// =====================================================
// =====================================================

typedef struct {
  int  year;
  byte month;
  byte day;
  byte hour;
  byte min;
  byte sec;
} date_time;

date_time tm;
void unix_time_to_date( unsigned long unix_datetime, date_time *tm );

/* 天気BMPパス情報 */
const String sunny  = "/img_bmp/1.BMP";
const String cloud_1  = "/img_bmp/2.BMP";
const String cloud_2  = "/img_bmp/3.BMP";
const String cloud_3  = "/img_bmp/4.BMP";
const String rain  = "/img_bmp/5.BMP";
const String sunny_night  = "/img_bmp/6.BMP";
const String cloud_night  = "/img_bmp/7.BMP";
const String thunderstorm  = "/img_bmp/8.BMP";
const String snow  = "/img_bmp/9.BMP";
const String mist  = "/img_bmp/10.BMP";


//----------------------------------------------------------------
// 日時 <-> 1970/1/1 00:00:00からの秒数(UNIXの日付) 相互変換
//----------------------------------------------------------------
#define SECONDS_IN_DAY   86400   // 24 * 60 * 60
#define SECONDS_IN_HOUR  3600    // 60 * 60

//----------------------------------------------------------------
// Unixの日付を日時に変換　ループ処理しているので2msec程度かかる
//----------------------------------------------------------------
void unix_time_to_date( unsigned long unix_datetime, date_time *tm )
{
  tm->year  = 1970;
  tm->month = 1;
  tm->day   = 1;
  tm->hour  = 0;
  tm->min   = 0;
  tm->sec   = 0;

  unsigned long yearindate;
  unsigned long seconds_year;
  unsigned long seconds_month;
  byte daysinmonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

  unix_datetime += 9 * SECONDS_IN_HOUR;
  while ( unix_datetime > 0 ) {
    if ( is_leapyear(tm->year) ) {
      yearindate = 366;
      daysinmonth[1] = 29;
    } else {
      yearindate = 365;
      daysinmonth[1] = 28;
    }
    seconds_year  = yearindate * SECONDS_IN_DAY;
    seconds_month = daysinmonth[tm->month - 1] * SECONDS_IN_DAY;
    if ( unix_datetime >= seconds_year ) {
      tm->year++;
      unix_datetime -= seconds_year;
    } else if ( unix_datetime >= seconds_month ) {
      tm->month++;
      unix_datetime -= seconds_month;
    } else if ( unix_datetime >= SECONDS_IN_DAY ) {
      tm->day++;
      unix_datetime -= SECONDS_IN_DAY;
    } else if ( unix_datetime >= SECONDS_IN_HOUR ) {
      tm->hour++;
      unix_datetime -= SECONDS_IN_HOUR;
    } else if ( unix_datetime >= 60 ) {
      tm->min++;
      unix_datetime -= 60;
    } else {
      tm->sec = (byte)unix_datetime;
      unix_datetime = 0;
    }
  }
}

//----------------------------------------------------------------
// 閏年か否かを返す
//----------------------------------------------------------------
boolean is_leapyear( int year )
{
  if ( (year % 400) == 0 || ((year % 4) == 0 && (year % 100) != 0)) {
    return true;
  } else {
    return false;
  }
}


/* ========================================== */
void bmp_draw(int x_grid, int y_grid, String p_file_name) {  //SDファイル読み込み
  File file = SD.open(p_file_name);

  signed int x = 0;
  signed int y = 0;
  unsigned char r = 0x00;
  unsigned char g = 0x00;
  unsigned char b = 0x00;
  unsigned long color = 0;
  signed int i = 0;

  // BMPサイズ情報格納
  unsigned long img_x = 0;
  unsigned long img_y = 0;

  // 画像の幅
  file.seek(15);
  for (i = 3; i >= 0; i--) {
    img_x |= (unsigned long)file.read() << (i * 8);
  }
  // 画像の高さ
  file.seek(19);
  for (i = 3; i >= 0; i--) {
    img_y |= (unsigned long)file.read() << (i * 8);
  }

  // 画像データ取得
  if (file) {
    file.seek(54);
    for (x = (img_y + x_grid - 1); x >= (x_grid); x--) {
      for (y = y_grid; y < (img_x + y_grid); y++) {
        b = file.read();
        g = file.read();
        r = file.read();

        color = (((unsigned long)(r >> 3) & 0x1F) << 11) | (((unsigned long)(g >> 2) & 0x3F) << 5) | (((unsigned long)(b >> 3) & 0x1F));

        tft.drawPixel(y, x, color);
      }
    }
  }
  file.close();
}


/* ========================================== */
void lcd_init() {
  // TFT LCD初期設定
  tft.begin();

  //  // read diagnostics (optional but can hel
  tft.setRotation(1);
  tft.fillScreen(ILI9341_WHITE);
  unsigned long start = micros();
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_BLACK);
  tft.setTextSize(2);
}


/* ========================================== */
void setup() {

  Serial.begin(115200);

  pinMode(LED, OUTPUT);

  delay(3000);

  // TFT LCD初期設定
  lcd_init();
  tft.print("TFT LCD Init OK!!");
  tft.print("\n");

  delay(3000);

  // WiFi初期設定
  WiFi.begin(ssid, password);

  tft.print("Connecting to WiFi..");
  while (WiFi.status() != WL_CONNECTED) {
    digitalWrite(LED, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(500);               // wait for a second
    digitalWrite(LED, LOW);    // turn the LED off by making the voltage LOW
    delay(500);               // wait for a second
    tft.print(".");
  }
  tft.print("\n");
  tft.print("Connected to the WiFi network\n");
  tft.print("\n");

  delay(3000);

  // SD初期設定
  tft.print("Initializing SD card...\n");
  if (!SD.begin(SDCS)) { // Initialization of SD card
    tft.print("initialization failed!\n");
    while (1) {
      digitalWrite(LED, HIGH);   // turn the LED on (HIGH is the voltage level)
      delay(1000);               // wait for a second
      digitalWrite(LED, LOW);    // turn the LED off by making the voltage LOW
      delay(1000);               // wait for a second
    }
  }
  tft.print("initialization done.\n");
  tft.print("\n");

  tft.print("LED Check\n");
  tft.print("\n");
  int led_cnt = 0;
  for (led_cnt = 0; led_cnt < 10; led_cnt++) {
    digitalWrite(LED, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(200);               // wait for a second
    digitalWrite(LED, LOW);    // turn the LED off by making the voltage LOW
    delay(200);               // wait for a second
  }

  tft.setRotation(1);
  tft.fillScreen(ILI9341_WHITE);
  tft.setCursor(0, 0);
}


/* ========================================== */
void loop() {

  // 画面クリア
  tft.fillScreen(ILI9341_WHITE);

  // Openweathermap から3時間ごとの気象情報取得
  if ((WiFi.status() == WL_CONNECTED)) { //Check the current connection status

    HTTPClient http;

    http.begin(endpoint + key); //Specify the URL
    int httpCode = http.GET();  //Make the request

    if (httpCode > 0) { //Check for the returning code

      // OpenWeatherよりJSON取得
      String payload = http.getString();

      // 型宣言
      const size_t capacity = 40 * JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(40) + 85 * JSON_OBJECT_SIZE(1) + 41 * JSON_OBJECT_SIZE(2) + 40 * JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(7) + 75 * JSON_OBJECT_SIZE(9) + 5 * JSON_OBJECT_SIZE(10) + 9660;
      DynamicJsonDocument doc(capacity);

      // Deserialize the JSON document
      deserializeJson(doc, payload);

      const char* cod = doc["cod"];
      int message = doc["message"];
      int cnt = doc["cnt"];

      JsonArray list = doc["list"];

      int i;
      for (i = 0; i < 7; i++) {
        //JSONからデータ取得
        JsonObject list_day = list[i];
        long list_dt = list_day["dt"];

        JsonObject list_main = list_day["main"];
        float list_main_temp = list_main["temp"];

        JsonObject list_weather = list_day["weather"][0];
        const char* list_weather_icon = list_weather["icon"];
        float list_rain_3h = list_day["rain"]["3h"];

        JsonObject city = doc["city"];
        const char* city_name = city["name"];

        // UNIX時間変換
        unix_time_to_date( list_dt, &tm );

        // 各種変数宣言
        String s = list_weather_icon;
        int x_txt = ((int)((i + 1) / 4) * 122);
        int y_txt = ((i + 1) % 4) * 80;
        int x_bmp = x_txt + 45;
        int y_bmp = y_txt + 1;

        // 左上情報bmp表示
        bmp_draw(0, 0, "/img_bmp/info.BMP");

        /* 昼アイコン */
        if (s.indexOf("d") >= 0) {
          if (s.equals("01d")) {
            bmp_draw(x_bmp, y_bmp, sunny);
          }
          else if (s.equals("02d") || s.equals("04d")) {
            bmp_draw(x_bmp, y_bmp, cloud_1);
          }
          else if (s.equals("03d")) {
            bmp_draw(x_bmp, y_bmp, cloud_2);
          }
          else if (s.equals("09d")) {
            bmp_draw(x_bmp, y_bmp, cloud_3);
          }
          else if (s.equals("10d")) {
            bmp_draw(x_bmp, y_bmp, rain);
          }
          else if (s.equals("11d")) {
            bmp_draw(x_bmp, y_bmp, thunderstorm);
          }
          else if (s.equals("13d")) {
            bmp_draw(x_bmp, y_bmp, snow);
          }
          else if (s.equals("50d")) {
            bmp_draw(x_bmp, y_bmp, mist);
          }

          /* 夜アイコン */
        } else {
          if (s.equals("01n")) {
            bmp_draw(x_bmp, y_bmp, sunny_night);
          }
          else if (s.equals("02n") || s.equals("04n")) {
            bmp_draw(x_bmp, y_bmp, cloud_night);
          }
          else if (s.equals("03n")) {
            bmp_draw(x_bmp, y_bmp, cloud_2);
          }
          else if (s.equals("09n")) {
            bmp_draw(x_bmp, y_bmp, cloud_3);
          }
          else if (s.equals("09n") || s.equals("10n")) {
            bmp_draw(x_bmp, y_bmp, rain);
          }
          else if (s.equals("11n")) {
            bmp_draw(x_bmp, y_bmp, thunderstorm);
          }
          else if (s.equals("13n")) {
            bmp_draw(x_bmp, y_bmp, snow);
          }
          else if (s.equals("50n")) {
            bmp_draw(x_bmp, y_bmp, mist);
          }
        }

        // 現在日付・都市名表示
        if (i == 0) {
          tft.setTextColor(ILI9341_BLACK);
          tft.setCursor(1 , 52);
          tft.print(tm.year);
          tft.setCursor(1 , 68);
          if (tm.month < 10) {
            tft.print("0");
          }
          tft.print(tm.month);
          tft.print("/");
          if (tm.day < 10) {
            tft.print("0");
          }
          tft.print(tm.day);

          tft.setTextColor(ILI9341_GREEN);
          tft.setCursor(1 , 84);
          tft.print(city_name);
          tft.setCursor(1 , 100);
          tft.print("City");
        }

        // 時刻表示
        tft.setTextColor(ILI9341_BLACK);
        tft.setCursor(1 + y_txt, 1 + x_txt);
        if (tm.hour < 10) {
          tft.print("0");
        }
        tft.print(tm.hour);
        tft.print(":");
        if (tm.min < 10) {
          tft.print("0");
        }
        tft.print(tm.min);

        // 温度表示
        tft.setTextColor(ILI9341_RED);
        tft.setCursor(1 + y_txt, 17 + x_txt);
        tft.print(list_main_temp);

        // 降水量表示
        tft.setTextColor(ILI9341_BLUE);
        tft.setCursor(1 + y_txt, 33 + x_txt);
        tft.print(list_rain_3h);
      }
    }

    http.end(); //Free the resources
  }

  delay(600000);

}
