
#include "SH1106.h"
#include "OLEDDisplayUi.h"
#include <WiFi.h>
#include <WiFiUdp.h>
#include <time.h>
#include <Wire.h>
#include <Adafruit_BME280.h>

#define icon_width  40
#define icon_height 40

const uint8_t rain_icon[] PROGMEM = {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x81, 0xFF, 0xFF, 0xFF, 0x3F, 0x04, 0xFE, 0xFF, 0xFF, 0xDF, 0xF0, 0xFC,
  0xFF, 0xFF, 0xE7, 0xFF, 0xFB, 0xFF, 0xFF, 0xFB, 0xFF, 0xF3, 0xFF, 0xFF,
  0xFD, 0xFF, 0xE7, 0xFF, 0xFF, 0xFD, 0xFF, 0x0F, 0xFE, 0x0F, 0xF8, 0xFF,
  0x8F, 0xFC, 0xE7, 0xFB, 0xFF, 0xC7, 0xF9, 0xF7, 0xE3, 0xFF, 0xC3, 0xF3,
  0xF3, 0xDF, 0xFF, 0xE8, 0xE7, 0xF9, 0xFF, 0xFF, 0xFE, 0xEF, 0xFD, 0xFF,
  0xFF, 0xFF, 0xE7, 0xF9, 0xFF, 0xFF, 0xFF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF,
  0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xF9, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF,
  0xFF, 0xFF, 0x3F, 0xFE, 0x3F, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x7D, 0xBF,
  0xEF, 0xFF, 0xFF, 0xBE, 0xDF, 0xF7, 0xFF, 0x7F, 0xDF, 0xEF, 0xFB, 0xFF,
  0xBF, 0xEF, 0xF7, 0xFD, 0xFF, 0xDF, 0xF7, 0xFB, 0xFE, 0xFF, 0xEF, 0xFB,
  0x7D, 0xFF, 0xFF, 0xF7, 0xFD, 0xBE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

const uint8_t sunny_icon[] PROGMEM = {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF,
  0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF,
  0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFD, 0xDF, 0xFF, 0xE3, 0xFF, 0xF8, 0x8F,
  0xFF, 0xE3, 0x7F, 0xF0, 0x07, 0xFF, 0xE3, 0x3F, 0xF8, 0x0F, 0xFE, 0xFF,
  0x1F, 0xFC, 0x1F, 0x7C, 0x00, 0x0E, 0xFE, 0x3F, 0x18, 0x00, 0x1C, 0xFF,
  0x7F, 0xCC, 0xFF, 0xB1, 0xFF, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xFF, 0xF3,
  0xFF, 0xCF, 0xFF, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0xFF, 0xF9, 0xFF, 0x9F,
  0xFF, 0xFF, 0xF9, 0xFF, 0x9F, 0xFF, 0xFF, 0xF9, 0xFF, 0x9F, 0xFF, 0x01,
  0xF9, 0xFF, 0x9F, 0x80, 0x01, 0xF9, 0xFF, 0x9F, 0x80, 0x01, 0xF9, 0xFF,
  0x9F, 0x80, 0xFF, 0xF9, 0xFF, 0x9F, 0xFF, 0xFF, 0xF1, 0xFF, 0x8F, 0xFF,
  0xFF, 0xF1, 0xFF, 0x8F, 0xFF, 0xFF, 0xE3, 0xFF, 0xE7, 0xFF, 0xFF, 0xC7,
  0xFF, 0xF3, 0xFF, 0xFF, 0x8D, 0xFF, 0xD8, 0xFF, 0xFF, 0x38, 0x00, 0x8C,
  0xFF, 0x7F, 0x70, 0x00, 0x07, 0xFF, 0x3F, 0xF8, 0xFF, 0x0F, 0xFE, 0x1F,
  0xFC, 0xE3, 0x1F, 0xFC, 0x0F, 0xFE, 0xE3, 0x3F, 0xF8, 0x07, 0xFF, 0xE3,
  0x7F, 0xF0, 0x8F, 0xFF, 0xE3, 0xFF, 0xF8, 0xDF, 0xFF, 0xE3, 0xFF, 0xFD,
  0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF,
  0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

const uint8_t mostlysunny_icon[] PROGMEM = {
  0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFD, 0x7E,
  0xFF, 0xFF, 0xFF, 0xFB, 0xBF, 0xEF, 0xFF, 0xFF, 0x17, 0xE0, 0xF7, 0xFF,
  0xFF, 0xCF, 0x9F, 0xF9, 0xFF, 0xFF, 0xE6, 0x3F, 0xFD, 0xFF, 0xFF, 0xF5,
  0x7F, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFE, 0xFF, 0xFF, 0xF9, 0xFF, 0x00,
  0xFF, 0xFF, 0xFD, 0x7F, 0x08, 0xFC, 0xFF, 0xFD, 0xBF, 0xE1, 0xF9, 0xFF,
  0xFD, 0xCF, 0xFF, 0xF7, 0xFF, 0xFD, 0xF7, 0xFF, 0xE7, 0xFF, 0xF9, 0xFB,
  0xFF, 0xCF, 0xFF, 0xF3, 0xFB, 0xFF, 0x1F, 0xFC, 0x17, 0xF0, 0xFF, 0x1F,
  0xF9, 0xC7, 0xF7, 0xFF, 0x8F, 0xF3, 0xEF, 0xC7, 0xFF, 0x87, 0xE7, 0xE7,
  0xBF, 0xFF, 0xD1, 0xCF, 0xF3, 0xFF, 0xFF, 0xFD, 0xDF, 0xFB, 0xFF, 0xFF,
  0xFF, 0xCF, 0xF3, 0xFF, 0xFF, 0xFF, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xE7,
  0xF7, 0xFF, 0xFF, 0xFF, 0xF3, 0xEF, 0xFF, 0xFF, 0xFF, 0xF9, 0x9F, 0xFF,
  0xFF, 0x7F, 0xFC, 0x7F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

const uint8_t cloudy_icon[] PROGMEM = {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0x7F, 0x78, 0xFC, 0xFF,
  0xFF, 0xBF, 0xFF, 0xF9, 0xFF, 0xFF, 0xCF, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7,
  0xFF, 0xE7, 0xFF, 0xFF, 0xFB, 0xFF, 0xCF, 0xFF, 0xFF, 0xFB, 0xFF, 0x1F,
  0xFC, 0x3F, 0xF0, 0xFF, 0xE7, 0xFB, 0xCF, 0xF7, 0xFF, 0xF3, 0xF7, 0xEF,
  0xCF, 0xFF, 0xF9, 0xEF, 0xF7, 0xBF, 0xFF, 0xFD, 0xCF, 0xF3, 0xFF, 0xFF,
  0xFD, 0xDF, 0xFB, 0xFF, 0xFF, 0xFF, 0xDF, 0xFB, 0xFF, 0xFF, 0xFF, 0xEF,
  0xF7, 0xFF, 0xFF, 0xFF, 0xE7, 0xF7, 0xFF, 0xFF, 0xFF, 0xF3, 0xEF, 0xFF,
  0xFF, 0xFF, 0xF9, 0x9F, 0xFF, 0xFF, 0x7F, 0xFC, 0x7F, 0x00, 0x00, 0x00,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

const uint8_t tstorms_icon[] PROGMEM = {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x81, 0xFF, 0xFF, 0xFF, 0x3F, 0x04, 0xFE, 0xFF, 0xFF, 0xDF, 0xF0, 0xFC,
  0xFF, 0xFF, 0xE7, 0xFF, 0xFB, 0xFF, 0xFF, 0xFB, 0xFF, 0xF3, 0xFF, 0xFF,
  0xFD, 0xFF, 0xE7, 0xFF, 0xFF, 0xFD, 0xFF, 0x0F, 0xFE, 0x0F, 0xF8, 0xFF,
  0x8F, 0xFC, 0xE7, 0xFB, 0xFF, 0xC7, 0xF9, 0xF7, 0xE3, 0xFF, 0xC3, 0xF3,
  0xF3, 0xDF, 0xFF, 0xE8, 0xE7, 0xF9, 0xFF, 0xFF, 0xFE, 0xEF, 0xFD, 0xFF,
  0xFF, 0xFF, 0xE7, 0xF9, 0xFF, 0xFF, 0xFF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF,
  0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xF9, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF,
  0xFF, 0xFF, 0x3F, 0xFE, 0x3F, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x7F, 0x7F,
  0xFF, 0xFF, 0xFF, 0xBF, 0xBF, 0xFF, 0xFF, 0xFF, 0xDF, 0x8F, 0xFF, 0xFF,
  0xFF, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 0x4F,
  0xF7, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xFF, 0xFF, 0xDF, 0xF1, 0xFF,
  0xFF, 0xFF, 0xDF, 0xFE, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
  0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};

String      time_str, weather_text, weather_extra_text;
int         last_reading_hour, reading_hour, hr_cnt;

const char* ssid     = "aBcAdfh"; //inserire nome rete WiFi
const char* password = "wyZ23tX"; //inserire password rete WiFi

enum image_names {
  rain_img, sunny_img, mostlysunny_img, cloudy_img, tstorms_img,
} image;

const char* NTP_SERVER = "europe.pool.ntp.org";
const char* TZ_INFO    = "CET-1 CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00";

enum weather_type {unknown     =  4,
                   sunny       =  2,
                   mostlysunny =  1,
                   cloudy      =  0,
                   rain        = -1,
                   tstorms     = -2
                  };

enum weather_description {GoodClearWeather, BecomingClearer,
                          NoChange, ClearSpells, ClearingWithin12hrs, ClearingAndColder,
                          GettingWarmer, WarmerIn2daysRainLikely,
                          ExpectRain, WarmerRainWithin36hrs, RainIn18hrs, RainHighWindsClearAndCool,
                          GalesHeavyRainSnowInWinter
                         };

weather_type current_wx;

typedef struct {
  float pressure;             
  float temperature;          
  float humidity;             
  weather_type wx_state_1hr;  
  weather_type wx_state_3hr;  
} wx_record_type;

wx_record_type reading[24];  

int wx_average_1hr, wx_average_3hr;  

bool look_3hr = true;
bool look_1hr = false;

#define SDA 5
#define SCL 4

SH1106 display(0x3c, 5, 4);  
OLEDDisplayUi ui     ( &display );

Adafruit_BME280 bme;
 
WiFiClient client;  

#define pressure_offset 134.6  
#define temperature_offset 6.4
#define humidity_offset 7.5
 
void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
  display->setTextAlignment(TEXT_ALIGN_LEFT);
  display->drawString(0, 0, time_str.substring(0, 8));  
  display->setTextAlignment(TEXT_ALIGN_RIGHT);
  display->drawString(128, 0, time_str.substring(9));
  display->setTextAlignment(TEXT_ALIGN_LEFT);
}

void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  float trend  = reading[23].pressure - reading[20].pressure;                           
  ForecastToImgTxt(get_forecast_text(reading[23].pressure, trend, look_3hr));           
  if (image == rain_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, rain_icon);           
  if (image == sunny_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, sunny_icon);         
  if (image == mostlysunny_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, mostlysunny_icon);  
  if (image == cloudy_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, cloudy_icon);       
  if (image == tstorms_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, tstorms_icon);     
  display->drawStringMaxWidth(x + 43, y + 14, 90, String(reading[23].pressure, 1) + " hPa");  
  display->drawStringMaxWidth(x + 43, y + 27, 90, String(trend, 1) + " " + get_trend_text(trend));  
}

void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  float  trend = reading[23].pressure - reading[20].pressure;                              
  weather_description wx_text = get_forecast_text(reading[23].pressure, trend, look_3hr);  
  ForecastToImgTxt(wx_text);                                                               
  display->setFont(ArialMT_Plain_10);
  display->drawStringMaxWidth(x + 1, y + 15, 127, weather_text + weather_extra_text);
  display->setFont(ArialMT_Plain_10);
}

void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  int gwidth   = 75;  
  int gscale   = 30;  
  int num_bars = 8;   
#define yscale 8    
  float bar_width = gwidth / (num_bars + 1);  
  x = 30; // Sets position of graph on screen
  y = 15; // Sets position of graph on screen
  display->drawVerticalLine(x, y, gscale + 1);
  display->drawString(x - 18, y - 6, ">+" + String(yscale));
  display->drawString(x - 8, y + gscale / 2 - 6, "0");
  display->drawString(x - 15, y + gscale - 6, "<-" + String(yscale));
  display->drawString(x - 30, y + gscale / 2 - 6, String(hr_cnt % 24));
  display->drawString(x + 2 + (bar_width + 3) * 0, y + gscale, "-24");  
  display->drawString(x + 2 + (bar_width + 3) * 2, y + gscale, "-12");  
  display->drawString(x + 2 + (bar_width + 3) * 5, y + gscale, "-2");  
  display->drawString(x + 2 + (bar_width + 3) * 7, y + gscale, "0");  
  int display_points [8] = {0, 5, 11, 17, 20, 21, 22, 23};  
  float value;
  for (int bar_num = 0; bar_num < num_bars; bar_num++) {      
    value = map(reading[display_points[bar_num]].pressure, reading[23].pressure - yscale, reading[23].pressure + yscale, gscale, 0);
    if (value > gscale) value = gscale;                       
    if (value < 0     ) value = 0;                            
    display->drawHorizontalLine(x + bar_num * (bar_width + 3) + 2, y + value, bar_width);
    for (int yplus = gscale; yplus > value; yplus = yplus - 1) {
      display->drawHorizontalLine(x + bar_num * (bar_width + 3) + 2, y + yplus, bar_width);
    }
  }
}

