#include "face_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 PGIE_DETECTED_CLASS_NUM 1
#define SGIE_DETECTED_CLASS_NUM 1
#define BODY_COMPONENT_ID 1
#define IMPRECISE_FACE_COMPONENT_ID 2
#define FINAL_FACE_COMPONENT_ID 3

gint FaceNvInferServerManager::frame_number = 0;
unsigned int FaceNvInferServerManager::FACE_NET_WIDTH = 1;
unsigned int FaceNvInferServerManager::FACE_NET_HEIGHT = 1;
unsigned int FaceNvInferServerManager::MUXER_OUTPUT_WIDTH = 1;
unsigned int FaceNvInferServerManager::MUXER_OUTPUT_HEIGHT = 1;
guint FaceNvInferServerManager::use_device_mem = 1;
float FaceNvInferServerManager::threshold_face_detection = 0;
unsigned int FaceNvInferServerManager::nvds_lib_major_version =
    NVDS_VERSION_MAJOR;
unsigned int FaceNvInferServerManager::nvds_lib_minor_version =
    NVDS_VERSION_MINOR;

const gchar pgie_class_str[PGIE_DETECTED_CLASS_NUM][32] = {"Person_SGIE"};
const gchar sgie_class_str[SGIE_DETECTED_CLASS_NUM][32] = {"PreciseFace_SGIE"};
int high_confidence_faces = 0;

std::vector<FaceNvInferServerManager::FACE_BODY>
    FaceNvInferServerManager::face_body_list;

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

FaceNvInferServerManager::FaceNvInferServerManager() {
    const auto &config = ConfigManager::get_instance().get_config();
    face_batch_size = config["pgie_batch_size"];
    inferserver_face_config_file =
        config["inferserver_face_config_file"].get<std::string>();
    FACE_NET_WIDTH = config["FACE_NET_WIDTH"];
    FACE_NET_HEIGHT = config["FACE_NET_HEIGHT"];
    // FACE_NET_WIDTH = config["PGIE_NET_WIDTH"];
    // FACE_NET_HEIGHT = config["PGIE_NET_HEIGHT"];
    MUXER_OUTPUT_WIDTH = config["MUXER_OUTPUT_WIDTH"];
    MUXER_OUTPUT_HEIGHT = config["MUXER_OUTPUT_HEIGHT"];
    threshold_face_detection = config["threshold_face_detection"];
    compression_coefficient =
        static_cast<float>(config["compression_coefficient"]) * 100;
}

