////////////////////////////////////////////////////////////////////////////////////////////////////
//ANDRUINO LIBRARY
//A.Scavuzzo
//www.andruino.it
////////////////////////////////////////////////////////////////////////////////////////////////////

void eep_RestoreFromFlash();
void eep_RestoreSystemFromFlash();
void eep_RestorePinsFromFlash();
void eep_StoreDataToFlash();
void eep_Write_HttpRequest_time();
void eep_StoreDigitalToFlash();
void eep_StoreVarToFlash();


//ONLY IF YOU HAVE SOME ISSUE, THIS IS TO RESET ALL THE FLASH CONTENT
//IS BETTER TO HAVE THE SAME NUMBER IN ALL THE BOARDS, so when you update the sketch the configuration setup will be not erased
//for example: is you want to erase the flash configuration setup, change 0x01 to 0x02, compile the sketch and upload it on the board, wait some seconds, put again 0x01 and re-upload the sketch.
//In this mode all your sketch will have 0x01 code
#define CODE_ERASE 0x01


//don't change this
#define ANDRUINO_USERNAME_EMAIL "email@provider.com"
#define ANDRUINO_PIN 0000
#define ARDUINO_NAME "boardname"
#define ETHERNET_PORT 8888
#define SSID_WIFI_DEFAULT "wifi name"
#define PASS_WIFI_DEFAULT "password"

void eep_RestoreFromFlash() {

  //LOAD SETUP DATA FROM EEPROM
  //if the CODE signature is not 0xA4 means that the flash is not initializated for Andruino so it has to be erased
  if (eeprom_read_byteNEW (FLA_CODE_SIGNATURE_ADDRESS) != CODE_ERASE) {
    //erase all and init all (pin and push user not erased)
    eep_EraseDefaultFlash (true);
  }
  else {
    eep_Read_UserPin();

    //check if the PIN array definition has been changed by code
    checksum_digital_vector = 0;
    for (byte i = 0; i < sizeof(base_digital_pins_used); i++)
      checksum_digital_vector = checksum_digital_vector + base_digital_pins_used[i];

    unsigned int checksum_inv = 0;
    for (byte i = 0; i < sizeof(base_digital_pins_inv_used); i++)
      checksum_inv = (2 ^ i * base_digital_pins_inv_used[i]) + checksum_inv;

    unsigned int checksum_out = 0;
    for (byte i = 0; i < sizeof(base_digital_pins_mode_used); i++)
      checksum_out = (2 ^ i * base_digital_pins_mode_used[i]) + checksum_out;
    checksum_digital_vector = checksum_digital_vector ^ checksum_inv ^ checksum_out;


    //Serial.print("checksum_digital_vector:");Serial.println(checksum_digital_vector);

    //check if the eeprom pin space memory has been changed, so erase all
    if (eeprom_read_wordNEW ((FLA_STRUCT_START_ADDRESS_OK)) != checksum_digital_vector || eeprom_read_byteNEW ((FLA_STRUCT_STOP_ADDRESS_OK)) != 0x0A) {
      //erase all
      Serial.println(F("Erase pin eeprom data"));
      //Serial.print(F("checksum_digital_vector:"));Serial.println(checksum_digital_vector);
      for (int i = FLA_STRUCT_START_ADDRESS_OK; i < FLA_STRUCT_STOP_ADDRESS_OK; i++) {
        eeprom_write_byteNEW (i, 0);
      }
      pins_InitSensorArray();
      eep_RestoreSystemFromFlash();
      eep_StoreDataToFlash();                                 //update the eeprom with the current init state
    }
    else {
      //restore pin status
      eep_RestoreSystemFromFlash();
      eep_RestorePinsFromFlash();                             //restore the sensor data from flash and outputs (limits enable, values, hour, min, etc
    }
  }
}