void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  reading[23].pressure = (reading[23].pressure + read_pressure()) / 2;                
  float  trend = reading[23].pressure - reading[22].pressure;                         
  weather_description wx_text = get_forecast_text(read_pressure(), trend, look_1hr);  
  ForecastToImgTxt(wx_text);
  if (image == rain_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, rain_icon);           
  if (image == sunny_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, sunny_icon);         
  if (image == mostlysunny_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, mostlysunny_icon);  
  if (image == cloudy_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, cloudy_icon);       
  if (image == tstorms_img) display->drawXbm(x + 0, y + 15, icon_width, icon_height, tstorms_icon);     
  display->drawStringMaxWidth(x + 43, y + 13, 90, "PREVISIONI 1 Hr.");
  display->drawStringMaxWidth(x + 43, y + 26, 90, String(read_pressure(), 1) + " hPa");
  display->drawStringMaxWidth(x + 43, y + 38, 90, String(trend, 1) + " " + get_trend_text(trend));
}

void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  reading[23].pressure = (reading[23].pressure + read_pressure()) / 2;                
  float  trend = reading[23].pressure - reading[22].pressure;                         
  weather_description wx_text = get_forecast_text(read_pressure(), trend, look_1hr);  
  ForecastToImgTxt(wx_text);
  display->drawString(x + 9, y + 14, "PREVISIONI A BREVE");
  display->setFont(ArialMT_Plain_10);
  display->drawStringMaxWidth(x + 4, y + 27, 127, weather_text + weather_extra_text);
  display->setFont(ArialMT_Plain_10);
}
 
