//
// Created by magino on 11.4.19.
//

#include "MyUaServer.h"

MyUaServer::MyUaServer(UaServerCfg cfg): UaServer(cfg.port, nullptr, BuildAppDescription(cfg), BuildmDnsConfiguration(cfg), cfg.ipAddress) {}

void MyUaServer::ServerOnNetwork(const UA_ServerOnNetwork *serverOnNetwork) {
    char* discoveryUrl = UaStringToPChar(&serverOnNetwork->discoveryUrl);

    // Check if is server is already registered
    for (const auto& client : _registerClients) {
        if(client.first == std::string(discoveryUrl)) {
            UA_free(discoveryUrl);
            return;
        }
    }

    UA_LOG_INFO(UA_Log_Stdout,
                UA_LOGCATEGORY_USERLAND,
                "[mDNS] New server announced itself on %.*s",
                (int)serverOnNetwork->discoveryUrl.length, serverOnNetwork->discoveryUrl.data);

    // Check if is announced server has desired capabilities
    bool serverWithDesiredCapability = false;
    for(int j=0; j<serverOnNetwork->serverCapabilitiesSize; ++j) {
        auto capability = ToStdString(serverOnNetwork->serverCapabilities[j]);
        if(capability == MyUaServer::StorageCapability || capability == MyUaServer::TransportCapability || capability ==MyUaServer::ActionCapability) {
            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "[mDNS] Process cell with desired capabilities found.");
            serverWithDesiredCapability = true;
            break;
        }
    }
    if (!serverWithDesiredCapability) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "[mDNS] Server do not include desired capabilities.");
        return;
    }

    // Find register endpoint
    UA_EndpointDescription* endpointRegister = GetRegisterEndpointFromServer(discoveryUrl);
    if(!endpointRegister || endpointRegister->securityMode == UA_MESSAGESECURITYMODE_INVALID) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"[mDNS] Could not find any suitable endpoints on discovery server");
        UA_free(discoveryUrl);
        return;
    }
    char *endpointUrl = UaStringToPChar(&endpointRegister->endpointUrl);

    // Set up periodic registration
    auto* registerClient = new UaClient();
    bool status = AddPeriodicServerRegister(registerClient, endpointUrl, 10 * 60 * 1000, 500, NULL);
    if(!status) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,"[mDNS] Could not create periodic job for server register.");
        UA_free(endpointUrl);
        UA_free(discoveryUrl);
        delete registerClient;
        return;
    }

    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "[mDNS] Registered to %s", endpointUrl);
    UA_free(endpointUrl);
    UA_free(discoveryUrl);
}

UA_EndpointDescription* MyUaServer::GetRegisterEndpointFromServer(const char *discoveryServerUrl) {
    UaClient client;

    UA_EndpointDescription *endpointArray = NULL;
    size_t endpointArraySize = 0;

    UA_StatusCode retVal = UA_Client_getEndpoints(client.GetClient(),
                                                  discoveryServerUrl,
                                                  &endpointArraySize,
                                                  &endpointArray);
    if(retVal != UA_STATUSCODE_GOOD) {
        UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "[mDNS] GetEndpoints failed with %s", UA_StatusCode_name(retVal));
        return NULL;
    }

    UA_EndpointDescription *returnEndpoint = NULL;

    for(size_t i = 0; i < endpointArraySize; ++i) {
        if(!UA_String_equal(&endpointArray[i].securityPolicyUri, &UA_SECURITY_POLICY_NONE_URI))
            continue;

        returnEndpoint = UA_EndpointDescription_new();
        UA_EndpointDescription_copy(&endpointArray[i], returnEndpoint);
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "[mDNS] Using LDS endpoint with security None");
        break;
    }

    UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);

    return returnEndpoint;
}

std::string const MyUaServer::StorageCapability = "storage";
std::string const MyUaServer::TransportCapability = "transport";
std::string const MyUaServer::ActionCapability = "action";