void eep_EraseDefaultFlash (bool erase_all) {

  char ssid_station_char[SSID_LENGHT] = {'\0'};
  char pass_station_char[PASS_LENGHT] = {'\0'};

  //read user and PIN
  eep_Read_UserPin();                               //read pin_push_flash and push_user


  //only for wifi
#if WIFI_ESP8266_SHIELD == 1 ||  MCU_ESP8266 == 1
  //read wifi SSID STATION and store on var
  eeprom_read_blockNEW (ssid_station_char, FLA_SSIDWIFI_ADDRESS, SSID_LENGHT);
  eeprom_read_blockNEW (pass_station_char, FLA_PASSWIFI_ADDRESS, PASS_LENGHT);
#endif


  if (erase_all)
    //erase everything
    Serial.println(F("Erase all eeprom data"));
  else
    //erase everything but User, pin, CODE_ERASE
    Serial.println(F("Erase all eeprom data but User, pin, ardu board, port"));




#if MCU_ESP8266 == 1
  EEPROM.begin(BOARD_EEP_SIZE);
  for (int i = 0; i <= FLA_LAST_ADDRESS_USED; i++) {
    EEPROM.write(i, 0);
    ESP.wdtFeed();                                      //used to refresh watchdog and prevent reset
  }
  EEPROM.end();

#elif MCU_STM32 == 1

  //DA FARE
  //eeprom_erase_fill_all(0);
  // EEPROM.begin(BOARD_EEP_SIZE);
  for (int i = 0; i <= FLA_STRUCT_START_ADDRESS_OK; i++) {
    EEPROM.write(i, 0);                                      //used to refresh watchdog and prevent reset
  }


  // EEPROM.end();
#else
  for (int i = 0; i <= FLA_LAST_ADDRESS_USED; i++) {
    eeprom_write_byteNEW (i, 0);
  }
#endif



  //power-up cycles = 0
  //eeprom_write_wordNEW ((FLA_POWER_ADDRESS), 0);

  //program other base flash as default
#if MCU_ESP8266 == 1 || MCU_STM32 == 1
  eeprom_write_byteNEW (FLA_CODE_SIGNATURE_ADDRESS, CODE_ERASE);
#else
  eeprom_write_byteNEW ((uint8_t *)(FLA_CODE_SIGNATURE_ADDRESS), CODE_ERASE);
#endif

  //disable force captive portal
  eeprom_write_byteNEW ((FLA_FORCE_CAPTIVE_ADDRESS), 1);


  //if erase all, program default values
  if (erase_all) {
#if WIFI_ESP8266_SHIELD == 1 ||  MCU_ESP8266 == 1
    //write default wifi ssid and password
    eeprom_write_blockNEW(SSID_WIFI_DEFAULT, FLA_SSIDWIFI_ADDRESS, sizeof(SSID_WIFI_DEFAULT));
    eeprom_write_blockNEW(PASS_WIFI_DEFAULT, FLA_PASSWIFI_ADDRESS, sizeof(PASS_WIFI_DEFAULT));
#endif
  }
  //if NOT erase all, re-write the previous values
  else  {
    //write USER and PIN on flash
    eep_Write_Andruino_user_parameters();
    //only for wifi
#if WIFI_ESP8266_SHIELD == 1 ||  MCU_ESP8266 == 1
    //Save Wifi SSID and PASS
    eeprom_write_blockNEW(ssid_station_char, FLA_SSIDWIFI_ADDRESS, SSID_LENGHT);
    eeprom_write_blockNEW(pass_station_char, FLA_PASSWIFI_ADDRESS, PASS_LENGHT);
#endif
  }


  //fault restart =0
  //eeprom_write_byteNEW ((FLA_FAULT_RESTART_ADDRESS), 0);

  //default values
  client_sendjson_active_req_every_sec = HTTP_SEND_JSON_ACTIVE_REQ_EVERY_SEC;
  client_sendjson_notactive_req_every_sec = HTTP_SEND_JSON_NOTACTIVE_REQ_EVERY_SEC;
  ping_req_every_sec = HTTP_SEND_PING_REQ_EVERY_SEC;



  //init all PINS and VARS
  pins_InitSensorArray();
  eep_StoreDataToFlash();                                 //update the eeprom with the current init state
  eep_Read_Andruino_it_address();
  eep_RestoreSystemFromFlash();
  //Serial.print("Andruino_it:");Serial.println(andruino_it_ip_address);

  //if true, restart (all is erased)
  if (erase_all) {
    //   Restart(false, true);           //false doesn't update in flash the fault number, true SILENT powerup
  }


}