void drawFrame6(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  display->drawString(x + 25, y + 15, "TEMPERATURA");
  display->setFont(ArialMT_Plain_16);
  display->drawStringMaxWidth(x + 34, y + 33, 97, String(read_temperature()) + (" *C"));
  display->setFont(ArialMT_Plain_10);
}
void drawFrame7(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  display->drawString(x + 41, y + 15, "UMIDITA'");
  display->setFont(ArialMT_Plain_16);
  display->drawStringMaxWidth(x + 39, y + 33, 97, String(read_humidity()) + ("%"));
  display->setFont(ArialMT_Plain_10);
}
float read_pressure() {
  int reading = (bme.readPressure() / 100.0F + pressure_offset) * 10;  
  return (float)reading / 10;
}
float read_temperature() {
  int reading = (bme.readTemperature() + temperature_offset) * 100;
  return (float)reading / 140;
}
float read_humidity() {
  int reading = (bme.readHumidity() + humidity_offset) * 100;
  return (float)reading / 130;
}
// Convert pressure trend to text
String get_trend_text(float trend) {
  String trend_str = "Stabile";  
  if (trend > 3.5)                          {
    trend_str = "Aumento Veloce";
  }
  else if (trend >   1.5  && trend <= 3.5)  {
    trend_str = "In Aumento";
  }
  else if (trend >   0.25 && trend <= 1.5)  {
    trend_str = "Aumento Lento";
  }
  else if (trend >  -0.25 && trend <  0.25) {
    trend_str = "Stabile";
  }
  else if (trend >= -1.5  && trend < -0.25) {
    trend_str = "Calo Lento";
  }
  else if (trend >= -3.5  && trend < -1.5)  {
    trend_str = "In Calo";
  }
  else if (trend <= -3.5)                   {
    trend_str = "Calo Veloce";
  }
  return trend_str;
}

