#include "lip/app/opcua/server/lip_opcua_server_cfg_defs.h"
#if LIP_OPCUA_SERVER_ENABLE
#include "lip_opcua_server_svcset_session.h"
#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/app/opcua/server/lip_opcua_server_session.h"
#include "../../lip_opcua_server.h"



static t_lip_opcua_err f_session_create_parse_req(t_lip_proc_buf *data_buf, t_lip_opcua_session_create_params *session_params) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;

    /* ----------- clientDescription -> AppDescription ------------*/
    /* applicationUri */
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string(data_buf, NULL);
    }
    /* productUri */
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string(data_buf, NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        /* applicationName */
        err = lip_opcua_procbuf_get_loc_string(data_buf, NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        uint32_t appType;
        err = lip_opcua_procbuf_get_uint32(data_buf, &appType);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        /* gatewayServerUri */
        err = lip_opcua_procbuf_get_string(data_buf, NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        /* discoveryProfileUri */
        err = lip_opcua_procbuf_get_string(data_buf, NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        /* discoveryUrls */
        err = lip_opcua_procbuf_get_string_arr(data_buf, NULL);
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        /* serverUri */
        err = lip_opcua_procbuf_get_string(data_buf, NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string(data_buf, &session_params->epUrl);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string(data_buf, &session_params->sessionName);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string(data_buf, &session_params->nonce);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        /* Client sertificate */
        err = lip_opcua_procbuf_get_string(data_buf, NULL);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        double session_tout;
        err = lip_opcua_procbuf_get_double(data_buf, &session_tout);
        if (err == LIP_OPCUA_ERR_GOOD) {
            session_params->sessionTout = session_tout;
        }
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_uint32(data_buf, &session_params->maxRespSize);
    }

    return err;
}

static t_lip_opcua_err f_session_create_send_resp(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req,
                                                const t_lip_opcua_session_create_result *resp, 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_CREATE_SESSION_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_nodeid(resp_buf, &resp->session_id);
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_nodeid(resp_buf, &resp->token_id);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_double(resp_buf, resp->sessionTout);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            /* serverNonce */
            err = lip_opcua_procbuf_put_string(resp_buf, NULL);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            /* serverCertificate */
            err = lip_opcua_procbuf_put_string(resp_buf, NULL);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            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) {
            /* serverSoftwareCertificates (obsolete) */
            err = lip_opcua_procbuf_put_int32(resp_buf, -1);
        }


        /* signature data (alg + sign) */
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_string(resp_buf, NULL);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_string(resp_buf, NULL);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_int32(resp_buf, resp->maxReqSize);
        }

        if (err == LIP_OPCUA_ERR_GOOD) {
            lip_opcua_con_resp_finish(con, resp_buf);
        }
    }
    return err;
}


t_lip_opcua_err lip_opcua_server_procreq_session_create(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_lip_opcua_session_create_params session_params;
    session_params.sc = lip_opcua_con_sc(con);
    t_lip_opcua_err err = f_session_create_parse_req(data_buf, &session_params);
    if (err == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_session_create_result res;
        err = lip_opcua_session_create(&session_params, &res);
        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;
            }



            err = f_session_create_send_resp(con, req, &res, ep_descr_list, fnd_ep_cnt);

        }
    }
    return err;
}