//////////////////////////////////////////////////////////
//restore from flash the system config
//////////////////////////////////////////////////////////
void eep_RestoreSystemFromFlash() {
  uint16_t address;

  //read the date if the device has been power-off for a while
  uint8_t tmp;
  tmp = eeprom_read_byteNEW ((FLA_DATE_ADDRESS + 1));                   //hours
  if (tmp < 24) {
    days_counter = eeprom_read_byteNEW ((FLA_DATE_ADDRESS));						//days
    hours_counter = tmp;
    minutes_counter = eeprom_read_byteNEW ((FLA_DATE_ADDRESS + 2));		  //minutes
  }

  //read Andruino ID
  pairing_id = eeprom_read_wordNEW ((FLA_ANDRUINO_PAIRING_ID_ADDRESS));

  //read fault reset
  fault_restart = eeprom_read_byteNEW ((FLA_FAULT_RESTART_ADDRESS));

  //read Andruino App version
  andruino_app_version = eeprom_read_wordNEW ((FLA_ANDRUINO_VERSION));

  //NRF read enable
  if (eeprom_read_byteNEW (FLA_NRF_ENABLE_ADDRESS) != 0x55) {     //0x55 = OFF
    nrf_radio_enable_rq = true;
  }

  unsigned int temp;

  temp = eeprom_read_byteNEW (FLA_HTTP_PING_ACTIVE_REQ_EVERY_SEC_ADDRESS);
  if (temp > 2)
    client_sendjson_active_req_every_sec = temp;

  temp = eeprom_read_wordNEW (FLA_HTTP_PING_REQ_EVERY_SEC_ADDRESS);
  if (temp > 0)
    ping_req_every_sec = temp;


  temp = eeprom_read_wordNEW (FLA_HTTP_PING_NOTACTIVE_REQ_EVERY_SEC_ADDRESS);   //0 is disabled
  if (temp >= 0)
    client_sendjson_notactive_req_every_sec = temp;



#if DEBUG_SERIAL_EEPROM == 1
  Serial.print(F("EEP: client_sendjson_active_req_every_sec:")); Serial.println(client_sendjson_active_req_every_sec);
  Serial.print(F("EEP: client_sendjson_notactive_req_every_sec:")); Serial.println(client_sendjson_notactive_req_every_sec);
  Serial.print(F("EEP: ping_req_every_sec:")); Serial.println(ping_req_every_sec);
#endif
  eep_Read_Andruino_it_address();

}