void ForecastToImgTxt(weather_description wx_text) {
  if      (wx_text == GoodClearWeather)           {
    image = sunny_img;
    current_wx = sunny;
    weather_text = " Bel Tempo Sereno";
  }
  else if (wx_text == BecomingClearer)            {
    image = mostlysunny_img;
    current_wx = mostlysunny;
    weather_text = "Tendenza a Schiarire";
  }
  else if (wx_text == NoChange)                   {
    image = cloudy_img;
    current_wx = cloudy;
    weather_text = "Non Cambia, Schiarisce";
  }
  else if (wx_text == ClearSpells)                {
    image = mostlysunny_img;
    current_wx = mostlysunny;
    weather_text = "Sereno Gradevole";
  }
  else if (wx_text == ClearingWithin12hrs)        {
    image = mostlysunny_img;
    current_wx = mostlysunny;
    weather_text = "Schiarisce in 12H";
  }
  else if (wx_text == ClearingAndColder)          {
    image = mostlysunny_img;
    current_wx = mostlysunny;
    weather_text = "Schiarisce e Più Freddo";
  }
  else if (wx_text == GettingWarmer)              {
    image = mostlysunny_img;
    current_wx = mostlysunny;
    weather_text = "Più Caldo";
  }
  else if (wx_text == WarmerIn2daysRainLikely)    {
    image = rain_img;
    current_wx = rain;
    weather_text = "Più Caldo in 2 Giorni, Probabile Pioggia";
  }
  else if (wx_text == ExpectRain)                 {
    image = rain_img;
    current_wx = rain;
    weather_text = "Attesa Pioggia";
  }
  else if (wx_text == WarmerRainWithin36hrs)      {
    image = rain_img;
    current_wx = rain;
    weather_text = "Più Caldo, Pioggia in 36H";
  }
  else if (wx_text == RainIn18hrs)                {
    image = rain_img;
    current_wx = rain;
    weather_text = "Pioggia in 18H";
  }
  else if (wx_text == RainHighWindsClearAndCool)  {
    image = rain_img;
    current_wx = rain;
    weather_text = "Pioggia, Forti Venti, Sereno e Freddo";
  }
  else if (wx_text == GalesHeavyRainSnowInWinter) {
    image = tstorms_img;
    current_wx = tstorms;
    weather_text = "Burrasche, Forti Piogge, in Inverno Neve";
  }
}

