#include "lip/app/opcua/server/lip_opcua_server_cfg_defs.h"
#if LIP_OPCUA_SERVER_ENABLE
#include "lip_opcua_server_svcset_monitem.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_con.h"
#include "lip/app/opcua/server/lip_opcua_server_session.h"
#include "lip/app/opcua/server/monitems/lip_opcua_server_subscriptions.h"



static t_lip_opcua_err lip_opcua_procbuf_get_monitoring_params(t_lip_proc_buf *buf, t_lip_opcua_monitoring_params *params) {
    t_lip_opcua_err err = lip_opcua_procbuf_get_intid(buf, &params->client_handle);
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_duration(buf, &params->sampling_interval);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_extobj_info filter_extobj;

        params->filter.type = LIP_OPCUA_MONITORING_FILTER_TYPE_NONE;

        err = lip_opcua_procbuf_get_extobj_info(buf, &filter_extobj);
        if ((filter_extobj.body_len > 0) && lip_opcua_nodeid_check_is_stdns_int(&filter_extobj.type_id)) {
            t_lip_proc_buf filter_buf;
            lip_procbuf_init(&filter_buf, filter_extobj.body, filter_extobj.body_len);

            if (filter_extobj.type_id.intval == LIP_OPCUA_NODEID_STD_DATA_CHANGE_FILTER_BIN) {

                err = lip_opcua_procbuf_get_data_change_filter(&filter_buf, &params->filter.data_change);
                if (err == LIP_OPCUA_ERR_GOOD) {

                }
            } else if (filter_extobj.type_id.intval == LIP_OPCUA_NODEID_STD_EVENT_FILTER_BIN) {
                /** @todo */
            } else if (filter_extobj.type_id.intval == LIP_OPCUA_NODEID_STD_AGGREGATE_FILTER_BIN) {
                /** @todo */
            }

        }

    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_counter(buf, &params->queue_size);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_get_bool(buf, &params->discart_oldest);
    }
    return err;
}

static t_lip_opcua_err f_put_revised_monitoring_params(t_lip_proc_buf *resp_buf, const t_lip_opcua_monitoring_params *mon_params) {
    t_lip_opcua_err err =  LIP_OPCUA_ERR_GOOD;
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_duration(resp_buf, mon_params->sampling_interval);
    }
    if (err == LIP_OPCUA_ERR_GOOD) {
        err = lip_opcua_procbuf_put_counter(resp_buf, mon_params->queue_size);
    }
    /** @todo - filterResult */
    if (err == LIP_OPCUA_ERR_GOOD) {
        t_lip_opcua_extobj_info filter_obj_info;
        filter_obj_info.type_id.type = LIP_OPCUA_NODEID_TYPE_INVALID;
        filter_obj_info.body_len = 0;
        err = lip_opcua_procbuf_put_extobj(resp_buf, &filter_obj_info);
    }
    return err;
}