//////////////////////////////////////////////////////////
//restore from flash the pin state and config
//////////////////////////////////////////////////////////
void eep_RestorePinsFromFlash() {

  Serial.println(F("eep_RestorePinsFromFlash"));

#if DEBUG_SERIAL_EEPROM == 1
  Serial.print(F("EEP: Arduino eeprom size:")); Serial.println(BOARD_EEP_SIZE);
  Serial.print(F("EEP: first location sensors:")); Serial.print(FLA_STRUCT_START_ADDRESS_OK); Serial.print(F(", LAST location:")); Serial.println(FLA_LAST_ADDRESS_USED);
  Serial.print(F("EEP: Arduino_User_var size:")); Serial.print(sizeof(Arduino_User_var)); Serial.print(F(", start add:")); Serial.println(FLA_VAR_START_ADDRESS);
  Serial.print(F("EEP: ArduinoAnalog size:")); Serial.print(sizeof(ArduinoAnalog)); Serial.print(F(", start add:")); Serial.println(FLA_ANA_START_ADDRESS);
  Serial.print(F("EEP: ArduinoIO size:")); Serial.print(sizeof(ArduinoIO)); Serial.print(F(", start add:")); Serial.println(FLA_DIG_START_ADDRESS);
  Serial.print(F("EEP: Timers MAX_TIMERS:")); Serial.print(MAX_TIMERS); Serial.print(F(", start add:")); Serial.println(FLA_TIMERS_ADDRESS);
#endif


  if (FLA_LAST_ADDRESS_USED >= BOARD_EEP_SIZE-1) {
    for (byte j = 0; j < 50; j++) {
      Serial.print(F("EEP: ERROR, too many PINS are used"));
      Serial.print(F("EEP: out of memory: ")); Serial.print(FLA_LAST_ADDRESS_USED); Serial.print(F(", limit is: ")); Serial.println(BOARD_EEP_SIZE);
    }
    while (1);
  }

  //Arduino base RESTORE STRUCT
  eeprom_read_blockNEW(Arduino_User_var, FLA_VAR_START_ADDRESS, sizeof(Arduino_User_var));
  eeprom_read_blockNEW(ArduinoAnalog, FLA_ANA_START_ADDRESS, sizeof(ArduinoAnalog));
  eeprom_read_blockNEW(ArduinoIO, FLA_DIG_START_ADDRESS, sizeof(ArduinoIO));



  //NRF base RESTORE STRUCT
#if NRF24L_ENABLE == 1
#if DEBUG_SERIAL_EEPROM == 1
  Serial.print(F("EEP: SystemNRF24LPins size:")); Serial.print(sizeof(SystemNRF24LPins)); Serial.print(F(", start add:")); Serial.println(FLA_NRF_START_ADDRESS);
#endif
  eeprom_read_blockNEW (SystemNRF24LPins, FLA_NRF_START_ADDRESS, sizeof(SystemNRF24LPins));
#endif


  /////////////////////////////////////////
  //UPDATE PIN MODE and STATE FROM VECTORS
#if DEBUG_SERIAL_EEPROM == 1
  Serial.println(F("Restoring pins from eeprom..."));
#endif
  for (int i = 0; i < sizeof(base_digital_pins_used); i++) {  //MAXPIN

    if (ArduinoIO[i].mode == PWM)
      setupPWM(ArduinoIO[i], ArduinoIO[i].mode, ArduinoIO[i].state);	                      //setup + write the pwm value from flash content
    else
      setupDig(ArduinoIO[i], ArduinoIO[i].mode, ArduinoIO[i].state, ArduinoIO[i].inv);     //setup + write the digital value from flash content
  }


}



//////////////////////////////////////////////////////////
//Store all the data array on flash
//////////////////////////////////////////////////////////
void eep_StoreDataToFlash() {
  uint16_t address;
#if DEBUG_SERIAL == 1
  Serial.println(F("StoreData"));
#endif


  //store date if the device will power-off for a while
  eeprom_write_byteNEW ((FLA_DATE_ADDRESS), days_counter);                        //days
  eeprom_write_byteNEW ((FLA_DATE_ADDRESS + 1), hours_counter);                   //hours
  eeprom_write_byteNEW ((FLA_DATE_ADDRESS + 2), minutes_counter);                 //min


  //http requestes
  eep_Write_HttpRequest_time();


  //STORE STRUCT
  eeprom_write_wordNEW (FLA_STRUCT_START_ADDRESS_OK, checksum_digital_vector);                     //ID=checksum_digital_vector
  eeprom_write_byteNEW (FLA_STRUCT_STOP_ADDRESS_OK, 0x0A);                      //ID=0A
  eeprom_write_blockNEW(Arduino_User_var, FLA_VAR_START_ADDRESS, sizeof(Arduino_User_var));
  eeprom_write_blockNEW(ArduinoAnalog, FLA_ANA_START_ADDRESS, sizeof(ArduinoAnalog));
  eeprom_write_blockNEW(ArduinoIO, FLA_DIG_START_ADDRESS, sizeof(ArduinoIO));
#if NRF24L_ENABLE == 1
  eeprom_write_blockNEW(SystemNRF24LPins, FLA_NRF_START_ADDRESS, sizeof(SystemNRF24LPins));
  // eeprom_read_blockNEW (SystemNRF24LPins, 1300, sizeof(SystemNRF24LPins));
#endif

}
//////////////////////////////////////////////////////////
//Store digital PIN array on flash
//////////////////////////////////////////////////////////
void eep_StoreDigitalToFlash() {
#if DEBUG_SERIAL == 1
  Serial.println(F("StoreDigitals"));
#endif
  eeprom_write_blockNEW(ArduinoIO, FLA_DIG_START_ADDRESS, sizeof(ArduinoIO));
  delay(1);
#if NRF24L_ENABLE == 1
  eeprom_write_blockNEW(SystemNRF24LPins, FLA_NRF_START_ADDRESS, sizeof(SystemNRF24LPins));
  // eeprom_read_blockNEW (SystemNRF24LPins, 1300, sizeof(SystemNRF24LPins));
#endif

}

