/**
 * Copyright (c) 2022 - James Owens <jjo(at)arduando.com.br>
 * 
 * Arquivo:     _005_Save2EEPROM.ino
 * Arquivo:     18/12/2022 12:31:03
 * Versão:      
 * Fonte:       
 * Website:     https://arduando.com.br
 *
 * Descrição: 
 *
 * DISCLAIMER:
 * The author is in no way responsible for any problems or damage caused by
 * using this code. Use at your own risk.
 *
 * LICENSE:
 * This code is distributed under the GNU Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 * More details can be found at http://www.gnu.org/licenses/gpl.txt
 */

#include <EEPROM.h>




 /* Random number generation based on post
   "1999-01-15 Jeff Stout" @ http://www.ciphersbyritter.com/NEWS4/RANDC.HTM
 */
#define UL unsigned long
#define znew  ((z=36969*(z&65535)+(z>>16))<<16)
#define wnew  ((w=18000*(w&65535)+(w>>16))&65535)
#define MWC   (znew+wnew)
#define SHR3  (jsr=(jsr=(jsr=jsr^(jsr<<17))^(jsr>>13))^(jsr<<5))
#define CONG  (jcong=69069*jcong+1234567)
#define KISS  ((MWC^CONG)+SHR3)
 // Global static variables: the seed changes on every minute)/
UL z, w, jsr, jcong;
uint32_t seed;


#define EEPROM_SIGNATURE "~jbit.com.br"
#define EEPROM_SIGNATURE_LENGTH 11

#define KEY_ADDRESS_MAP_START EEPROM_SIGNATURE_LENGTH+1

#define NUMBER_OF_KEYS 7

uint8_t keyAddrMap[4];

#define SECURITY_KEY_APPEUI   0
#define SECURITY_KEY_DEVEUI   1
#define SECURITY_KEY_APPKEY   2
#define SECURITY_KEY_NETID    3
#define SECURITY_KEY_DEVADDR  4
#define SECURITY_KEY_ARTKEY   5
#define SECURITY_KEY_NWKEY    6

const uint8_t securityKeys[] = {16, 8, 8, 1, 4, 16, 16};
//////

// The order
// 00 AppEUI (16)
// 01 DevEUI (8)
// 02 AppKey (8)
// 03 netId (1)
// 04 devAddr (4)
// 05 artKey (16)
// 06 nwKey (16)



void securityKeyHandler(uint8_t _keyName, uint8_t *_keyValue, char _operation)
{
  uint8_t i=0;
  uint8_t tempByte;
  bool isHighByte = true;
  uint8_t keyAddressOffset=0;

  // There can be only 7, anything else is in error, minimum attempt to validate it
  if (_keyName < 0x07) 
  {
    Serial.println();
    do{
      tempByte = EEPROM.read(KEY_ADDRESS_MAP_START+i);
      if (isHighByte)
      {
        if ((tempByte >> 4) == _keyName)
          break;
        keyAddressOffset += securityKeys[tempByte>>4];
        isHighByte = false;
      }
      if (!isHighByte)
      {
        if ((tempByte & 0x0F) == _keyName)
          break;
        keyAddressOffset += securityKeys[tempByte&0x0F];
        isHighByte = true;
      }  
      i++;
    } while (i < ((NUMBER_OF_KEYS / 2) + (NUMBER_OF_KEYS % 2)));
  

    // Store the requested key in the buffer provided in the argument
    for (i = 0; i < securityKeys[_keyName]; i++)
    {
      if (_operation == 'r')
        _keyValue[i] = EEPROM.read(KEY_ADDRESS_MAP_START + NUMBER_OF_KEYS/2 + NUMBER_OF_KEYS%2 + keyAddressOffset + i);
      if (_operation == 'w')
        EEPROM.write(KEY_ADDRESS_MAP_START + NUMBER_OF_KEYS/2 + NUMBER_OF_KEYS%2 + keyAddressOffset + i, _keyValue[i]);
    }
  }  
}

void setSecurityKey(uint8_t _keyName, uint8_t *_keyValue)
{
  securityKeyHandler (_keyName, _keyValue, 'w');
}

void getSecurityKey(uint8_t _keyName, uint8_t *_keyValue)
{
  securityKeyHandler (_keyName, _keyValue, 'r');
}