bool FaceNvInferServerManager::create_face_nv_infer_server(int num_sources) {
    /* Configure the nvinferserver element using the config file. */
    face_detector = gst_element_factory_make("nvinferserver",
                                             "secondary-nvinference-engine");
    g_object_set(G_OBJECT(face_detector), "config-file-path",
                 inferserver_face_config_file.c_str(), "unique-id", 3, NULL);

    /* Override the batch-size set in the config file with the number of
     * sources. */
    g_object_get(G_OBJECT(face_detector), "batch-size", &face_batch_size, NULL);
    if (face_batch_size != num_sources) {
        g_printerr(
            "WARNING: Overriding infer-config batch-size (%d) with number of "
            "sources (%d)\n",
            face_batch_size, num_sources);
        g_object_set(G_OBJECT(face_detector), "batch-size", num_sources, NULL);
    }
    if (!face_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;
// }

// 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;
// }

// 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 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 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 FaceNvInferServerManager::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;
//         // 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;

//         // float source_id = (float)frame_meta->source_id;
//         /* 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;

//             /* 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

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

//             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 += 4) {  // 100 persons for each frame
//                 if (data[jkl * 57 + 4] > threshold_body_detection) {
//                     detected_persons++;
//                     // std::cout
//                     //     << "nvinferserver first for x = " << data[jkl * 57
//                     +
//                     //     0]
//                     //     << " y = " << data[jkl * 57 + 1]
//                     //     << " w = " << data[jkl * 57 + 2]
//                     //     << " h = " << data[jkl * 57 + 3]
//                     //     << " score = " << data[jkl * 57 + 4] << std::endl;
//                     for (unsigned int mno = 0; mno < 57; ++mno) {
//                         float value = data[jkl * 57 + mno];
//                         (void)value;
//                         // std::cout << "data[" << jkl << "][" << mno
//                         //           << "] = " << value << std::endl;
//                     }
//                 }
//             }

//             for (uint index = 0; index < detected_persons; index++) {
//                 NvDsObjectMeta *obj_meta =
//                     nvds_acquire_obj_meta_from_pool(batch_meta);
//                 obj_meta->unique_component_id = meta->unique_id;
//                 obj_meta->confidence = data[index * 57 + 4];
//                 // obj_meta->object_id = UNTRACKED_OBJECT_ID;
//                 obj_meta->class_id = 0;
//                 NvOSD_RectParams &rect_params = obj_meta->rect_params;
//                 NvOSD_TextParams &text_params = obj_meta->text_params;
//                 /* Assign bounding box coordinates. */
//                 rect_params.left = int(data[index * 57 + 0] *
//                                        MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
//                 rect_params.top = int(data[index * 57 + 1] *
//                                       MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);
//                 rect_params.width =
//                     int((data[index * 57 + 2] - data[index * 57 + 0]) *
//                         MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
//                 rect_params.height =
//                     int((data[index * 57 + 3] - data[index * 57 + 1]) *
//                         MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);

//                 // std::cout << "nvinferserver second for x = " <<
//                 // rect_params.left
//                 //           << " y = " << rect_params.top
//                 //           << " w = " << rect_params.width
//                 //           << " h = " << rect_params.height
//                 //           << " score = " << obj_meta->confidence <<
//                 //           std::endl;

//                 /* Border of width 3. */
//                 rect_params.border_width = 3;
//                 rect_params.has_bg_color = 0;
//                 rect_params.border_color = NvOSD_ColorParams{1, 0, 0, 1};
//                 /* display_text requires heap allocated memory. */
//                 text_params.display_text = g_strdup(pgie_class_str[0]);
//                 /* Display text above the left top corner of the object. */
//                 text_params.x_offset = rect_params.left;
//                 text_params.y_offset = rect_params.top - 10;
//                 /* Set black background for the text. */
//                 text_params.set_bg_clr = 1;
//                 text_params.text_bg_clr = NvOSD_ColorParams{0, 0, 0, 1};
//                 /* Font face, size and color. */
//                 text_params.font_params.font_name = (gchar *)"Serif";
//                 text_params.font_params.font_size = 11;
//                 text_params.font_params.font_color =
//                     NvOSD_ColorParams{1, 1, 1, 1};
//                 // adding landmarks to 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(obj_meta, um1);
//                 nvds_add_obj_meta_to_frame(frame_meta, obj_meta, NULL);
//             }
//         }
//     }
//     // use_device_mem = 1 - use_device_mem;
//     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 FaceNvInferServerManager::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;
//         // 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;

//         // float source_id = (float)frame_meta->source_id;
//         /* 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;

//             /* 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

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

//             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 += 4) {  // 100 persons for each frame
//                 if (data[jkl * 57 + 4] > threshold_body_detection) {
//                     detected_persons++;
//                     // std::cout
//                     //     << "nvinferserver first for x = " << data[jkl * 57
//                     +
//                     //     0]
//                     //     << " y = " << data[jkl * 57 + 1]
//                     //     << " w = " << data[jkl * 57 + 2]
//                     //     << " h = " << data[jkl * 57 + 3]
//                     //     << " score = " << data[jkl * 57 + 4] << std::endl;
//                     for (unsigned int mno = 0; mno < 57; ++mno) {
//                         float value = data[jkl * 57 + mno];
//                         (void)value;
//                         // std::cout << "data[" << jkl << "][" << mno
//                         //           << "] = " << value << std::endl;
//                     }
//                 }
//             }

//             for (uint index = 0; index < detected_persons; index++) {
//                 NvDsObjectMeta *obj_meta =
//                     nvds_acquire_obj_meta_from_pool(batch_meta);
//                 obj_meta->unique_component_id = meta->unique_id;
//                 obj_meta->confidence = data[index * 57 + 4];
//                 // obj_meta->object_id = UNTRACKED_OBJECT_ID;
//                 obj_meta->class_id = 0;
//                 NvOSD_RectParams &rect_params = obj_meta->rect_params;
//                 NvOSD_TextParams &text_params = obj_meta->text_params;
//                 /* Assign bounding box coordinates. */
//                 rect_params.left = int(data[index * 57 + 0] *
//                                        MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
//                 rect_params.top = int(data[index * 57 + 1] *
//                                       MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);
//                 rect_params.width =
//                     int((data[index * 57 + 2] - data[index * 57 + 0]) *
//                         MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
//                 rect_params.height =
//                     int((data[index * 57 + 3] - data[index * 57 + 1]) *
//                         MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);

//                 // std::cout << "nvinferserver second for x = " <<
//                 // rect_params.left
//                 //           << " y = " << rect_params.top
//                 //           << " w = " << rect_params.width
//                 //           << " h = " << rect_params.height
//                 //           << " score = " << obj_meta->confidence <<
//                 //           std::endl;

//                 /* Border of width 3. */
//                 rect_params.border_width = 3;
//                 rect_params.has_bg_color = 0;
//                 rect_params.border_color = NvOSD_ColorParams{1, 0, 0, 1};
//                 /* display_text requires heap allocated memory. */
//                 text_params.display_text = g_strdup(pgie_class_str[0]);
//                 /* Display text above the left top corner of the object. */
//                 text_params.x_offset = rect_params.left;
//                 text_params.y_offset = rect_params.top - 10;
//                 /* Set black background for the text. */
//                 text_params.set_bg_clr = 1;
//                 text_params.text_bg_clr = NvOSD_ColorParams{0, 0, 0, 1};
//                 /* Font face, size and color. */
//                 text_params.font_params.font_name = (gchar *)"Serif";
//                 text_params.font_params.font_size = 11;
//                 text_params.font_params.font_color =
//                     NvOSD_ColorParams#include
//                     "face_nv_infer_server_manager.hpp"

/* 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 FaceNvInferServerManager::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;
//         // 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;

//         // float source_id = (float)frame_meta->source_id;
//         /* 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;

//             /* 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

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

//             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 += 4) {  // 100 persons for each frame
//                 if (data[jkl * 57 + 4] > threshold_body_detection) {
//                     detected_persons++;
//                     // std::cout
//                     //     << "nvinferserver first for x = " << data[jkl * 57
//                     +
//                     //     0]
//                     //     << " y = " << data[jkl * 57 + 1]
//                     //     << " w = " << data[jkl * 57 + 2]
//                     //     << " h = " << data[jkl * 57 + 3]
//                     //     << " score = " << data[jkl * 57 + 4] << std::endl;
//                     for (unsigned int mno = 0; mno < 57; ++mno) {
//                         float value = data[jkl * 57 + mno];
//                         (void)value;
//                         // std::cout << "data[" << jkl << "][" << mno
//                         //           << "] = " << value << std::endl;
//                     }
//                 }
//             }

//             for (uint index = 0; index < detected_persons; index++) {
//                 NvDsObjectMeta *obj_meta =
//                     nvds_acquire_obj_meta_from_pool(batch_meta);
//                 obj_meta->unique_component_id = meta->unique_id;
//                 obj_meta->confidence = data[index * 57 + 4];
//                 // obj_meta->object_id = UNTRACKED_OBJECT_ID;
//                 obj_meta->class_id = 0;
//                 NvOSD_RectParams &rect_params = obj_meta->rect_params;
//                 NvOSD_TextParams &text_params = obj_meta->text_params;
//                 /* Assign bounding box coordinates. */
//                 rect_params.left = int(data[index * 57 + 0] *
//                                        MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
//                 rect_params.top = int(data[index * 57 + 1] *
//                                       MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);
//                 rect_params.width =
//                     int((data[index * 57 + 2] - data[index * 57 + 0]) *
//                         MUXER_OUTPUT_WIDTH / PGIE_NET_WIDTH);
//                 rect_params.height =
//                     int((data[index * 57 + 3] - data[index * 57 + 1]) *
//                         MUXER_OUTPUT_HEIGHT / PGIE_NET_HEIGHT);

//                 // std::cout << "nvinferserver second for x = " <<
//                 // rect_params.left
//                 //           << " y = " << rect_params.top
//                 //           << " w = " << rect_params.width
//                 //           << " h = " << rect_params.height
//                 //           << " score = " << obj_meta->confidence <<
//                 //           std::endl;

//                 /* Border of width 3. */
//                 rect_params.border_width = 3;
//                 rect_params.has_bg_color = 0;
//                 rect_params.border_color = NvOSD_ColorParams{1, 0, 0, 1};
//                 /* display_text requires heap allocated memory. */
//                 text_params.display_text = g_strdup(pgie_class_str[0]);
//                 /* Display text above the left top corner of the object. */
//                 text_params.x_offset = rect_params.left;
//                 text_params.y_offset = rect_params.top - 10;
//                 /* Set black background for the text. */
//                 text_params.set_bg_clr = 1;
//                 text_params.text_bg_clr = NvOSD_ColorParams{0, 0, 0, 1};
//                 /* Font face, size and color. */
//                 text_params.font_params.font_name = (gchar *)"Serif";
//                 text_params.font_params.font_size = 11;
//                 text_params.font_params.font_color =
//                     NvOSD_ColorParams{1, 1, 1, 1};
//                 // adding landmarks to 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(obj_meta, um1);
//                 nvds_add_obj_meta_to_frame(frame_meta, obj_meta, NULL);
//             }
//         }
//     }
//     // use_device_mem = 1 - use_device_mem;
//     return GST_PAD_PROBE_OK;
// }

float FaceNvInferServerManager::numpy_clip(float value, float min_val,
                                           float max_val) {
    return std::clamp(value, min_val, max_val);
}

NvOSD_RectParams *FaceNvInferServerManager::allign_postprocess(
    NvOSD_RectParams &parent_bbox, float *face_location) {
    int target_w = 160;
    int target_h = 160;
    float x0 = parent_bbox.left;
    float y0 = parent_bbox.top;
    float w0 = parent_bbox.width;
    float h0 = parent_bbox.height;
    // std::cout << "x = " << x0
    //         << " y = " << y0
    //         << " w = " << w0
    //         << " h = " << h0
    //         << std::endl;
    float scale = std::min(target_w / w0, target_h / h0);
    int new_w = int(w0 * scale);
    int new_h = int(h0 * scale);
    int pad_w = target_w - new_w;
    int pad_h = target_h - new_h;

    // std::cout << "scale = " << scale
    //         << " new_w = " << new_w
    //         << " new_h = " << new_h
    //         << " pad_w = " << pad_w
    //         << " pad_h = " << pad_h
    //         << std::endl;
    int left = (int)pad_w / 2;
    // int right = pad_w - left;
    int top = (int)pad_h / 2;
    // int bottom = pad_h - top;
    // int _pad =(left, top);
    // int _scale = scale;
    // std::cout << "left = " << left
    //         << " top = " << top
    //         << std::endl;

    float x1n = numpy_clip(face_location[0], 0.0f, 1.0f);
    float y1n = numpy_clip(face_location[1], 0.0f, 1.0f);
    float x2n = numpy_clip(face_location[2], 0.0f, 1.0f);
    float y2n = numpy_clip(face_location[3], 0.0f, 1.0f);

    // std::cout << "x1n = " << x1n
    //         << " y1n = " << y1n
    //         << " x2n = " << x2n
    //         << " y2n = " << y2n
    //         << std::endl;

    float x1_p = x1n * target_w;
    float y1_p = y1n * target_h;
    float x2_p = x2n * target_w;
    float y2_p = y2n * target_h;
    // std::cout << "x1_p = " << x1_p
    //         << " y1_p = " << y1_p
    //         << " x2_p = " << x2_p
    //         << " y2_p = " << y2_p
    //         << std::endl;

    int pad_x = left;
    int pad_y = top;
    // std::cout << "pad_x = " << pad_x
    //         << " pad_y = " << pad_y
    //         << std::endl;
    float x1 = (x1_p - pad_x) / scale;
    float y1 = (y1_p - pad_y) / scale;
    float x2 = (x2_p - pad_x) / scale;
    float y2 = (y2_p - pad_y) / scale;
    // std::cout << "x1 = " << x1
    //         << " y1 = " << y1
    //         << " x2 = " << x2
    //         << " y2 = " << y2
    //         << std::endl;

    x1 += x0;
    y1 += y0;
    x2 += x0;
    y2 += y0;
    // std::cout << "x1 = " << x1
    //         << " y1 = " << y1
    //         << " x2 = " << x2
    //         << " y2 = " << y2
    //         << std::endl;

    NvOSD_RectParams *updated_bbox = new NvOSD_RectParams();
    updated_bbox->left = x1;
    updated_bbox->top = y1;
    updated_bbox->width = x2 - x1;
    updated_bbox->height = y2 - y1;
    // std::cout << "x = " << updated_bbox->left
    //         << " y = " << updated_bbox->top
    //         << " w = " << updated_bbox->width
    //         << " h = " << updated_bbox->height
    //         << std::endl;
    // box = [int(x1), int(y1), int(x2), int(y2)];
    // std::quick_exit(0);
    return updated_bbox;
}

void FaceNvInferServerManager::add_face_body(int object_id, float face_score) {
    bool find = false;
    for (std::vector<FACE_BODY>::iterator iter = face_body_list.begin();
         iter != face_body_list.end(); iter++) {
        if (((*iter).object_id == object_id)) {
            find = true;
            if (((*iter).face_score > face_score)) {
                // std::cout<<(*iter).face_score;
                (*iter).face_score = face_score;
                // std::cout<<" "<<(*iter).face_score<<std::endl;
                return;
            }
        }
    }
    if (!find) {
        FACE_BODY current_face_id;
        current_face_id.face_score = face_score;
        current_face_id.object_id = object_id;
        face_body_list.push_back(current_face_id);
        std::cout << " size face_body_list = " << face_body_list.size()
                  << std::endl;
    }
    return;
}

void FaceNvInferServerManager::encode_objects_attach_meta(
    gpointer ctx, NvBufSurface *ip_surf, NvDsFrameMeta *frame_meta,
    NvDsObjectMeta *obj_meta) {
    NvDsObjEncUsrArgs objData = {0};
    /* To be set by user */
    objData.saveImg = FaceNvInferServerManager::save_img;
    objData.attachUsrMeta = FaceNvInferServerManager::attach_user_meta;
    /* Set if Image scaling Required */
    objData.scaleImg = FALSE;
    objData.scaledWidth = 0;
    objData.scaledHeight = 0;
    /* Preset */
    // objData.objNum = num_rects;
    /* Quality */
    // objData.quality = 80;
    /* Set to calculate time taken to encode JPG image. */
    // if (calc_enc) {
    //     objData.calcEncodeTime = 1;
    // }
    /*Main Function Call */

    // bool nvds_obj_enc_process (NvDsObjEncCtxHandle, NvDsObjEncUsrArgs *,
    //     NvBufSurface *, NvDsObjectMeta *, NvDsFrameMeta *);
    nvds_obj_enc_process(static_cast<NvDsObjEncCtxHandle>(ctx), &objData,
                         ip_surf, obj_meta, frame_meta);
    if (obj_meta->unique_component_id == 1) {
        std::cout << "obj_meta->unique_component_id = "
                  << obj_meta->unique_component_id
                  << " obj_meta->rect_params.width = "
                  << obj_meta->rect_params.width
                  << " obj_meta->rect_params.height = "
                  << obj_meta->rect_params.height << std::endl;
        std::quick_exit(0);
    }
}

void FaceNvInferServerManager::encode_full_frame_attach_meta(
    gpointer ctx, NvBufSurface *ip_surf, NvDsFrameMeta *frame_meta) {
    NvDsObjEncUsrArgs frameData = {0};
    /* Preset */
    frameData.isFrame = 1;
    /* To be set by user */
    frameData.saveImg = FaceNvInferServerManager::save_img;
    frameData.attachUsrMeta = FaceNvInferServerManager::attach_user_meta;
    /* Set if Image scaling Required */
    frameData.scaleImg = FALSE;
    frameData.scaledWidth = 0;
    frameData.scaledHeight = 0;
    /* Quality */
    frameData.quality = compression_coefficient;
    /* Set to calculate time taken to encode JPG image. */
    //   if (calc_enc) {
    //     frameData.calcEncodeTime = 1;
    //   }
    /* Main Function Call */
    nvds_obj_enc_process(static_cast<NvDsObjEncCtxHandle>(ctx), &frameData,
                         ip_surf, NULL, frame_meta);
}

// AVX check function
bool FaceNvInferServerManager::all_zero_avx(const float *data, size_t size) {
    size_t i = 0;
    __m256 zero = _mm256_setzero_ps();  // 8 floats of 0.0
    for (; i + 8 <= size; i += 8) {
        __m256 v = _mm256_loadu_ps(&data[i]);              // load 8 floats
        __m256 cmp = _mm256_cmp_ps(v, zero, _CMP_NEQ_OQ);  // compare != 0
        if (_mm256_movemask_ps(cmp)) return false;         // if any != 0, exit
    }
    // leftover elements
    for (; i < size; ++i) {
        if (data[i] != 0.0f) return false;
    }
    return true;
}

bool FaceNvInferServerManager::all_zero(const float *data, size_t size) {
    size_t i = 0;

#if defined(__AVX512F__)
    // 16 floats per iteration
    __m512 zero512 = _mm512_setzero_ps();
    for (; i + 16 <= size; i += 16) {
        __m512 v = _mm512_loadu_ps(&data[i]);
        __mmask16 cmp = _mm512_cmp_ps_mask(v, zero512, _CMP_NEQ_OQ);
        if (cmp) return false;
    }
#elif defined(__AVX2__)
    // 8 floats per iteration
    __m256 zero256 = _mm256_setzero_ps();
    for (; i + 8 <= size; i += 8) {
        __m256 v = _mm256_loadu_ps(&data[i]);
        __m256 cmp = _mm256_cmp_ps(v, zero256, _CMP_NEQ_OQ);
        if (_mm256_movemask_ps(cmp)) return false;
    }
#elif defined(__SSE__)
    // 4 floats per iteration
    __m128 zero128 = _mm_setzero_ps();
    for (; i + 4 <= size; i += 4) {
        __m128 v = _mm_loadu_ps(&data[i]);
        __m128 cmp = _mm_cmpneq_ps(v, zero128);
        if (_mm_movemask_ps(cmp)) return false;
    }
#endif

    // leftover elements
    for (; i < size; ++i) {
        if (data[i] != 0.0f) return false;
    }
    return true;
}

/* This is the buffer probe function that we have registered on the sink pad
 * of the tiler element. All SGIE infer elements in the pipeline shall attach
 * their NvDsInferTensorMeta to each object's metadata of each frame, here we
 * will iterate & parse the tensor data to get classification confidence and
 * labels. The result would be attached as classifier_meta into its object's
 * metadata.
 */
GstPadProbeReturn FaceNvInferServerManager::sgie_pad_buffer_probe(
    GstPad *pad, GstPadProbeInfo *info, gpointer ctx) {
    (void)ctx;
    GstBuffer *buf = (GstBuffer *)info->data;
    GstMapInfo inmap = GST_MAP_INFO_INIT;
    if (!gst_buffer_map(buf, &inmap, GST_MAP_READ)) {
        GST_ERROR("input buffer mapinfo failed");
        return GST_PAD_PROBE_DROP;
    }
    NvBufSurface *ip_surf = (NvBufSurface *)inmap.data;
    gst_buffer_unmap(buf, &inmap);
    (void)ip_surf;

    (void)pad;
    // static guint use_device_mem = 0;
    // gboolean *use_new_mux = (gboolean *)u_data;
    // (void)use_new_mux;

    static NvDsInferNetworkInfo networkInfo{FACE_NET_WIDTH, FACE_NET_HEIGHT, 3};
    // (void)networkInfo;

    NvDsBatchMeta *batch_meta =
        gst_buffer_get_nvds_batch_meta(GST_BUFFER(info->data));
    if (!batch_meta) return GST_PAD_PROBE_OK;
    bool is_zero_embedding_vector = false;
    bool current_full_frame_is_captured = false;

    /* 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;
        current_full_frame_is_captured = false;

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

            if (obj_meta->unique_component_id == 1 &&
                obj_meta->obj_user_meta_list != NULL) {
                // body object obj_user_meta_list list is empty so
                // for loop : Iterate user metadata in object to search SGIE's
                // tensor data could not execute.
                std::cout << "obj_meta->unique_component_id = "
                          << obj_meta->unique_component_id
                          << " obj_meta->rect_params.width = "
                          << obj_meta->rect_params.width
                          << " obj_meta->rect_params.height = "
                          << obj_meta->rect_params.height << std::endl;
            }
            // if (obj_meta->unique_component_id == 2) {
            //     body = obj_meta->parent;

            //     // if(body)
            //     // {
            //     //     std::cout<<"body->class_id = "<<body->class_id
            //     //                         << " body->unique_component_id =
            //     "<<body->unique_component_id<<std::endl;
            //     //     if (body->unique_component_id==1){
            //     //         std::quick_exit(0);
            //     //     }
            //     // }

            // }
            // (void)parent;
            // if (obj_meta->unique_component_id !=
            // IMPRECISE_FACE_COMPONENT_ID){
            //     continue;
            // }
            // 1 , 41 for face
            // 0 for body
            if (obj_meta->class_id == 1) {
                if (obj_meta->rect_params.width >
                    obj_meta->rect_params.height) {
                    // printf(
                    //     "sgie_pad_buffer_probe WWW Face bbox: %.2f %.2f %.2f
                    //     "
                    //     "%.2f\n",
                    //     body_obj->rect_params.left,
                    //     body_obj->rect_params.top,
                    //     body_obj->rect_params.width,
                    //     body_obj->rect_params.height);
                } else {
                    // printf(
                    //     "sgie_pad_buffer_probe HHH Face bbox: %.2f %.2f %.2f
                    //     "
                    //     "%.2f\n",
                    //     body_obj->rect_params.left,
                    //     body_obj->rect_params.top,
                    //     body_obj->rect_params.width,
                    //     body_obj->rect_params.height);
                }
            }
            // else{
            //     // std::cout<<"obj_meta->class_id ="
            //     <<obj_meta->class_id<<std::endl; continue;
            // }
            // std::cout<<"obj_meta->class_id = "<<obj_meta->class_id
            //                     << " obj_meta->unique_component_id =
            //                     "<<obj_meta->unique_component_id<<std::endl;
            /* Iterate user metadata in object to search SGIE's tensor data */
            for (NvDsMetaList *l_user = obj_meta->obj_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;

                /* convert to tensor metadata */
                NvDsInferTensorMeta *meta =
                    (NvDsInferTensorMeta *)user_meta->user_meta_data;

                // from here
                // std::cout << "meta->num_output_layers: "
                //           << meta->num_output_layers << std::endl;
                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);
                // std::cout << "meta->network_info.width: "
                //           << meta->network_info.width
                //           << " meta->network_info.height: "
                //           << meta->network_info.height
                //           << " meta->network_info.channels: "
                //           << meta->network_info.channels << std::endl;
#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 sgie network info\n");
                    }
                }
#endif

                // std::cout << "frame number: " << frame_meta->frame_num
                //           << " frame id: " << frame_meta->source_id
                //           << " outputLayersInfo.size(): "
                //           << outputLayersInfo.size() << std::endl;

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

                float score_face = 0;
                float *face_location = (float *)g_malloc0(4 * sizeof(float));
                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;

                    // 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];
                        if (mno < numDims - 1) {
                            // std::cout << ", ";
                        }
                    }
                    // std::cout << "]\n";

                    float *data_face = static_cast<float *>(layer.buffer);
                    if (!(strcmp(layer.layerName, "embedding") == 0)) {
                        // for (unsigned int xyz = 0; xyz < numElements; xyz++)
                        // {
                        //     std::cout << "data_face[" << xyz
                        //               << "]= " << data_face[xyz] <<
                        //               std::endl;
                        // }
                        if ((strcmp(layer.layerName, "bbox") == 0)) {
                            for (int l = 0; l < 4; l++) {
                                face_location[l] = data_face[l];
                                // std::cout << "face_location[" << l
                                //         << "]= " << data_face[l] <<
                                //         std::endl;
                            }
                        }
                        if ((strcmp(layer.layerName, "score") == 0)) {
                            score_face = data_face[0];
                            // std::cout << "score_face= " << score_face
                            // <<std::endl; if (score_face>0.9){
                            // high_confidence_faces++;
                            // std::cout << "high_confidence_faces= " <<
                            // high_confidence_faces << std::endl;
                            // std::quick_exit(0);
                            // }
                        }
                    } else {
                        // is_zero_embedding_vector = 0: send data into server!
                        if (data_face[0] ==
                            0.0f)  // if the first element is zero, then its
                                   // possible all element are zero
                        {
                            is_zero_embedding_vector = all_zero(data_face, 512);
                            // is_zero_embedding_vector = 1 where approximately
                            // 3 times more than is_zero_embedding_vector 0
                        } else {
                            is_zero_embedding_vector =
                                std::all_of(data_face, data_face + 512,
                                            [](float v) { return v == 0.0f; });
                            // is_zero_embedding_vector = 0;
                        }
                        // std::cout<<"is_zero_embedding_vector =
                        // "<<is_zero_embedding_vector<<std::endl; for (int l =
                        // 0; l < 512; l++) {
                        //     std::cout << "face_location[" << l
                        //             << "]= " << data_face[l] << std::endl;
                        // }
                    }
                }

                if (score_face > threshold_face_detection) {
                    // std::cout<<"obj_meta->rect_params left =
                    // "<<obj_meta->rect_params.left
                    //          <<" top = "<<obj_meta->rect_params.top
                    //          <<" width = "<<obj_meta->rect_params.width
                    //          <<" height =
                    //          "<<obj_meta->rect_params.height<<std::endl;
                    NvOSD_RectParams *updated_bbox = allign_postprocess(
                        obj_meta->rect_params, face_location);
                    // std::cout<<"updated_bbox left = "<<updated_bbox->left
                    //          <<" top = "<<updated_bbox->top
                    //          <<" width = "<<updated_bbox->width
                    //          <<" height = "<<updated_bbox->height<<std::endl;
                    // (void)updated_bbox;

                    NvDsObjectMeta *final_face_obj =
                        nvds_acquire_obj_meta_from_pool(batch_meta);

                    strncpy(final_face_obj->obj_label, "FINAL_FACE",
                            sizeof(final_face_obj->obj_label) - 1);
                    final_face_obj
                        ->obj_label[sizeof(final_face_obj->obj_label) - 1] =
                        '\0';  // Ensure null termination
                    final_face_obj->unique_component_id =
                        FINAL_FACE_COMPONENT_ID;  // Use a new component ID
                    final_face_obj->confidence = score_face;

                    add_face_body(obj_meta->object_id, score_face);
                    // std::cout << "body_obj->object_id = " <<
                    // body_obj->object_id
                    //           << " score_face= "<<score_face<<std::endl;
                    // std::cout<< " size face_body_list = " <<
                    // face_body_list.size() << std::endl; obj_meta->object_id =
                    // UNTRACKED_OBJECT_ID;
                    final_face_obj->class_id = 1;
                    final_face_obj->object_id = obj_meta->object_id;

                    final_face_obj->rect_params = *updated_bbox;
                    NvOSD_RectParams &rect_params = final_face_obj->rect_params;
                    NvOSD_TextParams &text_params = final_face_obj->text_params;
                    /* Assign bounding box coordinates. */
                    // rect_params.left = int(obj_meta->rect_params.left *
                    //                     MUXER_OUTPUT_WIDTH /FACE_NET_WIDTH);
                    // rect_params.top = int(obj_meta->rect_params.top *
                    //                     MUXER_OUTPUT_HEIGHT
                    //                     /FACE_NET_HEIGHT);
                    // rect_params.width =
                    //     int(obj_meta->rect_params.width *
                    //         MUXER_OUTPUT_WIDTH / FACE_NET_WIDTH);
                    // rect_params.height =
                    //     int(obj_meta->rect_params.height *
                    //         MUXER_OUTPUT_HEIGHT / FACE_NET_HEIGHT);

                    // std::cout << "nvinferserver second for x = " <<
                    // rect_params.left
                    //           << " y = " << rect_params.top
                    //           << " w = " << rect_params.width
                    //           << " h = " << rect_params.height
                    //           << " score = " << obj_meta->confidence <<
                    //           std::endl;

                    /* Border of width 3. */
                    rect_params.border_width = 3;
                    rect_params.has_bg_color = 0;
                    rect_params.border_color =
                        NvOSD_ColorParams{0, 1, 0, 1};  // Green box
                    /* display_text requires heap allocated memory. */
                    // text_params.display_text = g_strdup(sgie_class_str[0]);
                    // Instead of letting OSD auto-generate text, set your own
                    text_params.display_text = g_strdup_printf(
                        "PreciseFace %lu", final_face_obj->object_id);
                    // printf("Imprecise Face ID: %lu, Precise Face ID: %lu\n",
                    //     obj_meta->object_id, final_face_obj->object_id);
                    /* Display text above the left top corner of the object.*/
                    text_params.x_offset = rect_params.left;
                    text_params.y_offset = rect_params.top + 30;
                    /* Set black background for the text. */
                    text_params.set_bg_clr = 1;
                    text_params.text_bg_clr = NvOSD_ColorParams{0, 0, 0, 1};
                    /* Font face, size and color. */
                    text_params.font_params.font_name = (gchar *)"Serif";
                    text_params.font_params.font_size = 11;
                    text_params.font_params.font_color =
                        NvOSD_ColorParams{1, 1, 1, 1};
                    // adding landmarks to 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(obj_meta, um1);
                    // nvds_add_obj_meta_to_frame(frame_meta, final_face_obj,
                    // NULL);
                    if (is_zero_embedding_vector == false) {
                        if (current_full_frame_is_captured == false) {
                            encode_full_frame_attach_meta(ctx, ip_surf,
                                                          frame_meta);
                            current_full_frame_is_captured = true;
                        }
                        encode_objects_attach_meta(ctx, ip_surf, frame_meta,
                                                   obj_meta);
                        encode_objects_attach_meta(ctx, ip_surf, frame_meta,
                                                   final_face_obj);
                    }
                    nvds_add_obj_meta_to_frame(frame_meta, final_face_obj,
                                               obj_meta);
                }

                // 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;

                //     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];
                //         if (mno < numDims - 1)
                //             std::cout << ", ";
                //     }
                //     std::cout << "]\n";

                //     float *data_face = static_cast<float *>(layer.buffer);
                // }

                // std::quick_exit(0);
                // const NvDsInferLayerInfo &layer =
                //     outputLayersInfo[0];  // or loop over all

                // std::cout<<"meta->num_output_layers: "<<
                // meta->num_output_layers<<std::endl; 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);
                //   }
                // }

                NvDsInferDimsCHW dims_chw;

                getDimsCHWFromDims(dims_chw,
                                   meta->output_layers_info[0].inferDims);
                unsigned int numClasses = dims_chw.c;
                // std::cout << "numClasses: " << numClasses << std::endl;
                (void)numClasses;
                // float *outputCoverageBuffer =
                //     (float *) meta->output_layers_info[0].buffer;
                // float maxProbability = 0;
                // bool attrFound = false;
                // NvDsInferAttribute attr;

                /* Iterate through all the probabilities that the object belongs
                 * to each class. Find the maximum probability and the
                 * corresponding class which meets the minimum threshold. */
                // for (unsigned int c = 0; c < numClasses; c++) {
                //   float probability = outputCoverageBuffer[c];
                //   if (probability > 0.51 && probability > maxProbability) {
                //     maxProbability = probability;
                //     attrFound = true;
                //     attr.attributeIndex = 0;
                //     attr.attributeValue = c;
                //     attr.attributeConfidence = probability;
                //   }
                // }

                /* Generate classifer metadata and attach to obj_meta */
                // if (attrFound) {
                //   NvDsClassifierMeta *classifier_meta =
                //       nvds_acquire_classifier_meta_from_pool (batch_meta);

                //   classifier_meta->unique_component_id = meta->unique_id;

                //   NvDsLabelInfo *label_info =
                //       nvds_acquire_label_info_meta_from_pool (batch_meta);
                //   label_info->result_class_id = attr.attributeValue;
                //   label_info->result_prob = attr.attributeConfidence;

                //   /* Fill label name */
                //   switch (meta->unique_id) {
                //     case sgie1_unique_id:
                //       strcpy (label_info->result_label,
                //           sgie1_classes_str[label_info->result_class_id]);
                //       break;
                //     // case sgie2_unique_id:
                //     //   strcpy (label_info->result_label,
                //     //       sgie2_classes_str[label_info->result_class_id]);
                //     //   break;
                //     default:
                //       break;
                //   }
                //   gchar *temp = obj_meta->text_params.display_text;
                //   obj_meta->text_params.display_text =
                //       g_strconcat (temp, " ", label_info->result_label,
                //       nullptr);
                //   g_free (temp);

                //   nvds_add_label_info_meta_to_classifier (classifier_meta,
                //   label_info); nvds_add_classifier_meta_to_object (obj_meta,
                //   classifier_meta);
                // }
            }
        }
    }

    nvds_obj_enc_finish(static_cast<NvDsObjEncCtxHandle>(ctx));
    // use_device_mem = 1 - use_device_mem;
    return GST_PAD_PROBE_OK;
}

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

    for (i = 0; i < 512; i++) {
        user_metadata[i] = arr[i];
    }
    return (void *)user_metadata;
}

gpointer FaceNvInferServerManager::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(512 * sizeof(gfloat));
    memcpy(dst_user_metadata, src_user_metadata, 512 * sizeof(gfloat));
    return (gpointer)dst_user_metadata;
}

void FaceNvInferServerManager::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;
    }
}

// }{1, 1, 1, 1};
//                 // adding landmarks to 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(obj_meta, um1);
//                 nvds_add_obj_meta_to_frame(frame_meta, obj_meta, NULL);
//             }
//         }
//     }
//     // use_device_mem = 1 - use_device_mem;
//     return GST_PAD_PROBE_OK;
// }