#include "nv_infer_server_manager.hpp"

// #define NVDS_USER_OBJECT_META_LANDMARKS_AND_SOURCE_ID
// (nvds_get_user_meta_type("NVIDIA.NVINFER.USER_META"))
#define NVDS_USER_OBJECT_META_LANDMARKS_AND_SOURCE_ID \
    (nvds_get_user_meta_type(const_cast<gchar *>("NVIDIA.NVINFER.USER_META")))

#define MAX_DISPLAY_LEN 64
#define PGIE_CLASS_ID_PERSON 0
#define IMPRECISE_FACE_CLASS_ID 1
#define PGIE_DETECTED_CLASS_NUM 1
#define BODY_COMPONENT_ID 1
#define IMPRECISE_FACE_COMPONENT_ID 2

gint NvInferServerManager::frame_number = 0;
unsigned int NvInferServerManager::PGIE_NET_WIDTH = 1;
unsigned int NvInferServerManager::PGIE_NET_HEIGHT = 1;
unsigned int NvInferServerManager::MUXER_OUTPUT_WIDTH = 1;
unsigned int NvInferServerManager::MUXER_OUTPUT_HEIGHT = 1;
guint NvInferServerManager::use_device_mem = 0;
float NvInferServerManager::threshold_body_detection = 0;
unsigned int NvInferServerManager::nvds_lib_major_version = NVDS_VERSION_MAJOR;
unsigned int NvInferServerManager::nvds_lib_minor_version = NVDS_VERSION_MINOR;

const gchar pgie_class_str[PGIE_DETECTED_CLASS_NUM][32] = {"Person_NVINFER"};
const gchar imprecise_face_str[PGIE_DETECTED_CLASS_NUM][32] = {
    "ImpreciseFace_NVINFER"};

/* nvds_lib_major_version and nvds_lib_minor_version is the version number of
 * deepstream sdk */

unsigned int nvds_lib_major_version = NVDS_VERSION_MAJOR;
unsigned int nvds_lib_minor_version = NVDS_VERSION_MINOR;

NvInferServerManager::NvInferServerManager() {
    const auto &config = ConfigManager::get_instance().get_config();
    pgie_batch_size = config["pgie_batch_size"];
    inferserver_pgie_config_file =
        config["inferserver_pgie_config_file"].get<std::string>();
    PGIE_NET_WIDTH = config["PGIE_NET_WIDTH"];
    PGIE_NET_HEIGHT = config["PGIE_NET_HEIGHT"];
    MUXER_OUTPUT_WIDTH = config["MUXER_OUTPUT_WIDTH"];
    MUXER_OUTPUT_HEIGHT = config["MUXER_OUTPUT_HEIGHT"];
    threshold_body_detection = config["threshold_body_detection"];
}

bool NvInferServerManager::create_nv_infer_server(int num_sources) {
    /* Configure the nvinferserver element using the config file. */
    primary_detector =
        gst_element_factory_make("nvinferserver", "primary-nvinference-engine");
    g_object_set(G_OBJECT(primary_detector), "config-file-path",
                 inferserver_pgie_config_file.c_str(), "unique-id", 1, NULL);

    /* Override the batch-size set in the config file with the number of
     * sources. */
    g_object_get(G_OBJECT(primary_detector), "batch-size", &pgie_batch_size,
                 NULL);
    if (pgie_batch_size != num_sources) {
        g_printerr(
            "WARNING: Overriding infer-config batch-size (%d) with number of "
            "sources (%d)\n",
            pgie_batch_size, num_sources);
        g_object_set(G_OBJECT(primary_detector), "batch-size", num_sources,
                     NULL);
    }
    if (!primary_detector) {
        g_printerr("Could not create primary detector. Exiting.\n");
        return false;
    }
    return true;
}

// Probe function to inspect NvDsObjectMeta

