#include "lip/app/opcua/server/lip_opcua_server_cfg_defs.h"
#if LIP_OPCUA_SERVER_ENABLE
#include "lip_opcua_server_svcset_discovery.h"
#include "lip/app/opcua/server/lip_opcua_server_con.h"
#include "lip/app/opcua/types/lip_opcua_nodeid.h"
#include "lip/app/opcua/lip_opcua_std_nodeids.h"
#include "../../lip_opcua_server.h"

typedef struct  {
    t_lip_opcua_string_ptr epUrl;
    t_lip_opcua_string_ptr_array localeIds;
    t_lip_opcua_string_ptr_array reqServers;
} t_find_servers_req_params;


static t_lip_opcua_err f_find_servers_parse_req(t_lip_proc_buf *data_buf, t_find_servers_req_params *req_params) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    err = lip_opcua_procbuf_get_string(data_buf, &req_params->epUrl);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string_arr(data_buf, &req_params->localeIds);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string_arr(data_buf, &req_params->reqServers);
    }
    return err;
}

static t_lip_opcua_err f_put_app_descr(t_lip_proc_buf *resp_buf, const t_lip_opcua_app_description *app_descr, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_string(resp_buf, &app_descr->appUri);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_string(resp_buf, &app_descr->productUri);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_loc_string_from_list(resp_buf, &app_descr->name, session);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_uint32(resp_buf, app_descr->type);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        /* gatewayServerUri */
        err = lip_opcua_procbuf_put_string(resp_buf, NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        /* discoveryProfileUri */
        err = lip_opcua_procbuf_put_string(resp_buf, NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_string_ptr_array discoveryUrls;
        discoveryUrls.cnt = 0;
        for (int ep_idx = 0; ep_idx < app_descr->ep_cnt; ++ep_idx) {
            const t_lip_opcua_ep_description *ep = &app_descr->eps[ep_idx];
            if (ep->is_discovery_ep) {
                discoveryUrls.strlist[discoveryUrls.cnt++] = ep->epUrl;
            }
        }
        err = lip_opcua_procbuf_put_string_arr(resp_buf, &discoveryUrls);
    }
    return err;
}


static t_lip_opcua_err f_find_servers_send_resp(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req,
                                                const t_lip_opcua_app_description *app_descr_list, int app_descr_cnt) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    static const t_lip_opcua_nodeid f_resp_node_id = LIP_OPCUA_NODEID_STDNS_INT(LIP_OPCUA_NODEID_STD_FIND_SERVERS_RESPONSE_BIN);
    t_lip_proc_buf *resp_buf = lip_opcua_con_resp_prepare(con, &req->resp, &f_resp_node_id, LIP_OPCUA_ERR_GOOD);
    if (!resp_buf) {
        err = LIP_OPCUA_ERR_BAD_TCP_SERVER_TOO_BUSY;
    } else {
        err = lip_opcua_procbuf_put_int32(resp_buf, app_descr_cnt);
        for (int app_idx = 0; (app_idx < app_descr_cnt) && (err == LIP_OPCUA_ERR_GOOD); app_idx++) {
            const t_lip_opcua_app_description *app_descr = &app_descr_list[app_idx];
            f_put_app_descr(resp_buf, app_descr, lip_opcua_con_session(con));
            if (err == LIP_OPCUA_ERR_GOOD) {
                lip_opcua_con_resp_finish(con, resp_buf);
            }
        }
    }
    return err;
}



t_lip_opcua_err lip_opcua_server_procreq_find_servers(
    struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_find_servers_req_params params;
    t_lip_opcua_err err =  f_find_servers_parse_req(data_buf, &params);

    if (err == LIP_OPCUA_ERR_GOOD) {
        const t_lip_opcua_app_description *appDescr = lip_opcua_server_app_descr();
        int app_cnt = 1;
        err = f_find_servers_send_resp(con, req, appDescr, app_cnt);
    }


    return err;
}



typedef struct  {
    t_lip_opcua_string_ptr epUrl;
    t_lip_opcua_string_ptr_array localeIds;
    t_lip_opcua_string_ptr_array profileUrls;
} t_get_eps_req_params;

static t_lip_opcua_err f_get_eps_parse_req(t_lip_proc_buf *data_buf, t_get_eps_req_params *req_params) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    err = lip_opcua_procbuf_get_string(data_buf, &req_params->epUrl);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string_arr(data_buf, &req_params->localeIds);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string_arr(data_buf, &req_params->profileUrls);
    }
    return err;
}

