#include "beremiz.h"
/* code generated by beremiz OPC-UA extension */

#include <open62541/client_config_default.h>
#include <open62541/client_highlevel.h>
#include <open62541/plugin/log_stdout.h>
#include <open62541/plugin/securitypolicy.h>
#include <open62541/plugin/securitypolicy_default.h>

#include <open62541/types.h>
#include <open62541/types_generated_handling.h>

#define _Log(level, ...)                                                                           \
    {                                                                                             \
        char mstr[256];                                                                            \
        snprintf(mstr, 255, __VA_ARGS__);                                                          \
        LogMessage(level, mstr, strlen(mstr));                                                     \
    }

#define LogInfo(...) _Log(LOG_INFO, __VA_ARGS__);
#define LogError(...) _Log(LOG_CRITICAL, __VA_ARGS__);
#define LogWarning(...) _Log(LOG_WARNING, __VA_ARGS__);

static UA_INLINE UA_ByteString
loadFile(const char *const path) {
    UA_ByteString fileContents = UA_STRING_NULL;

    FILE *fp = fopen(path, "rb");
    if(!fp) {
        errno = 0;
        LogError("OPC-UA could not open %s", path);
        return fileContents;
    }

    fseek(fp, 0, SEEK_END);
    fileContents.length = (size_t)ftell(fp);
    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
    if(fileContents.data) {
        fseek(fp, 0, SEEK_SET);
        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
        if(read != fileContents.length){
            UA_ByteString_clear(&fileContents);
            LogError("OPC-UA could not read %s", path);
        }
    } else {
        fileContents.length = 0;
        LogError("OPC-UA Not enough memoty to load %s", path);
    }
    fclose(fp);

    return fileContents;
}

static UA_Client *client;
static UA_ClientConfig *cc;

#define DECL_VAR(ua_type, C_type, c_loc_name)                                                       \
static UA_Variant c_loc_name##_variant;                                                             \
static C_type c_loc_name##_buf = 0;                                                                 \
C_type *c_loc_name = &c_loc_name##_buf;


DECL_VAR(UA_Double, double, __IL1_2)
DECL_VAR(UA_Double, double, __QL1_2)

void __cleanup_1(void)
{
    UA_Client_disconnect(client);
    UA_Client_delete(client);
}

#define INIT_NoAuth()                                                                              \
    LogInfo("OPC-UA Init no auth");                                                                \
    UA_ClientConfig_setDefault(cc);                                                                \
    retval = UA_Client_connect(client, uri);

/* Note : Single policy is enforced here, by default open62541 client supports all policies */
#define INIT_x509(Policy, UpperCaseMode, PrivateKey, Certificate)                                  \
    LogInfo("OPC-UA Init x509 %s,%s,%s,%s", #Policy, #UpperCaseMode, PrivateKey, Certificate);     \
                                                                                                   \
    UA_ByteString certificate = loadFile(Certificate);                                             \
    UA_ByteString privateKey  = loadFile(PrivateKey);                                              \
                                                                                                   \
    cc->securityMode = UA_MESSAGESECURITYMODE_##UpperCaseMode;                                     \
                                                                                                   \
    /* replacement for default behaviour */                                                        \
    /* UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey, NULL, 0, NULL, 0); */     \
    do{                                                                                           \
        retval = UA_ClientConfig_setDefault(cc);                                                   \
        if(retval != UA_STATUSCODE_GOOD)                                                           \
            break;                                                                                 \
                                                                                                   \
        UA_SecurityPolicy *sp = (UA_SecurityPolicy*)                                               \
            UA_realloc(cc->securityPolicies, sizeof(UA_SecurityPolicy) * 2);                       \
        if(!sp){                                                                                  \
            retval = UA_STATUSCODE_BADOUTOFMEMORY;                                                 \
            break;                                                                                 \
        }                                                                                         \
        cc->securityPolicies = sp;                                                                 \
                                                                                                   \
        retval = UA_SecurityPolicy_##Policy(&cc->securityPolicies[cc->securityPoliciesSize],       \
                                                 certificate, privateKey, &cc->logger);            \
        if(retval != UA_STATUSCODE_GOOD) {                                                        \
            UA_LOG_WARNING(&cc->logger, UA_LOGCATEGORY_USERLAND,                                   \
                           "Could not add SecurityPolicy Policy with error code %s",               \
                           UA_StatusCode_name(retval));                                            \
            UA_free(cc->securityPolicies);                                                         \
            cc->securityPolicies = NULL;                                                           \
            break;                                                                                 \
        }                                                                                         \
                                                                                                   \
        ++cc->securityPoliciesSize;                                                                \
    } while(0);                                                                                   \
                                                                                                   \
    retval = UA_Client_connect(client, uri);                                                       \
                                                                                                   \
    UA_ByteString_clear(&certificate);                                                             \
    UA_ByteString_clear(&privateKey);

#define INIT_UserPassword(User, Password)                                                          \
    LogInfo("OPC-UA Init UserPassword %s,%s", User, Password);                                     \
    UA_ClientConfig_setDefault(cc);                                                                \
    retval = UA_Client_connectUsername(client, uri, User, Password);

#define INIT_READ_VARIANT(ua_type, c_loc_name)                                                     \
    UA_Variant_init(&c_loc_name##_variant);

#define INIT_WRITE_VARIANT(ua_type, ua_type_enum, c_loc_name)                                      \
    UA_Variant_setScalar(&c_loc_name##_variant, (ua_type*)c_loc_name, &UA_TYPES[ua_type_enum]);

int __init_1(int argc,char **argv)
{
    UA_StatusCode retval;
    client = UA_Client_new();
    cc = UA_Client_getConfig(client);
    char *uri = "opc.tcp://192.168.0.118:4840";

    INIT_NoAuth()
    INIT_READ_VARIANT(UA_Double, __IL1_2)
    INIT_WRITE_VARIANT(UA_Double, UA_TYPES_DOUBLE, __QL1_2)

    if(retval != UA_STATUSCODE_GOOD) {
        LogError("OPC-UA Init Failed %d", retval);
        UA_Client_delete(client);
        return EXIT_FAILURE;
    }
    return 0;
}

#define READ_VALUE(ua_type, ua_type_enum, c_loc_name, ua_nodeid_type, ua_nsidx, ua_node_id)        \
    retval = UA_Client_readValueAttribute(                                                         \
        client, ua_nodeid_type(ua_nsidx, ua_node_id), &c_loc_name##_variant);                      \
    if(retval == UA_STATUSCODE_GOOD && UA_Variant_isScalar(&c_loc_name##_variant) &&               \
       c_loc_name##_variant.type == &UA_TYPES[ua_type_enum]) {                                    \
            c_loc_name##_buf = *(ua_type*)c_loc_name##_variant.data;                               \
            UA_Variant_clear(&c_loc_name##_variant);  /* Unalloc requiered on each read ! */       \
    }

void __retrieve_1(void)
{
    UA_StatusCode retval;

    READ_VALUE(UA_Double, UA_TYPES_DOUBLE, __IL1_2, UA_NODEID_NUMERIC, 2, 2)
}

#define WRITE_VALUE(ua_type, c_loc_name, ua_nodeid_type, ua_nsidx, ua_node_id)                     \
    UA_Client_writeValueAttribute(                                                                 \
        client, ua_nodeid_type(ua_nsidx, ua_node_id), &c_loc_name##_variant);

void __publish_1(void)
{

    WRITE_VALUE(UA_Double, __QL1_2, UA_NODEID_NUMERIC, 2, 2)
}