// GstPadProbeReturn NvInferServerManager::osd_sink_pad_buffer_probe(
//     GstPad *pad, GstPadProbeInfo *info, gpointer user_data) {
//     (void)pad;
//     (void)user_data;
//     GstBuffer *buf = (GstBuffer *)info->data;

//     // Retrieve batch metadata from buffer
//     NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta(buf);
//     if (!batch_meta) {
//         std::cerr << "No batch metadata found\n";
//         return GST_PAD_PROBE_OK;
//     }

//     // probe sees the frame metadata (NvDsFrameMeta) —
//     // but no object metadata (NvDsObjectMeta) was attached to that frame.

//     for (NvDsMetaList *l_frame = batch_meta->frame_meta_list; l_frame !=
//     NULL;
//          l_frame = l_frame->next) {
//         NvDsFrameMeta *frame_meta = (NvDsFrameMeta *)(l_frame->data);
//         // std::cout << "Frame number: " << frame_meta->frame_num <<
//         std::endl;
//         // if (frame_meta->obj_meta_list == NULL) {
//         //     std::cout << "  ⚠️ No object metadata for this frame.\n";
//         // }

//         for (NvDsMetaList *l_obj = frame_meta->obj_meta_list; l_obj != NULL;
//              l_obj = l_obj->next) {
//             NvDsObjectMeta *obj_meta = (NvDsObjectMeta *)(l_obj->data);

//             std::cout << "  Object ID: " << obj_meta->object_id << std::endl;
//             std::cout << "  Class ID: " << obj_meta->class_id << std::endl;
//             std::cout << "  Label: "
//                       << (obj_meta->obj_label ? obj_meta->obj_label : "N/A")
//                       << std::endl;
//             std::cout << "  BBox: x=" << obj_meta->rect_params.left
//                       << " y=" << obj_meta->rect_params.top
//                       << " w=" << obj_meta->rect_params.width
//                       << " h=" << obj_meta->rect_params.height << std::endl;
//         }
//     }
//     return GST_PAD_PROBE_OK;
// }

// // Attach probe to a pad in the pipeline
// void NvInferServerManager::attach_probe_to_element(GstElement *nvosd) {
//     GstPad *sink_pad = gst_element_get_static_pad(nvosd, "src");
//     if (!sink_pad) {
//         std::cerr << "Unable to get nvosd sink pad\n";
//         return;
//     }

//     gst_pad_add_probe(sink_pad, GST_PAD_PROBE_TYPE_BUFFER,
//                       osd_sink_pad_buffer_probe_new, NULL, NULL);
//     gst_object_unref(sink_pad);
// }

// /* This is the buffer probe function that we have registered on the sink pad
//  * of the OSD element. All the infer elements in the pipeline shall attach
//  * their metadata to the GstBuffer, here we will iterate & process the
//  metadata
//  * forex: class ids to strings, counting of class_id objects etc. */
// GstPadProbeReturn NvInferServerManager::osd_sink_pad_buffer_probe_new(
//     GstPad *pad, GstPadProbeInfo *info, gpointer u_data) {
//     (void)pad;
//     (void)u_data;
//     GstBuffer *buf = (GstBuffer *)info->data;
//     guint num_rects = 0;
//     guint person_count = 0;
//     NvDsObjectMeta *obj_meta = NULL;
//     NvDsMetaList *l_frame = NULL;
//     NvDsMetaList *l_obj = NULL;
//     NvDsDisplayMeta *display_meta = NULL;

//     NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta(buf);