weather_description get_forecast_text(float pressure_now, float trend, bool range) {
  String trend_str = get_trend_text(trend);
  weather_description wx_text = NoChange;  
  weather_extra_text = "";
  image = cloudy_img;  
  if (pressure_now >= 1022.68 )                                                           {
    wx_text = GoodClearWeather;
  }
  if (pressure_now >= 1022.7  && trend_str  == "Calo Veloce")                            {
    wx_text = WarmerRainWithin36hrs;
  }

  if (pressure_now >= 1013.2  && pressure_now <= 1022.68 &&
      (trend_str == "Stabile" || trend_str == "Aumento Lento"))                               {
    wx_text = NoChange;
    (range ? wx_history_3hr() : wx_history_1hr());
  }
  if (pressure_now >= 1013.2 && pressure_now <= 1022.68 &&
      (trend_str == "In Aumento" || trend_str == "Aumento Veloce"))                               {
    wx_text = GettingWarmer;
  }
  if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && trend_str == "Aumento Lento")    {
    wx_text = BecomingClearer;
  }

  if (pressure_now >= 1013.2  && pressure_now <= 1022.68 &&
      (trend_str == "In Calo Lento" || trend_str == "Calo Veloce")) {
    wx_text = ExpectRain;
  }
  if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && trend_str  == "Stabile")       {
    wx_text = ClearSpells;
    (range ? wx_history_3hr() : wx_history_1hr());
  };
  if (pressure_now <= 1013.2  && (trend_str == "In Calo Lento" || trend_str == "In Calo")) {
    wx_text = RainIn18hrs;
  }
  if (pressure_now >= 1013.2  && pressure_now <= 1022.68 && trend_str == "Calo Veloce")  {
    wx_text = ExpectRain;
  }
  if (pressure_now <= 1013.2  && trend_str == "Calo Veloce")                             {
    wx_text = RainHighWindsClearAndCool;
  }
  if (pressure_now <= 1009.14 && trend_str == "Calo Veloce")                             {
    wx_text = GalesHeavyRainSnowInWinter;
  }
  if (pressure_now <= 1013.2  &&
      (trend_str == "In Aumento" || trend_str == "Aumento Lento" || trend_str == "Aumento Veloce"))   {
    wx_text = ClearingWithin12hrs;
  }
  if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && trend_str  == "Stabile")        {
    wx_text = ClearSpells;
    (range ? wx_history_3hr() : wx_history_1hr());
  };

  if (pressure_now <= 1009.14 && trend_str  == "Aumento Veloce")                             {
    wx_text = ClearingAndColder;
  }
  return wx_text;
}

