/**
* @file Mesh_Network.h
* @brief Mesh network layer used by OTA and APP
* @author Hendrik Schutter, init based in ESP32-IDE code
* @date 20.01.2021
*
* Additional Infos: Start network and send and receive data.
*/

#ifndef H_MESH_NETWORK
#define H_MESH_NETWORK

#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_mesh.h"
#include "esp_mesh_internal.h"
#include "nvs_flash.h"

#ifndef CONFIG_MESH_MESSAGE_SIZE
#define CONFIG_MESH_MESSAGE_SIZE 1500
#endif
#ifndef CONFIG_MESH_TOPOLOGY
#define CONFIG_MESH_TOPOLOGY MESH_TOPO_TREE
#endif
#ifndef CONFIG_MESH_MAX_LAYER
#define CONFIG_MESH_MAX_LAYER 6
#endif
#ifndef CONFIG_MESH_ID
#define CONFIG_MESH_ID "00, 00, 00, 00, 00, 00"
#endif
#ifndef CONFIG_MESH_AP_AUTHMODE
#define CONFIG_MESH_AP_AUTHMODE WIFI_AUTH_WPA2_PSK
#endif
#ifndef CONFIG_MESH_AP_CONNECTIONS
#define CONFIG_MESH_AP_CONNECTIONS 6
#endif
#ifndef CONFIG_MESH_AP_PASSWD
#define CONFIG_MESH_AP_PASSWD "MAP_PASSWD"
#endif
#ifndef CONFIG_MESH_CHANNEL
#define CONFIG_MESH_CHANNEL 0
#endif

#ifndef CONFIG_MESH_ROUTER_SSID
#define CONFIG_MESH_ROUTER_SSID "ROUTER_SSID"
#endif

#ifndef CONFIG_MESH_ROUTER_PASSWD
#define CONFIG_MESH_ROUTER_PASSWD "ROUTER_PASSWD"
#endif

#ifndef CONFIG_MESH_ROUTE_TABLE_SIZE
#define CONFIG_MESH_ROUTE_TABLE_SIZE 50
#endif

#define MESH_NETWORK_PAYLOAD_SIZE 1024U

struct meshPacket
{
    enum otaMeshPacketType
    {
        APP_Data,            //data for application
        OTA_Version_Request, //send own version in payload
        OTA_Version_Response, //send own version in payload
        OTA_Data,            //send image segment
        OTA_ACK,             //ack image segment
        OTA_Complete,         //signal end of image
        OTA_Abort             //abort OTA process
    } type;
    uint8_t au8Payload[MESH_NETWORK_PAYLOAD_SIZE];
    mesh_addr_t meshSenderAddr;
};

typedef struct meshPacket MESH_PACKET_t;

#define ERROR_CHECK(x) if (err == ESP_OK)                                                               \
    {                                                                                                   \
        err = (x);                                                                                      \
        if (err != ESP_OK)                                                                              \
            {                                                                                           \
                ESP_LOGE(LOG_TAG,  "%s failed with error: 0x%x -> %s", #x,  err, esp_err_to_name(err)); \
            }                                                                                           \
    }                                                                                                   \

extern bool bIsMeshConnected;
extern int32_t i32MeshLayer;
extern mesh_addr_t meshParentAddr;
extern esp_netif_t* pNetifSta;
extern uint8_t u8ownMAC[6];
extern void (*pOTAChildConnectHandle)(const uint8_t* const);
extern void (*pChangeStateOfServerWorkerHandle)(const bool );

esp_err_t errMeshNetworkInitialize(void);
esp_err_t errMeshNetworkInitializeWiFi(void);
esp_err_t errMeshNetworkInitializeRouter(mesh_cfg_t* cfg);

esp_err_t errMeshNetworkSetChildConnectedHandle(void (*pChildConnectHandleTmp)(const uint8_t* const cpcu8Data));
esp_err_t errMeshNetworkSetAppReceiveHandle(void (*pAppRxHandleTmp)(const uint8_t* const cpcu8Data, const uint8_t* const cpcu8Sender));
esp_err_t errMeshNetworkSetOTAMessageHandleHandle(void (*pOTAMessageHandleTmp)(const MESH_PACKET_t* const cpcuMeshPacket));
esp_err_t errMeshNetworkSetChangeStateOfServerWorkerHandle(void (*pChangeStateOfServerWorkerHandleTmp)(const bool cbState));

bool bMeshNetworkIsRootNode(void);
bool bMeshNetworkIsNodeNeighbour(const mesh_addr_t* const cpcNode);
bool bMeshNetworkCheckMacEquality(const uint8_t* const cpcu8aMAC, const uint8_t* const cpcu8bMAC);

esp_err_t errMeshNetworkStartReceiveTask(void);
esp_err_t errMeshNetworkGetParentNode(mesh_addr_t* const cpMeshParentAddr);
esp_err_t errMeshNetworkGetChildren(mesh_addr_t* const cpChildren, uint16_t* const cpu16ChildrenSize);
esp_err_t errMeshNetworkSendMeshPacket(const mesh_addr_t* const cpcAddrDest, const MESH_PACKET_t* const cpcPacket);

void vMeshNetworkTaskReceiveMeshData(void *arg);
void vMeshNetworkGetOwnAddr(mesh_addr_t* const cpMeshOwnAddr);
void vMeshNetworkIpEventHandler(void *arg, esp_event_base_t event_base, int32_t i32EventID, void *vpEventData);
void vMeshNetworkMeshEventHandler(void *arg, esp_event_base_t event_base, int32_t i32EventID, void* vpEventData);

#endif /* H_MESH_NETWORK */