//     for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
//          l_frame = l_frame->next) {
//         NvDsFrameMeta *frame_meta = (NvDsFrameMeta *)(l_frame->data);
//         int offset = 0;
//         for (l_obj = frame_meta->obj_meta_list; l_obj != NULL;
//              l_obj = l_obj->next) {
//             obj_meta = (NvDsObjectMeta *)(l_obj->data);
//             if (obj_meta->class_id == PGIE_CLASS_ID_PERSON) {
//                 person_count++;
//                 num_rects++;
//                 std::cout << "In OSD sink "
//                           << "x = " << obj_meta->rect_params.left
//                           << " y = " << obj_meta->rect_params.top
//                           << " w = " << obj_meta->rect_params.width
//                           << " h = " << obj_meta->rect_params.height
//                           << " score = " << obj_meta->confidence
//                           << "  Object ID: " << obj_meta->object_id
//                           << std::endl;
//             }
//         }
//         display_meta = nvds_acquire_display_meta_from_pool(batch_meta);
//         NvOSD_TextParams *txt_params = &display_meta->text_params[0];
//         display_meta->num_labels = 1;
//         txt_params->display_text = (gchar *)g_malloc0(MAX_DISPLAY_LEN);
//         offset = snprintf(txt_params->display_text, MAX_DISPLAY_LEN,
//                           "Person = %d ", person_count);
//         (void)offset;

//         /* Now set the offsets where the string should appear */
//         txt_params->x_offset = 10;
//         txt_params->y_offset = 12;

//         /* Font , font-color and font-size */
//         txt_params->font_params.font_name = (gchar *)"Serif";
//         txt_params->font_params.font_size = 10;
//         txt_params->font_params.font_color.red = 1.0;
//         txt_params->font_params.font_color.green = 1.0;
//         txt_params->font_params.font_color.blue = 1.0;
//         txt_params->font_params.font_color.alpha = 1.0;

//         /* Text background color */
//         txt_params->set_bg_clr = 1;
//         txt_params->text_bg_clr.red = 0.0;
//         txt_params->text_bg_clr.green = 0.0;
//         txt_params->text_bg_clr.blue = 0.0;
//         txt_params->text_bg_clr.alpha = 1.0;

//         nvds_add_display_meta_to_frame(frame_meta, display_meta);
//     }
//     g_print(
//         "In OSD sink "
//         "Frame Number = %d "
//         "Person Count = %d\n",
//         frame_number, person_count);

//     frame_number++;
//     return GST_PAD_PROBE_OK;
// }

/* This is the buffer probe function that we have registered on the src pad
 * of the PGIE's next queue element. PGIE element in the pipeline shall attach
 * its NvDsInferTensorMeta to each frame metadata on GstBuffer, here we will
 * iterate & parse the tensor data to get detection bounding boxes. The result
 * would be attached as object-meta(NvDsObjectMeta) into the same frame
 * metadata.
 */