static t_lip_opcua_err f_session_activate_parse_req(t_lip_proc_buf *data_buf, t_lip_opcua_session_activate_params *session_params) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    err = lip_opcua_procbuf_get_signature_data(data_buf, &session_params->clientSign);

    if (err == LIP_OPCUA_ERR_GOOD) {
        int32_t cleintCertCnt;
        err = lip_opcua_procbuf_get_int32(data_buf, &cleintCertCnt);
        for (int cert_idx = 0; (cert_idx < cleintCertCnt) && (err == LIP_OPCUA_ERR_GOOD); ++cert_idx) {
            /* certificateData */
            err = lip_opcua_procbuf_get_string(data_buf, NULL);
            if (err == LIP_OPCUA_ERR_GOOD) {
                /* signature */
                err = lip_opcua_procbuf_get_string(data_buf, NULL);
            }
        }
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_string_arr(data_buf, &session_params->localeIds);

    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_extobj_info ex_obj;
        err = lip_opcua_procbuf_get_extobj_info(data_buf, &ex_obj);
        if (err == LIP_OPCUA_ERR_GOOD) {
            if (ex_obj.body_len > 0) {
                t_lip_proc_buf ex_obj_buf;
                lip_procbuf_init(&ex_obj_buf, ex_obj.body, ex_obj.body_len);
                if (lip_opcua_nodeid_check_is_stdns_int(&ex_obj.type_id)) {
                    if (ex_obj.type_id.intval == LIP_OPCUA_NODEID_STD_ANONYMOUS_IDENTITY_TOKEN_BIN) {
                        session_params->userToken.policy.type = LIP_OPCUA_USER_TOKEN_TYPE_ANONYMOUS;
                        err = lip_opcua_procbuf_get_string(&ex_obj_buf, &session_params->userToken.policy.policyId);
                    } else if (ex_obj.type_id.intval == LIP_OPCUA_NODEID_STD_USERNAME_IDENTITY_TOKEN_BIN) {
                        session_params->userToken.policy.type = LIP_OPCUA_USER_TOKEN_TYPE_USERNAME;
                        err = lip_opcua_procbuf_get_string(&ex_obj_buf, &session_params->userToken.policy.policyId);
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            err = lip_opcua_procbuf_get_string(&ex_obj_buf, &session_params->userToken.username.name);
                        }
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            err = lip_opcua_procbuf_get_string(&ex_obj_buf, &session_params->userToken.username.passwd);
                        }
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            err = lip_opcua_procbuf_get_string(&ex_obj_buf, &session_params->userToken.username.encAlg);
                        }
                    } else {
                        err = LIP_OPCUA_ERR_BAD_IDENTITY_TOKEN_INVALID;
                    }
                } else {
                    err = LIP_OPCUA_ERR_BAD_IDENTITY_TOKEN_INVALID;
                }
            }
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            if (session_params->userToken.policy.type == LIP_OPCUA_USER_TOKEN_TYPE_ANONYMOUS) {

            } else if (session_params->userToken.policy.type == LIP_OPCUA_USER_TOKEN_TYPE_USERNAME) {
                /** @todo */
            } else {
                err = LIP_OPCUA_ERR_BAD_IDENTITY_TOKEN_INVALID;
            }
        }
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_signature_data(data_buf, &session_params->userTokenSign);
    }
    return err;
}



static t_lip_opcua_err f_session_activate_send_resp(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, const t_lip_opcua_string_ptr *nonce) {
    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_ACTIVATE_SESSION_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_string(resp_buf, nonce);
        if (err == LIP_OPCUA_ERR_GOOD) {
            /* results[] for clientSoftwareCertificates */
            err = lip_opcua_procbuf_put_int32(resp_buf, -1);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            /* diagnosticInfos[] */
            err = lip_opcua_procbuf_put_int32(resp_buf, -1);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            lip_opcua_con_resp_finish(con, resp_buf);
        }
    }
    return err;
}


t_lip_opcua_err lip_opcua_server_procreq_session_activate(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {    
    t_lip_opcua_session_activate_params session_params;
    session_params.sc = lip_opcua_con_sc(con);
    t_lip_opcua_err err = f_session_activate_parse_req(data_buf, &session_params);
    if (err == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_string_ptr nonce;
        err = lip_opcua_session_activate(lip_opcua_con_session(con), &session_params, &nonce);
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = f_session_activate_send_resp(con, req, &nonce);
        }
    }
    return err;
}



t_lip_opcua_err lip_opcua_server_procreq_session_close(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {    
    bool deleteSubs;
    t_lip_opcua_err err = lip_opcua_procbuf_get_bool(data_buf, &deleteSubs);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_session_close(lip_opcua_con_session(con), deleteSubs);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        static const t_lip_opcua_nodeid f_resp_node_id = LIP_OPCUA_NODEID_STDNS_INT(LIP_OPCUA_NODEID_STD_CLOSE_SESSION_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 {
            lip_opcua_con_resp_finish(con, resp_buf);
        }
    }
    return err;
}


#endif
