#ifndef LORARADIO_H
#define LORARADIO_H

#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <Arduino.h>

/*!	\mainpage Guide de démarrage rapide
 *
 * \section intro_sec Introduction
 *
 * Cette bibliothèque inclue deux classes : PinMap et LoRaRadio.
 * Attention, il faut préalablement installer la LMIC dans le dossier Documents\Arduino\libraries téléchargeable <a href="http://www.web-directories.tk/git/Th3maz1ng/projet3_temperature" target="_blank">ici</a>.   
 *
 * Voici un code d'exemple de l'utilisation de l'objet LoRaRadio :
 *
 * \code{.cpp}
 * #include <Arduino.h>
 * #include "LoRaRadio.h"
 * #include "PayloadFormatter.h"
 * 
 * u1_t dio[3] = {26,33,32};
 * 
 * void os_getArtEui (u1_t* buf) { }
 * void os_getDevEui (u1_t* buf) { }
 * void os_getDevKey (u1_t* buf) { }
 * 
 * static u1_t NWKSKEY[16] = { 0x1F, 0x9E, 0xE2, 0x7A, 0xC8, 0xBA, 0xE8, 0xEA, 0xF5, 0xC2, 0x5E, 0x47, 0x5D, 0xE0, 0x77, 0x55 };
 * static u1_t APPSKEY[16] = { 0x3B, 0x89, 0x86, 0x96, 0xBB, 0xAA, 0x38, 0x1E, 0x1F, 0xC4, 0xAD, 0x03, 0xEF, 0x3F, 0x56, 0x12 };
 * static u4_t DEVADDR = 0x260113D3;//0x03FF0001 ; // <-- Change this address for every node!
 * 
 * unsigned long lapse(0);
 * 
 * PinMap pinMap(18, LMIC_UNUSED_PIN, 14, dio);
 * LoRaRadio radio(pinMap);
 * 
 * PayloadFormatter pf(1,4);
 * DateTime payloadDate(2020,12,26,8,42);
 * double temps[4] = {21.31, 20.35, 21.25, 21.36};
 * 
 * void setup()
 * {
 *   Serial.begin(115200);
 *   Serial.println("Starting setup");
 *   radio.init();
 *   radio.setTTNSession(0x1, DEVADDR, NWKSKEY, APPSKEY);
 *   radio.setRadioEUChannels();
 *   radio.setMCUClockError();
 *   lapse = millis();
 *   Serial.println("Ending setup");
 * }
 * 
 * void loop()
 * {
 *   radio.run();
 *   if(millis() - lapse > 30000)
 *   {
 *     Serial.printf("Building payload\n");
 *     pf.startSession(1);
 *     uint8_t *p(NULL);
 *     uint8_t size = pf.buildPayload(&p, &payloadDate,temps);
 *     pf.endSession();
 *     Serial.printf("Sending data\n");
 *     radio.send(1, p, size);
 *     lapse = millis();
 *   }
 * }
 * \endcode
 * 
 * \author Anatole SCHRAMM-HENRY
 * \author Tim THUREL
 * \date 29 Jan 2020
 * \copyright All Right Reserved
 */

//void onEvent(ev_t ev);

/*! @brief La classe PinMap permet de mapper les différentes connections faites entre le module SX127X physique et le microcontrôleur.
 *
 *  Cet objet est ensuite utilisé par la classe LoRaRadio lors de son initialisation.
 */
class PinMap
{
  friend class LoRaRadio;
  public:
	/*!
	 * Le constructeur de la classe prend en paramètres :
	 * @param nss : la broche du microcontrôleur qui est reliée au chip select du SX127X
     * @param rxtx : la broche du microcontrôleur qui est reliée à la broche rxtx du SX127X. Si cette broche n'éxiste pas, mettre la valeur : LMIC_UNUSED_PIN.
	 * @param rst : la broche du microcontrôleur qui est reliée à la broche reset du SX127X.
	 * @param dio : un tableau de type u1_t de 3 éléments contenant les broches du microcontrôleur qui sont respectivement reliée aux broches DIO0, DIO1 et DIO2 du SX127X.
	 * Les broches DIOX transmettent des informations à la LMIC. Par exemple dans le mode LoRa, DIO0 correspond à TxDone et RxDone et DIO1 à RxTimeout.
	 * DIO2 peut quand à lui prendre la valeur LMIC_UNUSED_PIN.
	 */
    PinMap(u1_t nss, u1_t rxtx, u1_t rst, const u1_t dio[3]) : _nss(nss), _rxtx(rxtx), _rst(rst)
    {
      _dio[0] = dio[0];
      _dio[1] = dio[1];
      _dio[2] = dio[2];
    }
  protected:
  private:
    u1_t _nss, _rxtx, _rst, _dio[3];
};

