#include "lip/app/opcua/server/lip_opcua_server_cfg_defs.h"
#if LIP_OPCUA_SERVER_ENABLE
#include "lip_opcua_server_svcset_subscription.h"
#include "lip_opcua_server_svcset_session.h"
#include "lip/app/opcua/server/lip_opcua_server_con.h"
#include "lip/app/opcua/types/lip_opcua_types.h"
#include "lip/app/opcua/lip_opcua_std_nodeids.h"
#include "lip/app/opcua/server/lip_opcua_server_session.h"
#include "lip/app/opcua/server/monitems/lip_opcua_server_subscriptions.h"
#include "../../lip_opcua_server.h"




static t_lip_opcua_err f_subscription_create_parse_req(t_lip_proc_buf *data_buf, t_lip_opcua_subscription_params *params, bool *enabled) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_duration(data_buf, &params->pubInterval);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_counter(data_buf, &params->lifetimeCnt);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_counter(data_buf, &params->maxKeepAliveCnt);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_counter(data_buf, &params->maxNotificationPerPublish);
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_bool(data_buf, enabled);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_uint8(data_buf, &params->priority);
    }


    return err;
}

static t_lip_opcua_err f_subscription_create_end_resp(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req,
                                                const t_lip_opcua_subscription_params *params, t_lip_opcua_intid subId) {
    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_SUBSCRIPTION_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_intid(resp_buf, subId);
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_duration(resp_buf, params->pubInterval);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_counter(resp_buf, params->lifetimeCnt);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_put_counter(resp_buf, params->maxKeepAliveCnt);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            lip_opcua_con_resp_finish(con, resp_buf);
        }
    }
    return err;

}



t_lip_opcua_err lip_opcua_server_procreq_subscription_create(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    t_lip_opcua_subscription_params params;
    bool publish_en;
    err = f_subscription_create_parse_req(data_buf, &params, &publish_en);
    if (err == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_intid subId;
        err = lip_opcua_session_sub_create(lip_opcua_con_session(con), &params, publish_en, &subId);
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = f_subscription_create_end_resp(con, req, &params, subId);
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_server_procreq_subscription_modify(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;

    t_lip_opcua_intid sub_id;
    err = lip_opcua_procbuf_get_intid(data_buf, &sub_id);
    if (err == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_srvsub_ctx *sub;
        err = lip_opcua_session_sub_get(lip_opcua_con_session(con), sub_id, &sub);
        if (err == LIP_OPCUA_ERR_GOOD) {
            t_lip_opcua_subscription_params params;
            err = lip_opcua_procbuf_get_duration(data_buf, &params.pubInterval);
            if (err == LIP_OPCUA_ERR_GOOD) {
                err = lip_opcua_procbuf_get_counter(data_buf, &params.lifetimeCnt);
            }
            if (err == LIP_OPCUA_ERR_GOOD) {
                err = lip_opcua_procbuf_get_counter(data_buf, &params.maxKeepAliveCnt);
            }
            if (err == LIP_OPCUA_ERR_GOOD) {
                err = lip_opcua_procbuf_get_counter(data_buf, &params.maxNotificationPerPublish);
            }
            if (err == LIP_OPCUA_ERR_GOOD) {
                err = lip_opcua_procbuf_get_uint8(data_buf, &params.priority);
            }

            if (err == LIP_OPCUA_ERR_GOOD) {
                err = lip_opcua_server_sub_set_params(sub, &params);
            }

            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_MODIFY_SUBSCRIPTION_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_duration(resp_buf, params.pubInterval);
                    if (err == LIP_OPCUA_ERR_GOOD) {
                        err = lip_opcua_procbuf_put_counter(resp_buf, params.lifetimeCnt);
                    }
                    if (err == LIP_OPCUA_ERR_GOOD) {
                        err = lip_opcua_procbuf_put_counter(resp_buf, params.maxKeepAliveCnt);
                    }
                    if (err == LIP_OPCUA_ERR_GOOD) {
                        lip_opcua_con_resp_finish(con, resp_buf);
                    }
                }
            }
        }
    }
    return err;
}

t_lip_opcua_err lip_opcua_server_procreq_subscription_set_pubmode(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    t_lip_opcua_arrsize id_cnt;
    bool publish_en = false;
    err = lip_opcua_procbuf_get_bool(data_buf, &publish_en);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_arrsize(data_buf, &id_cnt);
    }
    if ((err == LIP_OPCUA_ERR_GOOD) && (id_cnt <= 0)) {
        err = LIP_OPCUA_ERR_BAD_NOTHING_TO_DO;
    }
    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_SET_PUBLISHING_MODE_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_arrsize(resp_buf, id_cnt);

            for (int32_t id_idx = 0; (id_idx < id_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++id_idx) {
                uint32_t sub_id;
                err = lip_opcua_procbuf_get_intid(data_buf, &sub_id);
                if (err == LIP_OPCUA_ERR_GOOD) {
                    t_lip_opcua_srvsub_ctx *sub;
                    t_lip_opcua_err op_res = lip_opcua_session_sub_get(lip_opcua_con_session(con), sub_id, &sub);
                    if (op_res == LIP_OPCUA_ERR_GOOD) {
                        op_res = lip_opcua_server_sub_set_publish_en(sub, publish_en);
                    }
                    err = lip_opcua_procbuf_put_status(resp_buf, op_res);
                }
            }

            if (err == LIP_OPCUA_ERR_GOOD) {
                /* Diagnostic Info */
                err = lip_opcua_procbuf_put_arrsize_null(resp_buf);
            }
            if (err == LIP_OPCUA_ERR_GOOD) {
                lip_opcua_con_resp_finish(con, resp_buf);
            }
        }
    }

    return err;
}