//////////////////////////////////////////////////////////
//Store var array on flash
//////////////////////////////////////////////////////////
void eep_StoreVarToFlash() {
#if DEBUG_SERIAL == 1
  Serial.println(F("StoreVariables"));
#endif
  eeprom_write_blockNEW(Arduino_User_var, FLA_VAR_START_ADDRESS, sizeof(Arduino_User_var));
}



/////////////////////////////////////
//read andruino.it from flash
void eep_Read_Andruino_it_address()
{

  if (eeprom_read_byteNEW ((FLA_ANDRUINO_IT_IP_OK)) == 0x55) {
    eeprom_read_blockNEW (andruino_it_ip_address, FLA_ANDRUINO_IT_IP, sizeof(andruino_it_ip_address));
    Serial.print(F("Read andruino ip from flash:\"")); Serial.print(andruino_it_ip_address); Serial.println(F("\""));
  } else {
    eeprom_write_byteNEW (FLA_ANDRUINO_IT_IP_OK, 0x55);
    eeprom_write_blockNEW(HTTP_ANDRUINO_SITEIP_DNS, FLA_ANDRUINO_IT_IP, sizeof(HTTP_ANDRUINO_SITEIP_DNS));
  }
}

/////////////////////////////////////
//read andruino.it from flash
bool eep_Write_Andruino_it_address(char *address_ip)
{
  byte lenght =  strlen(address_ip);                                     //strlen doesn't count the end of line \0
  if (lenght < sizeof(andruino_it_ip_address) && lenght > 5)  {
    Serial.print(F("Writing andruino ip to flash:\"")); Serial.print(address_ip); Serial.println(F("\""));
    Serial.print(F("lenght:")); Serial.println(lenght);
    memset(andruino_it_ip_address, 0, sizeof(andruino_it_ip_address));
    eeprom_write_blockNEW(andruino_it_ip_address, FLA_ANDRUINO_IT_IP, sizeof(andruino_it_ip_address));
    memcpy(andruino_it_ip_address, address_ip, lenght);
    eeprom_write_blockNEW(andruino_it_ip_address, FLA_ANDRUINO_IT_IP, lenght);
    eep_Read_Andruino_it_address();
    return (1);
  }
  return (0);
}

/////////////////////////////////////
//
void eep_Write_Andruino_user_parameters ()
{
  eeprom_write_byteNEW (FLA_PARAM_ADDRESS_OK, 0x55);
  eeprom_write_blockNEW(AndruinoApp_email_array, FLA_EMAIL_ADDRESS, sizeof(AndruinoApp_email_array));
  eeprom_write_blockNEW(AndruinoApp_PIN_array, FLA_PIN_ADDRESS, sizeof(AndruinoApp_PIN_array));
  eeprom_write_blockNEW(board_name_array, FLA_BOARD_NAME_ADDRESS, sizeof(board_name_array));
  eeprom_write_blockNEW(board_PORT_array, FLA_PORT_ADDRESS, sizeof(board_PORT_array));
}

void eep_Set_Andruino_user_default_parameters (char *email, int pin, char * board, int port)
{
  Serial.print(F("email:")); Serial.println(email);
  Serial.print(F("pin:")); Serial.println(pin);
  Serial.print(F("board:")); Serial.println(board);
  Serial.print(F("port:")); Serial.println(port);

  //fill array with default name
  fill_array(AndruinoApp_email_array, email);
  eep_generate_push_user(AndruinoApp_email_array);
  char buf [5];
  sprintf (buf, "%04i\0", pin);
  fill_array(AndruinoApp_PIN_array, buf);
  pin_push_flash = atoi(AndruinoApp_PIN_array);

  fill_array(board_name_array, board);

  sprintf (buf, "%i\0", port);
  fill_array(board_PORT_array, buf);
  server_port_number = atoi(board_PORT_array);

  eep_Write_Andruino_user_parameters ();

}


