#include "ua_client.h"
#include "../common.h"
#include "ua_com.h"

UA_Client *ua_connect_opcua_ps(const char *url, const char *name, const char *passwd)
{
    UA_Client *uacli;
    UA_StatusCode retval;
    UA_ClientConfig *ccfg;
    UA_ByteString *revocationList = NULL;
    size_t revocationListSize = 0;
    size_t trustListSize = 1;
    UA_String securityPolicyUri = UA_STRING_NULL;
    UA_MessageSecurityMode securityMode = UA_MESSAGESECURITYMODE_INVALID;

    uacli = UA_Client_new();
    if (uacli == NULL)
    {
        ems_syslog(LOG_ERR, "Failed to create new UA client.");
        return NULL;
    }

    UA_ByteString certificate = loadFile(CLIENT_CERT_CERT);
    UA_ByteString privateKey = loadFile(CLIENT_CERT_KEY);
    ccfg = UA_Client_getConfig(uacli);

    /* Load the trustList */
    UA_STACKARRAY(UA_ByteString, trustList, trustListSize + 1);
    for (size_t trustListCount = 0; trustListCount < trustListSize; trustListCount++)
        trustList[trustListCount] = loadFile(SERVER_CERT_CERT);

    /* Secure client initialization */
    UA_ClientConfig_setDefaultEncryption(ccfg, certificate, privateKey,
                                         trustList, trustListSize, revocationList, revocationListSize);
    ccfg->securityMode = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT;

    ccfg->securityMode = securityMode;
    ccfg->securityPolicyUri = securityPolicyUri;
    UA_ByteString_clear(&certificate);
    UA_ByteString_clear(&privateKey);
    for (size_t trustListCount = 0; trustListCount < trustListSize; trustListCount++)
    {
        UA_ByteString_clear(&trustList[trustListCount]);
    }

    retval = UA_Client_connectUsername(uacli, url, name, passwd);
    if (retval != UA_STATUSCODE_GOOD)
    {
        ems_syslog(LOG_ERR, "Failed to connect to %s: %s", url, UA_StatusCode_name(retval));
        UA_Client_disconnect(uacli);
        UA_Client_delete(uacli);
        return NULL;
    }

    return uacli;
}

int ua_client_delete(UA_Client *client)
{
    if (client)
    {
        UA_Client_disconnect(client);
        UA_Client_delete(client);
    }
    return 0;
}

UA_StatusCode ua_mul_read(UA_Client *client, UA_ReadRequest request)
{
    return UA_STATUSCODE_GOOD;
}

char *ua_client_call_read_cfg_by_hd(UA_Client *client, char *par)
{
    char *ret = NULL;
    UA_Variant input;
    UA_String arg = UA_STRING(par);
    UA_Variant_init(&input);
    UA_Variant_setScalarCopy(&input, &arg, &UA_TYPES[UA_TYPES_STRING]);
    size_t outputSize;
    UA_Variant *output;
    UA_StatusCode retval = UA_Client_call(client, ua_user_string_2_node_id("Advance"),
                                          UA_NODEID_STRING(1, "ReadCfg"), 1, &input, &outputSize, &output);
    if (retval == UA_STATUSCODE_GOOD)
    {
        if (output->type == &UA_TYPES[UA_TYPES_STRING])
        {

            UA_String *outputString = (UA_String *)output->data;
            ret = (char *)calloc(1, outputString->length + 1);
            if (ret != NULL)
            {
                memcpy(ret, outputString->data, outputString->length);
            }
        }
        UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
    }
    UA_Variant_clear(&input);

    return ret;
}

char *ua_client_call_read_cfg(const char *url, char *par)
{
    char *ret = NULL;
    UA_Client *client = ua_connect_opcua(url);
    if (client == NULL)
    {
        return NULL;
    }
    ret = ua_client_call_read_cfg_by_hd(client, par);
    ua_client_delete(client);

    return ret;
}