t_lip_opcua_err lip_opcua_server_procreq_subscription_publish(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    t_lip_opcua_arrsize acks_cnt;
    err = lip_opcua_procbuf_get_arrsize(data_buf, &acks_cnt);
    for (t_lip_opcua_arrsize ack_idx = 0; (ack_idx < acks_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++ack_idx) {
        t_lip_opcua_intid sub_id;
        t_lip_opcua_counter ack_seq_num;
        err = lip_opcua_procbuf_get_intid(data_buf, &sub_id);
        if (err == LIP_OPCUA_ERR_GOOD) {
            err = lip_opcua_procbuf_get_counter(data_buf, &ack_seq_num);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            /** @noto не поддерживаем retransmission queue */
        }
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_session_publish(lip_opcua_con_session(con), &req->resp);
    }

    return err;
}
t_lip_opcua_err lip_opcua_server_procreq_subscription_republish(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_BAD_SERVICE_UNSUPPORTED;

    return err;
}

t_lip_opcua_err lip_opcua_server_procreq_subscription_transfer(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_BAD_SERVICE_UNSUPPORTED;



    return err;
}

t_lip_opcua_err lip_opcua_server_procreq_subscription_delete(struct st_lip_opcua_con *con, const t_lip_opcua_req_info *req, t_lip_proc_buf *data_buf) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    t_lip_opcua_arrsize id_cnt;
    err = lip_opcua_procbuf_get_arrsize(data_buf, &id_cnt);
    if ((err == LIP_OPCUA_ERR_GOOD) && (id_cnt <= 0)) {
        err = LIP_OPCUA_ERR_BAD_NOTHING_TO_DO;
    }
    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_DELETE_SUBSCRIPTIONS_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_arrsize(resp_buf, id_cnt);

            for (int32_t id_idx = 0; (id_idx < id_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++id_idx) {
                uint32_t sub_id;
                err = lip_opcua_procbuf_get_intid(data_buf, &sub_id);
                if (err == LIP_OPCUA_ERR_GOOD) {
                    t_lip_opcua_err op_res = lip_opcua_session_sub_delete(lip_opcua_con_session(con), sub_id);
                    err = lip_opcua_procbuf_put_status(resp_buf, op_res);
                }
            }

            if (err == LIP_OPCUA_ERR_GOOD) {
                /* Diagnostic Info */
                err = lip_opcua_procbuf_put_arrsize_null(resp_buf);
            }
            if (err == LIP_OPCUA_ERR_GOOD) {
                lip_opcua_con_resp_finish(con, resp_buf);
            }
        }
    }

    return err;
}




t_lip_proc_buf *lip_opcua_server_subscription_pub_resp_prepare(struct st_lip_opcua_con *con, t_lip_opcua_req_resp_info *req, t_lip_opcua_intid subid,t_lip_proc_buf *more_notify_buf) {
    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_PUBLISH_RESPONSE_BIN);
    t_lip_proc_buf *resp_buf = lip_opcua_con_resp_prepare(con, req, &f_resp_node_id, LIP_OPCUA_ERR_GOOD);
    if (resp_buf) {
        err =  lip_opcua_procbuf_put_intid(resp_buf, subid);
        if (err == LIP_OPCUA_ERR_GOOD) {
            /* availableSequenceNumbers - не поддерживаем resend queue */
            err =  lip_opcua_procbuf_put_arrsize_null(resp_buf);
        }
        if (err == LIP_OPCUA_ERR_GOOD) {
            if (more_notify_buf) {
                *more_notify_buf = *resp_buf;
            }
            err =  lip_opcua_procbuf_put_bool(resp_buf, false);
        }

        /* делаем запас на 8 байт для записи конечного результат */
        resp_buf->end -= 8;
    }
    return (err == LIP_OPCUA_ERR_GOOD) ? resp_buf : NULL;
}

t_lip_opcua_err lip_opcua_server_subscription_pub_resp_finish(struct st_lip_opcua_con *con, t_lip_proc_buf *resp_buf) {
    /* восстановления запаса в 8 байт для записи конечного результата */
    resp_buf->end += 8;
    lip_opcua_procbuf_put_arrsize_null(resp_buf); /* results -> нет availableSequenceNumbers */
    lip_opcua_procbuf_put_arrsize_null(resp_buf); /* diagnosticInfos */
    lip_opcua_con_resp_finish(con, resp_buf);
    return LIP_OPCUA_ERR_GOOD;
}




#endif