void eep_Read_Andruino_user_parameters ()
{
  if (eeprom_read_byteNEW ((FLA_PARAM_ADDRESS_OK)) == 0x55) {
    Serial.println(F("Read param from flash"));
    eeprom_read_blockNEW (AndruinoApp_email_array, FLA_EMAIL_ADDRESS, sizeof(AndruinoApp_email_array));
    eep_generate_push_user(AndruinoApp_email_array);

    eeprom_read_blockNEW (AndruinoApp_PIN_array, FLA_PIN_ADDRESS, sizeof(AndruinoApp_PIN_array));
    pin_push_flash = atoi(AndruinoApp_PIN_array);

    eeprom_read_blockNEW (board_name_array, FLA_BOARD_NAME_ADDRESS, sizeof(board_name_array));

    eeprom_read_blockNEW (board_PORT_array, FLA_PORT_ADDRESS, sizeof(board_PORT_array));
    server_port_number = atoi(board_PORT_array);
  }
  else {
    //fill array with default name
    eep_Set_Andruino_user_default_parameters(ANDRUINO_USERNAME_EMAIL, ANDRUINO_PIN, ARDUINO_NAME, ETHERNET_PORT);

  }
  /*Serial.print(F("AndruinoApp_email_array:"));Serial.println(AndruinoApp_email_array);
    Serial.print(F("AndruinoApp_PIN_array:"));Serial.println(AndruinoApp_PIN_array);
    Serial.print(F("board_name_array:"));Serial.println(board_name_array);
  */
}


void fill_array (char *destination, char *source) {
  memset(destination, 0, strlen(destination));
  memcpy(destination, source, strlen(source));
}


/////////////////////////////////////
//read USER and PIN PUSH from flash
bool eep_Read_UserPin() {

  //read from flash
  eep_Read_Andruino_user_parameters();
  Serial.print(F("USER EMAIL:")); Serial.print(AndruinoApp_email_array);
  Serial.print(F(", PUSH USER:")); Serial.print(push_user);
  Serial.print(F(", PIN:")); Serial.print(pin_push_flash);
  Serial.print(F(", BOARD:")); Serial.print(board_name_array);
  Serial.print(F(", PORT:")); Serial.println(server_port_number);
  if (pin_push_flash == 0) {
    Serial.println(F("WARN: PIN NOT SET, connect AndruinoApp for the first time"));
    return (false);
  }
  else {
    return (true);
  }
}



void eep_generate_push_user (char *USER_EMAIL) {
  byte USER_EMAIL_lenght = strlen(USER_EMAIL);
  memset(push_user, 0, sizeof(push_user));
  //max 12 char for user
  if (USER_EMAIL_lenght > 12)
    memcpy(push_user, USER_EMAIL, 12);                                                                    //copy only 12 char of user
  else
    memcpy(push_user, USER_EMAIL, USER_EMAIL_lenght);
}




void eep_Write_HttpRequest_time()
{
  //http requestes
#if MCU_ESP8266 == 1 || MCU_STM32 == 1
  eeprom_write_byteNEW ((FLA_HTTP_PING_ACTIVE_REQ_EVERY_SEC_ADDRESS), client_sendjson_active_req_every_sec);
  eeprom_write_wordNEW ((FLA_HTTP_PING_NOTACTIVE_REQ_EVERY_SEC_ADDRESS), client_sendjson_notactive_req_every_sec);
  eeprom_write_wordNEW ((FLA_HTTP_PING_REQ_EVERY_SEC_ADDRESS), ping_req_every_sec);
#else
  eeprom_write_byteNEW ((uint8_t *)(FLA_HTTP_PING_ACTIVE_REQ_EVERY_SEC_ADDRESS), client_sendjson_active_req_every_sec);
  eeprom_write_wordNEW ((uint16_t *)(FLA_HTTP_PING_NOTACTIVE_REQ_EVERY_SEC_ADDRESS), client_sendjson_notactive_req_every_sec);
  eeprom_write_wordNEW ((uint16_t *)(FLA_HTTP_PING_REQ_EVERY_SEC_ADDRESS), ping_req_every_sec);
#endif
}