GstPadProbeReturn NvInferServerManager::pgie_pad_buffer_probe(
    GstPad *pad, GstPadProbeInfo *info, gpointer u_data) {
    (void)pad;
    gboolean *use_new_mux = (gboolean *)u_data;
    guint stream_width = 0, stream_height = 0;

    static NvDsInferNetworkInfo networkInfo{PGIE_NET_WIDTH, PGIE_NET_HEIGHT, 3};
    NvDsBatchMeta *batch_meta =
        gst_buffer_get_nvds_batch_meta(GST_BUFFER(info->data));

    /* Iterate each frame metadata in batch */
    for (NvDsMetaList *l_frame = batch_meta->frame_meta_list; l_frame != NULL;
         l_frame = l_frame->next) {
        NvDsFrameMeta *frame_meta = (NvDsFrameMeta *)l_frame->data;

        // std::cout << "frame number = " << frame_meta->frame_num
        //             << " frame id = " << frame_meta->source_id << std::endl;

        // to solve track not showing up issue
        nvds_acquire_meta_lock(batch_meta);
        frame_meta->bInferDone = TRUE;
        nvds_release_meta_lock(batch_meta);
        if (*use_new_mux) {
            stream_width = frame_meta->source_frame_width;
            stream_height = frame_meta->source_frame_height;
        } else {
            stream_width = MUXER_OUTPUT_WIDTH;
            stream_height = MUXER_OUTPUT_HEIGHT;
        }
        (void)stream_height;
        (void)stream_width;
        uint detected_persons = 0;

        /* Iterate user metadata in frames to search PGIE's tensor metadata */
        for (NvDsMetaList *l_user = frame_meta->frame_user_meta_list;
             l_user != NULL; l_user = l_user->next) {
            NvDsUserMeta *user_meta = (NvDsUserMeta *)l_user->data;
            if (user_meta->base_meta.meta_type != NVDSINFER_TENSOR_OUTPUT_META)
                continue;
            detected_persons = extract_tensor_metadata(user_meta, networkInfo,
                                                       batch_meta, frame_meta);
        }

        NvDsDisplayMeta *display_meta = NULL;
        display_meta = nvds_acquire_display_meta_from_pool(batch_meta);
        NvOSD_TextParams *txt_params = &display_meta->text_params[0];
        display_meta->num_labels = 1;
        txt_params->display_text = (gchar *)g_malloc0(MAX_DISPLAY_LEN);
        int offset = 0;
        offset = snprintf(txt_params->display_text, MAX_DISPLAY_LEN,
                          "Person_NVInfer = %d ", detected_persons);
        (void)offset;

        /* Now set the offsets where the string should appear */
        txt_params->x_offset = 10;
        txt_params->y_offset = 12;

        /* Font , font-color and font-size */
        txt_params->font_params.font_name = (gchar *)"Serif";
        txt_params->font_params.font_size = 10;
        txt_params->font_params.font_color.red = 1.0;
        txt_params->font_params.font_color.green = 1.0;
        txt_params->font_params.font_color.blue = 1.0;
        txt_params->font_params.font_color.alpha = 1.0;

        /* Text background color */
        txt_params->set_bg_clr = 1;
        txt_params->text_bg_clr.red = 0.0;
        txt_params->text_bg_clr.green = 0.0;
        txt_params->text_bg_clr.blue = 0.0;
        txt_params->text_bg_clr.alpha = 1.0;

        nvds_add_display_meta_to_frame(frame_meta, display_meta);
    }
    // use_device_mem = 1 - use_device_mem;
    return GST_PAD_PROBE_OK;
}

uint NvInferServerManager::extract_tensor_metadata(
    NvDsUserMeta *user_meta, NvDsInferNetworkInfo networkInfo,
    NvDsBatchMeta *batch_meta, NvDsFrameMeta *frame_meta) {
    /* convert to tensor metadata */
    NvDsInferTensorMeta *meta =
        (NvDsInferTensorMeta *)user_meta->user_meta_data;
    for (unsigned int i = 0; i < meta->num_output_layers; i++) {
        NvDsInferLayerInfo *info = &meta->output_layers_info[i];
        info->buffer = meta->out_buf_ptrs_host[i];
        if (use_device_mem && meta->out_buf_ptrs_dev[i]) {
            cudaMemcpy(meta->out_buf_ptrs_host[i], meta->out_buf_ptrs_dev[i],
                       info->inferDims.numElements * 4, cudaMemcpyDeviceToHost);
        }
    }
    /* Parse output tensor and fill detection results into objectList.
     */
    std::vector<NvDsInferLayerInfo> outputLayersInfo(
        meta->output_layers_info,
        meta->output_layers_info + meta->num_output_layers);
#if NVDS_VERSION_MAJOR >= 5
    if (nvds_lib_major_version >= 5) {
        if (meta->network_info.width != networkInfo.width ||
            meta->network_info.height != networkInfo.height ||
            meta->network_info.channels != networkInfo.channels) {
            g_error("failed to check pgie network info\n");
        }
    }
#endif

    float *outputBuffer = (float *)outputLayersInfo[0].buffer;
    (void)outputBuffer;
    // NvDsInferDims dims = outputLayersInfo[0].inferDims;

    for (size_t jkl = 0; jkl < outputLayersInfo.size(); jkl++) {
        const NvDsInferLayerInfo &layer = outputLayersInfo[jkl];

        unsigned int numDims = layer.inferDims.numDims;
        unsigned int numElements = layer.inferDims.numElements;
        (void)numElements;
        (void)numDims;

        // std::cout << "Layer " << jkl << " (" << layer.layerName <<
        // "):\n"; std::cout << "  Num Dims: " << numDims << "\n";
        // std::cout << "  Num Elements: " << numElements << "\n";
        // std::cout << "  Dims: [";
        // for (unsigned int mno = 0; mno < numDims; ++mno) {
        //     std::cout << layer.inferDims.d[mno];
        //     // layer.inferDims.d[0] = 100;
        //     // layer.inferDims.d[1] = 57;
        //     if (mno < numDims - 1)
        //        std::cout << ", ";
        // }
        // std::cout << "]\n";
    }
    const NvDsInferLayerInfo &layer = outputLayersInfo[0];  // or loop over all

    uint detected_persons = 0;
    float *data = static_cast<float *>(layer.buffer);
    for (unsigned int jkl = 0; jkl < 100;
         jkl++) {  // maximum 100 persons for each frame
        if (data[jkl * 57 + 4] > threshold_body_detection) {
            detected_persons++;
        }
    }
    update_frame_with_face_body_meta(detected_persons, batch_meta, data,
                                     frame_meta);
    return detected_persons;
}