/*! @brief La classe LoRaRadio permet de manipuler le module radio.
 *
 */
class LoRaRadio
{
  public:
	/*!
	 * Le constructeur de la classe prend en paramètres :
	 * @param pinMap : un objet de type PinMap créé préalablement.
	 * @param dataRate : de type dr_t indiquant quel spreading factor utiliser lors des envoies de données. Les valeurs possibles : DR_SF7B, DR_SF7, DR_SF8, DR_SF9, DR_SF10, DR_SF11, DR_SF12, DR_FSK et DR_NONE. 
	 * Plus le SF choisi est faible, plus le débit est élevé mais plus l'imunité face au bruit est mauvaise.
	 * @param txPower : puissance d'émission comprise entre 7 et 27 inclu (à vérifier).
	 */
    LoRaRadio(PinMap pinMap, dr_t dataRate = DR_SF7, s1_t txPower = 23);
    
	/*!
	 * Initialise le SX127X. Cette méthode doit obligatoirement être appelée en premier.
	 */
    void init();
	/*!
	 * Permet de renseigner les informations de la session TTN à utiliser. 
	 * @param channelId : l'id du canal
	 * @param deviceAddress : l'adresse unique du noeud LoRa. Information disponible dans la console TTN.
	 * @param networkSystemKey : la clef de chiffrement réseau. Information disponible dans la console TTN.
	 * @param applicationSystemKey : la clef de chiffrement application. Information disponible dans la console TTN.
	 * @param linkCheckMode : Active ou désactive la vérification périodique du lien.
	 */
    void setTTNSession(u4_t channelId, devaddr_t deviceAddress, xref2u1_t networkSystemKey, xref2u1_t applicationSystemKey, bit_t linkCheckMode = 0);
	/*!
	 * Paramètre la radio pour utiliser les fréquences européennes comprises entre 867.1Mhz et 868.1Mhz. 
	 */
    void setRadioEUChannels();
	/*!
	 * Directive permettant de compenser les imprécisions dû au cristal en alongeant la fenêtre de réception. Cette méthode est à utiliser si le downlink n'est pas reçu par le noeud.
     * @param percent : l'erreur de précision en % à corriger.	 
	 */
    void setMCUClockError(u2_t percent = 30);
	/*!
	 * Envoi une trame à la gateway LoRa.
	 * @param port : port de réception de la trame dans la console TTN. Peut prendre la valeur 1,2,3,4,5...
	 * @param *data : pointeur sur un tableau d'uint8_t contenant les données à envoyer. La longueur maximale des données et de 51 octets/trame quelque soit le spreading factor selectionné.
	 * @param length : le nombre d'octets de données.
	 * @param confirmed : demander un accusé de réception lors de l'envoi.
	 */
    void send(u1_t port, uint8_t *data, uint8_t length, u1_t confirmed = false);
	/*!
	 * Méthode à appeler dans la boucle principale afin que le module opère correctement.
	 *
	 */
    void run();
	/*!
	 * Méthode qui enregistre un callback (pointeur de fonction) qui sera appelé lors de la réception d'un downlink.
	 * @param *funcP : un pointeur sur fonction qui prend 3 paramètres. la longueur de la données reçu, l'indice de la première donnée dans le buffer et un pointeur sur le buffer.
	 */
    void setDownlinkHandler(void (*funcP)(u1_t, u1_t, u1_t*));
	/*!
	 * Méthode qui enregistre un callback (pointeur de fonction) qui sera appelé lorsque les donnée auront fini d'être envoyées suite à un appel à send.
	 * @param *funcP : un pointeur sur fonction qui ne prend pas de paramètre.
	 */
	void setSendCompleteHandler(void (*funcP)(void));
	/*!
	 * Permet de désactiver un canal et donc une fréquence d'envoie.
	 * @param channel : le canal en question.
	 */
    void disableEUChannel(u1_t channel);
	/*!
	 * Permet de désactiver tous les canaux sauf un.
	 * @param channel : le canal à laisser actif.
	 */
    void disableAllEUChannelsBut(u1_t channel);
    /**
    * Here, we define the onEvent function required by the LMIC
    **/
    friend void onEvent(ev_t ev);
  protected:
  private:
    //Function pointers used to interact with events
    //Parameters : dataLen, dataBeg, dataBuffer
    static void (*downlinkHandler)(u1_t, u1_t, u1_t*);
    static void (*sendCompleteHandler)(void);
    dr_t _dataRate;
    s1_t _txPower;
    PinMap _pinMap;
};

#endif //LORARADIO_H