void wx_history_1hr() {
  if      (wx_average_1hr >  0) weather_extra_text = ", Attesa Sole";
  else if (wx_average_1hr == 0) weather_extra_text = ", Tendenza Nuvoloso";
  else if (wx_average_1hr <  0) weather_extra_text = ", Attesa Pioggia";
  else weather_extra_text = "";
}
 
void wx_history_3hr() {
  if      (wx_average_3hr >  0) weather_extra_text = ", Attesa Sole";
  else if (wx_average_3hr == 0) weather_extra_text = ",Tendenza Nuvoloso";
  else if (wx_average_3hr <  0) weather_extra_text = ", Attesa Pioggia";
  else weather_extra_text = "";
}

FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5, drawFrame6, drawFrame7};

int frameCount = 7;

OverlayCallback overlays[] = { msOverlay };
int overlaysCount = 1;

void setup() {
  float p, t;
  Serial.begin(115200);
  Wire.begin(5, 4);
  if (!StartWiFi(ssid, password)) Serial.println("Failed to start WiFi Service after 20 attempts");;  
  configTime(0, 0, NTP_SERVER);  
  setenv("TZ", TZ_INFO, 1);    
  time_t now = time(nullptr);
  delay(2000);  
  if (!bme.begin()) {
    Serial.println("Could not find a sensor, check wiring!");
  }
  else
  {
    Serial.println("Found a sensor continuing");
    while (isnan(bme.readPressure())) {
      Serial.println(bme.readPressure());
    }
  }
  bme.setSampling(Adafruit_BME280::MODE_NORMAL,     
                  Adafruit_BME280::SAMPLING_X16,       
                  Adafruit_BME280::SAMPLING_X16);     
  (Adafruit_BME280::FILTER_X16);        

  while (!update_time());   
  for (int i = 0; i <= 23; i++) {  
    reading[i].pressure     = read_pressure();        
    reading[i].temperature  = bme.readTemperature();  
    reading[i].humidity     = bme.readHumidity();     
    reading[i].wx_state_1hr = unknown;                
    reading[i].wx_state_3hr = unknown;                
  }                                                   
  last_reading_hour = reading_hour;
  wx_average_1hr = 0;  
  wx_average_3hr = 0;  

  ui.setTargetFPS(3);
  ui.setIndicatorPosition(BOTTOM);          
  ui.setIndicatorDirection(LEFT_RIGHT);     
  ui.setFrameAnimation(SLIDE_LEFT);        
  ui.setFrames(frames, frameCount);         
  ui.setOverlays(overlays, overlaysCount);  
  ui.init();  
  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
}