void setup() 
{
	char keyValue[16];
	Serial.begin(115200);
	while (!Serial);

  eeSaveSignature();
  setKeyAddrMap();

  //keyValue = "Test"; 
  setSecurityKey(SECURITY_KEY_APPKEY, &keyValue[0]);

  dumpEEPROM();

  

  for (uint8_t i=0; i<NUMBER_OF_KEYS; i++)
  {
    getSecurityKey(i, &keyValue[0]);
    for (int j=0; j<securityKeys[i]; j++)
    {
      Serial.print(keyValue[j], HEX);
      Serial.print(" ");
    }
  }
}


void loop() 
{
}


void eeSaveSignature(void)
{
  // Initialize the first 255 EEPROM addresses
  // sequentially with values from 0 to 254 
  for (int i=0; i<255; i++)
    EEPROM.write(i, i);
	EEPROM.put(0, EEPROM_SIGNATURE);
	}



void setKeyAddrMap(void)
{
    uint8_t foundIndex = 0;
    bool foundIndexHighNibble = true;
    bool exists = false;
    uint8_t keyIndex = 0;
    bool keyIndexHighNibble = true;
    uint8_t sequence[NUMBER_OF_KEYS];


    for (uint8_t i = 0; i < strlen(__TIMESTAMP__); i++)
    {
        seed += __TIMESTAMP__[i];
    }

    // Set global variables used in the random number generator algorithm
    z = 362436069 * (int)seed, w = 521288629 * (int)seed, jsr = 123456789 * (int)seed, jcong = 380116160 * (int)seed;


    uint8_t digit;

    do
    {
        digit = KISS & 0xF;
        //Serial.print (digit , HEX);
        exists = false;
        if (digit >= 0 && digit < NUMBER_OF_KEYS)
        {
            for (uint8_t i = 0; i < foundIndex; i++)
            {
                if (sequence[i] == digit)
                    exists = true;
            }
            if (exists == false)
            {
                sequence[foundIndex] = digit;
                foundIndex++;
            }
        }
    } while (foundIndex < NUMBER_OF_KEYS);


    for (int i = 0, j = 0; j < (NUMBER_OF_KEYS / 2) + (NUMBER_OF_KEYS % 2); i += 2, j++)
    {
        keyAddrMap[j] = (sequence[i] << 4);

        if (NUMBER_OF_KEYS % 2 > 0)
        {
          if (i != (NUMBER_OF_KEYS - 1))
            keyAddrMap[j] += sequence[i + 1];
        }
        else keyAddrMap[j] += sequence[i + 1];

        EEPROM.write(KEY_ADDRESS_MAP_START+j, keyAddrMap[j]);
        Serial.print (keyAddrMap[j], HEX); Serial.print(" ");
    }

    delay(500);
    Serial.println();
}

void dumpEEPROM()
{
    int memIdx = 0;       // Indice na memória EEPROM
    int n = 0;            
    int tmp = 0;
    int precision = 1;    // Número de dígitos hex no endereço de memória
    int addrLength = 0;   // Total de bytes na memória EEPROM
    int rows = 0;         // Linha sendo processada nos resultados
    char address[128];    // Buffer contendo o intervalo de memória nos resultados
    char mask[16];        // Buffer com máscara de formatação do intervalo de memória

    // Diferentes arquiteturas podem variar o tamanho da EEPROM. Obtenho o numero de bytes.
    addrLength = EEPROM.length();
    tmp = addrLength;
    rows = addrLength / 16;   // São 16 bytes mostrados por linha
    // Cálculo do número de digitos hex no endereço, usado para formatação da saída
    do {
        tmp = tmp >> 4;
        if (tmp > 0) precision++;
    } while (tmp > 0);
    sprintf(mask, "0x%%.%dX 0x%%.%dX  ", precision, precision);

    Serial.print("\nDescarregando conteúdo da EEPROM (");
    Serial.print(addrLength);
    Serial.print(" bytes):\n");

    // Apresenta o conteúdo da memória EEPROM
    for (int r = 0; r < rows; r++)
    {
        sprintf(address, mask, memIdx, memIdx + 0xF);
        Serial.print(address);
        for (n = 0; n < 16; n++)
        {
            if (EEPROM[memIdx + n] <= 0xF)
              Serial.print("0");
            Serial.print(EEPROM[memIdx + n], HEX);
            if (n != 7)Serial.print(" ");
            else Serial.print("-");
        }
        Serial.print("  ");
        for (int n = 0; n < 16; n++)
        {
            // Mostra apenas caracteres válidos (entre ASCII 32 e 128)
            if (EEPROM[memIdx + n] > 128 || EEPROM[memIdx + n] < 32) Serial.print(".");
            else Serial.write(EEPROM[memIdx + n]);
        }
        memIdx += n;
        Serial.print("\n");
    }
    Serial.print("fim!");
}