void NvInferServerManager::update_frame_with_face_body_meta(
    uint detected_persons, NvDsBatchMeta *batch_meta, float *data,
    NvDsFrameMeta *frame_meta) {
    for (uint index = 0; index < detected_persons; index++) {
        // imprecise_face_obj_meta is the imprecise face
        NvDsObjectMeta *imprecise_face_obj_meta =
            nvds_acquire_obj_meta_from_pool(batch_meta);

        // meta->unique_id in NvDsInferTensorMeta
        // This is the unique ID of the inference component (PGIE/SGIE)
        // that produced the tensor output. It comes directly from the
        // unique-id property in the [property] section of your
        // config_infer_primary.txt or config_infer_secondary.txt. A
        // pipeline can have multiple inference components (1 PGIE +
        // many SGIEs). Each inference element might output tensors
        // (NvDsInferTensorMeta) that are attached as user metadata.
        // unique_id lets you know which inference element the tensor
        // belongs to. meta->unique_id → The unique-id you assigned in
        // the config for the inference component that produced these
        // tensor outputs.

        strncpy(imprecise_face_obj_meta->obj_label, imprecise_face_str[0],
                sizeof(imprecise_face_obj_meta->obj_label) - 1);
        imprecise_face_obj_meta
            ->obj_label[sizeof(imprecise_face_obj_meta->obj_label) - 1] =
            '\0';  // Ensure null-termination
        imprecise_face_obj_meta->unique_component_id =
            IMPRECISE_FACE_COMPONENT_ID;  // meta->unique_id

        // imprecise_face_obj_meta->unique_component_id
        // Meaning: The ID of the component (PGIE, SGIE, Tracker,
        // Custom, etc.) that generated this metadata. Source: Assigned
        // by DeepStream when metadata is attached by a specific element
        // in the pipeline. Example: PGIE might be assigned
        // unique_component_id = 1 SGIE might be assigned
        // unique_component_id = 2 Tracker usually doesn’t overwrite
        // PGIE’s class_id but may extend metadata (like assigning
        // object_id). You normally don’t set this manually. DeepStream
        // sets it when a particular component (PGIE/SGIE) attaches
        // object metadata. You might override it only if you’re
        // injecting your own custom objects into the pipeline and need
        // to differentiate your component from PGIE/SGIE.

        imprecise_face_obj_meta->confidence = data[index * 57 + 4];
        // imprecise_face_obj_meta->object_id = UNTRACKED_OBJECT_ID;
        imprecise_face_obj_meta->class_id =
            IMPRECISE_FACE_CLASS_ID;  // 0 for body detection
        NvOSD_RectParams &rect_params_imprecise_face =
            imprecise_face_obj_meta->rect_params;
        NvOSD_TextParams &text_params_imprecise_face =
            imprecise_face_obj_meta->text_params;
        /* Assign bounding box coordinates. */
        rect_params_imprecise_face.left =
            (data[index * 57 + 0] * MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
        rect_params_imprecise_face.top =
            (data[index * 57 + 1] * MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);
        Point2D left_down_shoulder =
            find_left_down_corner_shoulder(data, index);
        rect_params_imprecise_face.width =
            ((left_down_shoulder.x - data[index * 57 + 0]) *
             MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
        rect_params_imprecise_face.height =
            ((left_down_shoulder.y - data[index * 57 + 1]) *
             MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);

        /* Border of width 3. */
        rect_params_imprecise_face.border_width = 3;
        rect_params_imprecise_face.has_bg_color = 0;
        rect_params_imprecise_face.border_color =
            NvOSD_ColorParams{0, 0, 1, 1};  // Blue box
        /* display_text requires heap allocated memory. */
        text_params_imprecise_face.display_text =
            g_strdup(imprecise_face_str[0]);  // g_strdup(pgie_class_str[0]);
        /* Display text above the left top corner of the object. */
        text_params_imprecise_face.x_offset = rect_params_imprecise_face.left;
        text_params_imprecise_face.y_offset = rect_params_imprecise_face.top;
        /* Set black background for the text. */
        text_params_imprecise_face.set_bg_clr = 1;
        text_params_imprecise_face.text_bg_clr = NvOSD_ColorParams{0, 0, 0, 1};
        /* Font face, size and color. */
        text_params_imprecise_face.font_params.font_name = (gchar *)"Serif";
        text_params_imprecise_face.font_params.font_size = 11;
        text_params_imprecise_face.font_params.font_color =
            NvOSD_ColorParams{1, 1, 1, 1};
        // adding landmarks to imprecise_face_obj_meta as user_meta
        NvDsUserMeta *um1 = nvds_acquire_user_meta_from_pool(batch_meta);
        assert(um1 != NULL);
        um1->user_meta_data =
            set_metadata_ptr(&(data[index * 57]));  // Add landmarks here
        um1->base_meta.meta_type =
            NVDS_USER_OBJECT_META_LANDMARKS_AND_SOURCE_ID;
        um1->base_meta.copy_func = (NvDsMetaCopyFunc)copy_user_meta;
        um1->base_meta.release_func = (NvDsMetaReleaseFunc)release_user_meta;
        nvds_add_user_meta_to_obj(imprecise_face_obj_meta, um1);
        nvds_add_obj_meta_to_frame(frame_meta, imprecise_face_obj_meta, NULL);

        NvDsObjectMeta *body_obj_meta =
            nvds_acquire_obj_meta_from_pool(batch_meta);

        strncpy(body_obj_meta->obj_label, pgie_class_str[0],
                sizeof(body_obj_meta->obj_label) - 1);
        body_obj_meta->obj_label[sizeof(body_obj_meta->obj_label) - 1] =
            '\0';  // Ensure null-termination
        body_obj_meta->unique_component_id =
            BODY_COMPONENT_ID;  // meta->unique_id;
        body_obj_meta->confidence = data[index * 57 + 4];
        // body_obj_meta->object_id = UNTRACKED_OBJECT_ID;
        body_obj_meta->class_id = PGIE_CLASS_ID_PERSON;  // 0 for body detection
        NvOSD_RectParams &rect_params_body = body_obj_meta->rect_params;
        NvOSD_TextParams &text_params_body = body_obj_meta->text_params;
        /* Assign bounding box coordinates. */
        rect_params_body.left =
            (data[index * 57 + 0] * MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
        rect_params_body.top =
            (data[index * 57 + 1] * MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);
        rect_params_body.width =
            ((data[index * 57 + 2] - data[index * 57 + 0]) *
             MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
        rect_params_body.height =
            ((data[index * 57 + 3] - data[index * 57 + 1]) *
             MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);

        /* Border of width 3. */
        rect_params_body.border_width = 3;
        rect_params_body.has_bg_color = 0;
        rect_params_body.border_color =
            NvOSD_ColorParams{1, 0, 0, 1};  // Red box
        /* display_text requires heap allocated memory. */
        text_params_body.display_text = g_strdup(pgie_class_str[0]);
        // text_params.display_text = g_strdup_printf("ImpreciseFace %lu",
        //     face_obj->object_id);
        /* Display text above the left top corner of the object. */
        text_params_body.x_offset = rect_params_body.left - 30;
        text_params_body.y_offset = rect_params_body.top - 30;
        /* Set black background for the text. */
        text_params_body.set_bg_clr = 1;
        text_params_body.text_bg_clr = NvOSD_ColorParams{0, 0, 0, 1};
        /* Font face, size and color. */
        text_params_body.font_params.font_name = (gchar *)"Serif";
        text_params_body.font_params.font_size = 11;
        text_params_body.font_params.font_color = NvOSD_ColorParams{1, 1, 1, 1};
        // // adding landmarks to body_obj_meta as user_meta
        // NvDsUserMeta *um1 =
        //     nvds_acquire_user_meta_from_pool(batch_meta);
        // um1->user_meta_data = set_metadata_ptr(
        //     &(data[index * 57]));  // Add landmarks here
        // um1->base_meta.meta_type =
        //     NVDS_USER_OBJECT_META_LANDMARKS_AND_SOURCE_ID;
        // um1->base_meta.copy_func = (NvDsMetaCopyFunc)copy_user_meta;
        // um1->base_meta.release_func =
        //     (NvDsMetaReleaseFunc)release_user_meta;
        // nvds_add_user_meta_to_obj(body_obj_meta, um1);
        nvds_add_obj_meta_to_frame(frame_meta, body_obj_meta, NULL);
    }
}

NvInferServerManager::Point2D
NvInferServerManager::find_left_down_corner_shoulder(float *data, uint index) {
    Point2D left_down_shoulder;
    if (data[index * 57 + 21] > data[index * 57 + 24]) {
        left_down_shoulder.x = data[index * 57 + 21];
        left_down_shoulder.y = data[index * 57 + 22];
    } else {
        left_down_shoulder.x = data[index * 57 + 24];
        left_down_shoulder.y = data[index * 57 + 25];
    }
    return left_down_shoulder;
}

// add custom infromation to metadata by: set_metadata_ptr, copy_user_meta,
// release_user_meta
void *NvInferServerManager::set_metadata_ptr(float *arr) {
    int i = 0;
    float *user_metadata = (float *)g_malloc0(57 * sizeof(float));

    for (i = 0; i < 57; i++) {
        user_metadata[i] = arr[i];
    }
    // user_metadata[51] = source_id;
    return (void *)user_metadata;
}

gpointer NvInferServerManager::copy_user_meta(gpointer data,
                                              gpointer user_data) {
    (void)user_data;
    NvDsUserMeta *user_meta = (NvDsUserMeta *)data;
    gfloat *src_user_metadata = (gfloat *)user_meta->user_meta_data;
    gfloat *dst_user_metadata = (gfloat *)g_malloc0(57 * sizeof(gfloat));
    memcpy(dst_user_metadata, src_user_metadata, 57 * sizeof(gfloat));
    return (gpointer)dst_user_metadata;
}

void NvInferServerManager::release_user_meta(gpointer data,
                                             gpointer user_data) {
    (void)user_data;
    NvDsUserMeta *user_meta = (NvDsUserMeta *)data;
    if (user_meta->user_meta_data) {
        g_free(user_meta->user_meta_data);
        user_meta->user_meta_data = NULL;
    }
}