t_lip_opcua_err lip_opcua_server_svcset_monitem_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_intid sub_id;
    t_lip_opcua_ret_tstmp_types retTstmps;


    err = lip_opcua_procbuf_get_intid(data_buf, &sub_id);
    if (err == LIP_OPCUA_ERR_GOOD) {
        uint32_t val;
        err = lip_opcua_procbuf_get_enum(data_buf, &val);
        if (err == LIP_OPCUA_ERR_GOOD) {
            retTstmps = val;
        }
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        struct st_lip_opcua_srvsub_ctx *sub;
        struct st_lip_opcua_session_ctx *session = lip_opcua_con_session(con);
        err = lip_opcua_session_sub_get(session, sub_id, &sub);
        if (err == LIP_OPCUA_ERR_GOOD) {
            int32_t nodes_cnt;
            err = lip_opcua_procbuf_get_arrsize(data_buf, &nodes_cnt);
            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_CREATE_MONITORED_ITEMS_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 {
                    t_lclock_ticks req_time = lclock_get_ticks();

                    err = lip_opcua_procbuf_put_arrsize(resp_buf, nodes_cnt);

                    for (int32_t node_idx = 0; (node_idx < nodes_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++node_idx) {
                        t_lip_opcua_monitem_create_params params;
                        params.tstmps = retTstmps;
                        params.req_time = req_time;

                        err = lip_opcua_procbuf_get_readvalueid(data_buf, &params.rd_value_id);
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            t_lip_opcua_enumval val;
                            err = lip_opcua_procbuf_get_enum(data_buf, &val);
                            if ((err == LIP_OPCUA_ERR_GOOD) &&
                                 ((val == LIP_OPCUA_MONMODE_DISABLED)
                                    || (val ==  LIP_OPCUA_MONMODE_SAMPLING)
                                    || (val ==  LIP_OPCUA_MONMODE_REPORTING))) {
                                params.mon_mode = val;
                            } else {
                                err = LIP_OPCUA_ERR_BAD_MONITORING_MODE_INVALID;
                            }
                        }
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            err = lip_opcua_procbuf_get_monitoring_params(data_buf, &params.mon_params);
                        }

                        if (err == LIP_OPCUA_ERR_GOOD) {
                            t_lip_opcua_intid monid = 0;

                            t_lip_opcua_err procerr = lip_opcua_server_sub_monitem_create(sub, session, &params, &monid);

                            err = lip_opcua_procbuf_put_status(resp_buf, procerr);
                            if (err == LIP_OPCUA_ERR_GOOD) {
                                err = lip_opcua_procbuf_put_intid(resp_buf, monid);
                            }
                            if (err == LIP_OPCUA_ERR_GOOD) {
                                err = f_put_revised_monitoring_params(resp_buf, &params.mon_params);
                            }

                        }
                    } /* for (node_idx) */

                    if (err == LIP_OPCUA_ERR_GOOD) {
                        /* Diagnostic info */
                        err = lip_opcua_procbuf_put_arrsize(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_svcset_monitem_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;
    t_lip_opcua_ret_tstmp_types retTstmps;


    err = lip_opcua_procbuf_get_intid(data_buf, &sub_id);
    if (err == LIP_OPCUA_ERR_GOOD) {
        uint32_t val;
        err = lip_opcua_procbuf_get_enum(data_buf, &val);
        if (err == LIP_OPCUA_ERR_GOOD) {
            retTstmps = val;
        }
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        struct st_lip_opcua_srvsub_ctx *sub;
        struct st_lip_opcua_session_ctx *session = lip_opcua_con_session(con);
        err = lip_opcua_session_sub_get(session, sub_id, &sub);
        if (err == LIP_OPCUA_ERR_GOOD) {
            int32_t nodes_cnt;
            err = lip_opcua_procbuf_get_arrsize(data_buf, &nodes_cnt);
            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_MONITORED_ITEMS_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, nodes_cnt);
                    t_lclock_ticks req_time = lclock_get_ticks();

                    for (int32_t node_idx = 0; (node_idx < nodes_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++node_idx) {
                        t_lip_opcua_monitoring_params mon_params;
                        t_lip_opcua_intid monitem_id;
                        err = lip_opcua_procbuf_get_intid(data_buf, &monitem_id);
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            err = lip_opcua_procbuf_get_monitoring_params(data_buf, &mon_params);
                        }
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            t_lip_opcua_monitem_ctx *monctx = NULL;
                            t_lip_opcua_err item_err = lip_opcua_server_sub_monitem_get(sub, monitem_id,  &monctx);
                            if (item_err == LIP_OPCUA_ERR_GOOD) {
                                item_err = lip_opcua_server_monitem_set_monitoring_params(sub, monctx, &mon_params, retTstmps, req_time);
                            }
                            err = lip_opcua_procbuf_put_status(resp_buf, item_err);
                            if (err == LIP_OPCUA_ERR_GOOD) {
                                err = f_put_revised_monitoring_params(resp_buf, &mon_params);
                            }
                        }
                    }

                    if (err == LIP_OPCUA_ERR_GOOD) {
                        /* Diagnostic info */
                        err = lip_opcua_procbuf_put_arrsize(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_svcset_monitem_set_mode(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;
    t_lip_opcua_monmode mon_mode;


    err = lip_opcua_procbuf_get_intid(data_buf, &sub_id);
    if (err == LIP_OPCUA_ERR_GOOD) {
        uint32_t val;
        err = lip_opcua_procbuf_get_enum(data_buf, &val);
        if (err == LIP_OPCUA_ERR_GOOD) {
            if ((val == LIP_OPCUA_MONMODE_DISABLED)
                || (val ==  LIP_OPCUA_MONMODE_SAMPLING)
                || (val ==  LIP_OPCUA_MONMODE_REPORTING)) {
                mon_mode = val;
            } else {
                err = LIP_OPCUA_ERR_BAD_MONITORING_MODE_INVALID;
            }
        }
    }

    if (err == LIP_OPCUA_ERR_GOOD) {
        struct st_lip_opcua_srvsub_ctx *sub;
        struct st_lip_opcua_session_ctx *session = lip_opcua_con_session(con);
        err = lip_opcua_session_sub_get(session, sub_id, &sub);
        if (err == LIP_OPCUA_ERR_GOOD) {
            int32_t nodes_cnt;
            err = lip_opcua_procbuf_get_arrsize(data_buf, &nodes_cnt);
            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_MONITORING_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 {
                    t_lclock_ticks req_time = lclock_get_ticks();
                    err = lip_opcua_procbuf_put_arrsize(resp_buf, nodes_cnt);

                    for (int32_t node_idx = 0; (node_idx < nodes_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++node_idx) {
                        t_lip_opcua_intid monitem_id;
                        err = lip_opcua_procbuf_get_intid(data_buf, &monitem_id);
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            t_lip_opcua_monitem_ctx *monctx = NULL;
                            t_lip_opcua_err item_err = lip_opcua_server_sub_monitem_get(sub, monitem_id,  &monctx);
                            if (item_err == LIP_OPCUA_ERR_GOOD) {
                                item_err = lip_opcua_server_monitem_set_monmode(monctx, mon_mode, req_time);
                            }
                            err = lip_opcua_procbuf_put_status(resp_buf, item_err);
                        }
                    }

                    if (err == LIP_OPCUA_ERR_GOOD) {
                        /* Diagnostic info */
                        err = lip_opcua_procbuf_put_arrsize(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_svcset_monitem_set_trig(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_svcset_monitem_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_intid sub_id;

    err = lip_opcua_procbuf_get_intid(data_buf, &sub_id);

    if (err == LIP_OPCUA_ERR_GOOD) {
        struct st_lip_opcua_srvsub_ctx *sub;
        struct st_lip_opcua_session_ctx *session = lip_opcua_con_session(con);
        err = lip_opcua_session_sub_get(session, sub_id, &sub);
        if (err == LIP_OPCUA_ERR_GOOD) {
            int32_t nodes_cnt;
            err = lip_opcua_procbuf_get_arrsize(data_buf, &nodes_cnt);
            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_MONITORED_ITEMS_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, nodes_cnt);

                    for (int32_t node_idx = 0; (node_idx < nodes_cnt) && (err == LIP_OPCUA_ERR_GOOD); ++node_idx) {
                        t_lip_opcua_intid monitem_id;
                        err = lip_opcua_procbuf_get_intid(data_buf, &monitem_id);
                        if (err == LIP_OPCUA_ERR_GOOD) {
                            t_lip_opcua_err procerr = lip_opcua_server_sub_monitem_delete(sub, monitem_id);
                            err = lip_opcua_procbuf_put_status(resp_buf, procerr);
                        }
                    }


                    if (err == LIP_OPCUA_ERR_GOOD) {
                        /* Diagnostic info */
                        err = lip_opcua_procbuf_put_arrsize(resp_buf, -1);
                    }

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

    return err;
}


#endif