char *ua_client_call_write_cfg_by_hd(UA_Client *client, char *name, const char *content, int content_len)
{
    char *ret = NULL;
    UA_Variant input[2];
    UA_String ua_name = UA_STRING(name);
    // 为了方便，直接手动构造UA_String
    UA_String ua_con = UA_STRING_NULL;
    ua_con.length = content_len;
    ua_con.data = (UA_Byte *)content;
    //
    UA_Variant_init(&input[0]);
    UA_Variant_init(&input[1]);
    UA_Variant_setScalarCopy(&input[0], &ua_name, &UA_TYPES[UA_TYPES_STRING]);
    UA_Variant_setScalarCopy(&input[1], &ua_con, &UA_TYPES[UA_TYPES_STRING]);
    size_t outputSize;
    UA_Variant *output;
    UA_StatusCode retval = UA_Client_call(client, ua_user_string_2_node_id("Advance"),
                                          UA_NODEID_STRING(1, "WriteCfg"), 2, input, &outputSize, &output);
    if (retval == UA_STATUSCODE_GOOD)
    {
        if (output->type == &UA_TYPES[UA_TYPES_STRING])
        {
            UA_String *outputString = (UA_String *)output->data;
            ret = (char *)calloc(1, outputString->length + 1);
            if (ret != NULL)
            {
                memcpy(ret, outputString->data, outputString->length);
            }
        }
        UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
    }
    UA_Variant_clear(&input[0]);
    UA_Variant_clear(&input[1]);

    return ret;
}

char *ua_client_call_write_cfg(const char *url, char *name, char *content, int content_len)
{
    char *ret = NULL;
    UA_Client *client = ua_connect_opcua(url);
    if (client == NULL)
    {
        return NULL;
    }
    ret = ua_client_call_write_cfg_by_hd(client, name, content, content_len);
    ua_client_delete(client);
    return ret;
}

long ua_client_read_node_value_int(UA_Client *ua_client, char *node_id)
{
    UA_Variant val;
    long result = -1;
    UA_NodeId ua_node_id = UA_NODEID_STRING_ALLOC(1, node_id);

    UA_Variant_init(&val);
    UA_StatusCode retval = UA_Client_readValueAttribute(ua_client, ua_node_id, &val);
    if (retval == UA_STATUSCODE_GOOD)
    {
        if (val.type == &UA_TYPES[UA_TYPES_INT32])
        {
            result = (long)(*(UA_Int32 *)val.data);
        }
    }
    UA_Variant_clear(&val);
    UA_NodeId_clear(&ua_node_id);

    return result;
}



double ua_client_read_node_value_float(UA_Client *ua_client, char *node_id)
{
    UA_Variant val;
    double result = -1;
    UA_NodeId ua_node_id = UA_NODEID_STRING_ALLOC(1, node_id);

    UA_Variant_init(&val);
    UA_StatusCode retval = UA_Client_readValueAttribute(ua_client, ua_node_id, &val);
    if (retval == UA_STATUSCODE_GOOD)
    {
        if (val.type == &UA_TYPES[UA_TYPES_DOUBLE])
        {
            result = *(UA_Double *)val.data;
        }
    }
    UA_Variant_clear(&val);
    UA_NodeId_clear(&ua_node_id);

    return result;
}


UA_StatusCode ua_client_write_node_value_int(UA_Client *ua_client, char *node_id, int value)
{
    UA_NodeId ua_node_id = UA_NODEID_STRING_ALLOC(1, node_id);
    UA_Variant val;
    UA_Variant_init(&val);
    UA_Variant_setScalar(&val, &value, &UA_TYPES[UA_TYPES_INT32]);
    UA_StatusCode retval = UA_Client_writeValueAttribute(ua_client, ua_node_id, &val);
    //UA_Variant_clear(&val);
    UA_NodeId_clear(&ua_node_id);
    return retval;
}

UA_StatusCode ua_client_write_node_value_float(UA_Client *ua_client, char *node_id, double value)
{
    UA_NodeId ua_node_id = UA_NODEID_STRING_ALLOC(1, node_id);
    UA_Variant val;
    UA_Variant_init(&val);
    UA_Variant_setScalar(&val, &value, &UA_TYPES[UA_TYPES_DOUBLE]);
    UA_StatusCode retval = UA_Client_writeValueAttribute(ua_client, ua_node_id, &val);
    //UA_Variant_clear(&val);
    UA_NodeId_clear(&ua_node_id);
    return retval;
}