void loop() {
  int remainingTimeBudget = ui.update();
  update_time_and_data();
  if (remainingTimeBudget > 0) {  
    //for (int i = 0; i < 24;i++){
    //  Serial.println(String(i)+" "+String(reading[pressure][i]));
    //}
    delay(remainingTimeBudget);
  }
}

void update_time_and_data() {
  while (!update_time());
  if (reading_hour != last_reading_hour) { 
    for (int i = 0; i < 23; i++) {
      reading[i].pressure     = reading[i + 1].pressure;
      reading[i].temperature  = reading[i + 1].temperature;
      reading[i].wx_state_1hr = reading[i + 1].wx_state_1hr;
      reading[i].wx_state_3hr = reading[i + 1].wx_state_3hr;
    }
    reading[23].pressure     = read_pressure();  
    reading[23].wx_state_1hr = current_wx;
    reading[23].wx_state_3hr = current_wx;
    last_reading_hour        = reading_hour;
    hr_cnt++;
    wx_average_1hr = reading[22].wx_state_1hr + current_wx;            
    wx_average_3hr = 0;
    for (int i = 23; i >= 21; i--) {                                   
      wx_average_3hr = wx_average_3hr + (int)reading[i].wx_state_3hr;  
    }
  }
}

bool update_time() {
  time_t now = time(nullptr);
  struct tm *now_tm;
  int hour, min, second, day, month, year;
  now = time(NULL);
  now_tm = localtime(&now);
  hour = now_tm->tm_hour;
  min  = now_tm->tm_min;
  int weekday = now_tm->tm_wday;
  String week_days[] = {"DOM ", "LUN ", "MAR ", "MER ", "GIO ", "VEN ", "SAB "};
  String wday = week_days[weekday];
  second = now_tm->tm_sec;
  day    = now_tm->tm_mday;
  month  = now_tm->tm_mon + 1;
  year   = now_tm->tm_year - 100; 
   
  time_str = (hour < 10 ? "0" : "") + String(hour) + ":" + (min < 10 ? "0" : "") + String(min) + ":" + (second < 10 ? "0" : "") + String(second)
             + " " + wday + (day < 10 ? "0" : " ") + String(day) + " / " + (month < 10 ? "0" : "") + String(month) + " / " + String(year);
  Serial.println(time_str);
  reading_hour = hour;
  return true;
}

int StartWiFi(const char* ssid, const char* password) {
  int connAttempts = 0;
  Serial.println("\r\nConnecting to: " + String(ssid));
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED ) {
    delay(500);
    Serial.print(".");
    if (connAttempts > 20) return false;
    connAttempts++;
  }
  Serial.print("WiFi connected\r\nIP address: ");
  Serial.println(WiFi.localIP());
  return true;
}
