#include <open62541.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>

static volatile UA_Boolean running = true;
 
static void stopHandler(int sig) {
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Received Ctrl-C");
    running = false;
}


static UA_StatusCode
helloMethodCallback(UA_Server *server,
                    const UA_NodeId *sessionId, void *sessionContext,
                    const UA_NodeId *methodId, void *methodContext,
                    const UA_NodeId *objectId, void *objectContext,
                    size_t inputSize, const UA_Variant *input,
                    size_t outputSize, UA_Variant *output)
{
    UA_String *inputStr = (UA_String*)input[0].data;
    const char *helloPrefix = "Hello ";
    size_t helloLen = strlen(helloPrefix);

    // 分配内存用于存储输出字符串（前缀 + 输入字符串）
    size_t resultLen = helloLen + inputStr->length;
    UA_Byte *result = (UA_Byte*)UA_malloc(resultLen);
    if (!result) {
        return UA_STATUSCODE_BADOUTOFMEMORY;
    }

    // 将 "Hello " 和输入字符串复制到结果中
    memcpy(result, helloPrefix, helloLen);
    memcpy(result + helloLen, inputStr->data, inputStr->length);

    // 将输出 variant 设置为结果字符串
    UA_String resultStr;
    resultStr.data = result;
    resultStr.length = resultLen;
    UA_Variant_setScalarCopy(output, &resultStr, &UA_TYPES[UA_TYPES_STRING]);

    // 清理结果缓冲区
    UA_String_clear(&resultStr);

    return UA_STATUSCODE_GOOD;
}

static void manuallyDefinePump(UA_Server *server)
{
    //Add NameSpace

    const char *namespaceUri = "http://example.com/MyNamespace";
    UA_UInt16 namespaceIndex = UA_Server_addNamespace(server, namespaceUri);


    // 声明并获取服务器分配的节点 ID
    UA_NodeId pumpId;

    // 定义对象属性并设置显示名称
    UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
    oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Pump (Manual)");

    // 添加泵对象节点
    UA_Server_addObjectNode(server,
                            UA_NODEID_NUMERIC(namespaceIndex, 1),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                            UA_QUALIFIEDNAME(1, "Pump (Manual)"),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
                            oAttr, NULL, &pumpId);

    // 定义变量属性并添加“ManufacturerName”变量节点
    UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
    UA_String manufacturerName = UA_STRING("Pump King Ltd.");
    UA_Variant_setScalar(&mnAttr.value, &manufacturerName, &UA_TYPES[UA_TYPES_STRING]);
    mnAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ManufacturerName");

    UA_Server_addVariableNode(server,
                            UA_NODEID_NULL,
                            pumpId,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                            UA_QUALIFIEDNAME(1, "ManufacturerName"),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                            mnAttr, NULL, NULL);

    // 定义变量属性并添加“ModelName”变量节点
    UA_VariableAttributes modelAttr = UA_VariableAttributes_default;
    UA_String modelName = UA_STRING("Mega Pump 3000");
    UA_Variant_setScalar(&modelAttr.value, &modelName, &UA_TYPES[UA_TYPES_STRING]);
    modelAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ModelName");

    UA_Server_addVariableNode(server,
                            UA_NODEID_NULL,
                            pumpId,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                            UA_QUALIFIEDNAME(1, "ModelName"),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                            modelAttr, NULL, NULL);

    // 定义变量属性并添加“Status”变量节点
    UA_VariableAttributes statusAttr = UA_VariableAttributes_default;
    UA_Boolean status = true;
    UA_Variant_setScalar(&statusAttr.value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
    statusAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Status");

    UA_Server_addVariableNode(server,
                            UA_NODEID_NULL,
                            pumpId,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                            UA_QUALIFIEDNAME(1, "Status"),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                            statusAttr, NULL, NULL);

    // 定义变量属性并添加“MotorRPM”变量节点
    UA_VariableAttributes rpmAttr = UA_VariableAttributes_default;
    UA_Double rpm = 50.0;
    UA_Variant_setScalar(&rpmAttr.value, &rpm, &UA_TYPES[UA_TYPES_DOUBLE]);
    rpmAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MotorRPM");

    UA_Server_addVariableNode(server,
                            UA_NODEID_NULL,
                            pumpId,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                            UA_QUALIFIEDNAME(1, "MotorRPM"),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                            rpmAttr, NULL, NULL);
    
    UA_ObjectAttributes ptAttr = UA_ObjectAttributes_default;
    ptAttr.displayName = UA_LOCALIZEDTEXT("en-US", "PumpType");
    UA_NodeId pumpTypeId;
    UA_Server_addObjectNode(server, UA_NODEID_NULL,
                                         pumpId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                         UA_QUALIFIEDNAME(1, "PumpType"), 
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
                                         ptAttr,
                                         NULL, &pumpTypeId);


    UA_Argument inputArgument;
    UA_Argument_init(&inputArgument);
    inputArgument.description = UA_LOCALIZEDTEXT("zh-CN", "输入字符串");
    inputArgument.name = UA_STRING("InputString");
    inputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId; // 字符串类型
    inputArgument.valueRank = UA_VALUERANK_SCALAR; // 标量

    // 定义方法的输出参数
    UA_Argument outputArgument;
    UA_Argument_init(&outputArgument);
    outputArgument.description = UA_LOCALIZEDTEXT("zh-CN", "输出字符串");
    outputArgument.name = UA_STRING("OutputString");
    outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId; // 字符串类型
    outputArgument.valueRank = UA_VALUERANK_SCALAR; // 标量

    // 添加方法节点
    UA_MethodAttributes helloAttr = UA_MethodAttributes_default;
    helloAttr.description = UA_LOCALIZEDTEXT("zh-CN", "添加Hello前缀");
    helloAttr.displayName = UA_LOCALIZEDTEXT("zh-CN", "HelloMethod");
    helloAttr.executable = true;
    helloAttr.userExecutable = true;

    UA_NodeId helloMethodNodeId;
    UA_Server_addMethodNode(server,
                            UA_NODEID_NULL, // NodeId (自动生成)
                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), // 父节点为 ObjectsFolder
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), // 引用类型
                            UA_QUALIFIEDNAME(1, "HelloMethod"), // 浏览名称
                            helloAttr, // 属性
                            &helloMethodCallback, // 回调函数
                            1, &inputArgument, // 输入参数的数量和描述
                            1, &outputArgument, // 输出参数的数量和描述
                            NULL,NULL);


}


 
int main(void) {
    signal(SIGINT, stopHandler);
    signal(SIGTERM, stopHandler);
 
    UA_Server *server = UA_Server_new();
    UA_ServerConfig_setDefault(UA_Server_getConfig(server));
    
    manuallyDefinePump(server);
    
    UA_StatusCode retval = UA_Server_run(server, &running);
    UA_Server_delete(server);
    return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
