#include "lip_opcua_server_cfg_defs.h"
#if LIP_OPCUA_SERVER_ENABLE
#include "lip_opcua_server.h"
#include "lip_opcua_server_con.h"
#include "lip_opcua_server_session.h"
#include "lip_opcua_server_private.h"
#include "lip_opcua_server_time.h"
#include "lip_opcua_sc.h"
#include "tree/lip_opcua_server_tree.h"
#include "lip/misc/lip_misc_string.h"

static bool f_opcua_server_en;
static t_lip_opcua_app_description f_app_descr;
static t_lip_opcua_ep_description  f_ep_descr[LIP_OPCUA_SERVER_EP_MAX_CNT];
static t_lip_opcua_user_token_policy f_tokens;

static const char *anon_token_type = "Anonymous";
static const char *f_security_policy_none = LIP_OPCUA_SECPOLICY_URL_NONE;
static const char *f_transport_uri_uasc_uabinary = "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary";

static uint16_t f_srv_port = LIP_OPCUA_SERVER_TCP_DEFAULT_PORT;
static char f_ep_ipv4_name_str[32];




static void f_rebuild_ipv4_ep_url(void) {
    const uint8_t *ipv4_addr = lip_ipv4_cur_addr();
    if (ipv4_addr) {
        strcpy(f_ep_ipv4_name_str, "opc.tcp://");
        char *str = &f_ep_ipv4_name_str[10];
        str = lip_str_put_ipv4_addr(str, lip_ipv4_cur_addr());
        *str++ = ':';
        str = lip_str_put_uint16(str, f_srv_port);
        lip_opcua_string_init(&f_ep_descr[0].epUrl, f_ep_ipv4_name_str, str - f_ep_ipv4_name_str);
    }
}

uint16_t lip_opcua_server_port(void) {
    return f_srv_port;
}

void lip_opcua_server_set_port(uint16_t port) {
    if (port != f_srv_port) {
        f_srv_port = port;
        f_rebuild_ipv4_ep_url();
        /** @todo: закрыть подключения по старому порту */
    }
}


void lip_opcua_server_set_app_uri(const char *uri, int32_t len) {
    lip_opcua_string_init(&f_app_descr.appUri, uri, len);
    lip_opcua_server_set_server_uri(uri, len);
}
void lip_opcua_server_set_app_name(uint8_t locale_num, const char *name, int32_t len) {
    if (locale_num < LIP_OPCUA_LOCALE_CNT) {
        lip_opcua_string_init(&f_app_descr.name.strlist[locale_num], name, len);
    }
}
void lip_opcua_server_set_product_uri(const char *uri, int32_t len) {
    lip_opcua_string_init(&f_app_descr.productUri, uri, len);
}

const t_lip_opcua_app_description *lip_opcua_server_app_descr(void) {
    return &f_app_descr;
}

int lip_opcua_server_eps_cnt(void) {
    return 1;
}
const t_lip_opcua_ep_description *lip_opcua_server_ep(int idx) {
    return &f_ep_descr[idx];
}


void lip_opcua_server_ip_addr_changed(t_lip_ip_type type) {
    if (type == LIP_IPTYPE_IPv4) {
        f_rebuild_ipv4_ep_url();
    }
}


void lip_opcua_server_init(void) {
    lip_opcua_server_time_init();

    t_lip_opcua_user_token_policy *token = &f_tokens;
    token->type = LIP_OPCUA_USER_TOKEN_TYPE_ANONYMOUS;
    lip_opcua_string_init(&token->policyId, anon_token_type, -1);

    t_lip_opcua_ep_description *ep = &f_ep_descr[0];


    ep->is_discovery_ep = true;
    /** @todo build dynamicly from hostname (ip addr or mdns) and port */
    //lip_opcua_string_init(&ep->epUrl, "opc.tcp://localhost:4840");
    ep->app = &f_app_descr;
    ep->securityMode = LIP_OPCUA_MSG_SECMODE_NONE;
    ep->securityLevel = 1;
    lip_opcua_string_init(&ep->securityPolicyUri, f_security_policy_none, -1);
    lip_opcua_string_init(&ep->transportProfileUri, f_transport_uri_uasc_uabinary, -1);



    ep->userTokensCnt = 1;
    ep->userTokens = &f_tokens;

    f_app_descr.type = LIP_OPCUA_APPTYPE_SERVER;
    /** @todo сделать функции для установки appUri, productUri и appname) */
    lip_opcua_string_init(&f_app_descr.appUri, NULL, -1);
    lip_opcua_string_init(&f_app_descr.productUri, NULL, -1);
    f_app_descr.ep_cnt = 1;
    f_app_descr.eps = ep;
    lip_opcua_string_init(&f_app_descr.name.strlist[0], NULL, -1);

    lip_opcua_server_tree_init();
    lip_opcua_server_con_init();
    lip_opcua_scs_init();
    lip_opcua_sessions_init();
    lip_opcua_server_monitem_init();

    f_opcua_server_en = true;
}
void lip_opcua_server_pull(void) {
    if (f_opcua_server_en) {
        lip_opcua_server_con_pull();
        lip_opcua_scs_pull();
        lip_opcua_sessions_pull();
    }
}
void lip_opcua_server_close(void) {
    lip_opcua_sessions_close();
    lip_opcua_scs_close();
    lip_opcua_server_con_close();
    f_opcua_server_en = false;
}

void lip_opcua_server_enable(void) {
    if (!f_opcua_server_en) {
        lip_opcua_server_init();
    }
}
void lip_opcua_server_disable(void) {
    if (f_opcua_server_en) {
        lip_opcua_server_close();
    }
}

bool lip_opcua_server_is_enabled(void) {
    return f_opcua_server_en;
}

#endif