t_lip_opcua_err lip_procbuf_put_ep_list(t_lip_proc_buf *resp_buf, const t_lip_opcua_ep_description **ep_descr_list, int ep_descr_cnt, struct st_lip_opcua_session_ctx *session) {
    t_lip_opcua_err err = lip_opcua_procbuf_put_int32(resp_buf, ep_descr_cnt);
    for (int ep_idx = 0; (ep_idx < ep_descr_cnt) && (err == LIP_OPCUA_ERR_GOOD); ep_idx++) {
        const t_lip_opcua_ep_description *ep = ep_descr_list[ep_idx];
        err = lip_opcua_procbuf_put_string(resp_buf, &ep->epUrl);
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = f_put_app_descr(resp_buf, ep->app, session);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            /* serverCertificate */
            err = lip_opcua_procbuf_put_string(resp_buf, NULL);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_uint32(resp_buf, ep->securityMode);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_string(resp_buf, &ep->securityPolicyUri);
        }

        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_int32(resp_buf, ep->userTokensCnt);
            for (int token_idx = 0; (token_idx < ep->userTokensCnt) && (err == LIP_OPCUA_ERR_GOOD); ++token_idx) {
                const t_lip_opcua_user_token_policy *token = &ep->userTokens[token_idx];
                err = lip_opcua_procbuf_put_string(resp_buf, &token->policyId);
                if (err == LIP_OPCUA_ERR_GOOD) {
                    err = lip_opcua_procbuf_put_uint32(resp_buf, token->type);
                }
                if (err == LIP_OPCUA_ERR_GOOD) {
                    /* issuedTokenType */
                    err = lip_opcua_procbuf_put_string(resp_buf, NULL);
                }
                if (err == LIP_OPCUA_ERR_GOOD) {
                    /* issuerEndpointUrl */
                    err = lip_opcua_procbuf_put_string(resp_buf, NULL);
                }
                if (err == LIP_OPCUA_ERR_GOOD) {
                    /* securityPolicyUri */
                    err = lip_opcua_procbuf_put_string(resp_buf, NULL);
                }
            }
        }

        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_string(resp_buf, &ep->transportProfileUri);
        }

        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_uint8(resp_buf, ep->securityLevel);
        }

    }
    return err;
}

static t_lip_opcua_err f_get_eps_send_resp(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req,
                                                const t_lip_opcua_ep_description **ep_descr_list, int ep_descr_cnt) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    static const t_lip_opcua_nodeid f_resp_node_id = LIP_OPCUA_NODEID_STDNS_INT(LIP_OPCUA_NODEID_STD_GET_ENDPOINTS_RESPONSE_BIN);
    t_lip_proc_buf *resp_buf = lip_opcua_con_resp_prepare(con, &req->resp, &f_resp_node_id, LIP_OPCUA_ERR_GOOD);
    if (!resp_buf) {
        err = LIP_OPCUA_ERR_BAD_TCP_SERVER_TOO_BUSY;
    } else {
        err = lip_procbuf_put_ep_list(resp_buf, ep_descr_list, ep_descr_cnt, lip_opcua_con_session(con));
        if (err == LIP_OPCUA_ERR_GOOD) {
            lip_opcua_con_resp_finish(con, resp_buf);
        }
    }
    return err;
}


t_lip_opcua_err lip_opcua_server_procreq_get_endpoints(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_get_eps_req_params params;
    t_lip_opcua_err err =  f_get_eps_parse_req(data_buf, &params);

    if (err == LIP_OPCUA_ERR_GOOD) {
        const t_lip_opcua_ep_description *ep_descr_list[LIP_OPCUA_SERVER_EP_MAX_CNT] = {NULL};
        int ep_cnt = lip_opcua_server_eps_cnt();
        int fnd_ep_cnt = 0;
        for (int ep_idx = 0; ep_idx < ep_cnt; ++ep_idx) {
            const t_lip_opcua_ep_description *ep = lip_opcua_server_ep(ep_idx);
            /** @todo check ep */
            ep_descr_list[fnd_ep_cnt++] = ep;
        }

        if (err == LIP_OPCUA_ERR_GOOD) {
            err = f_get_eps_send_resp(con, req, ep_descr_list, fnd_ep_cnt);
        }
    }
    return err;
}

#endif
