// SPDX-License-Identifier: GPL-2.0+ AND LGPL-2.0+ AND LGPL-2.1+

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include <stdint.h>

#include "gstmvbmeta.h"

#define xstr(s) str(s)
#define str(s) #s

enum FD_TYPE {
  FD_TYPE_NORMAL_BASIC  = 0,
  FD_TYPE_NORMAL_VENDOR = 1,
  FD_TYPE_SECURE_BASIC  = 2,
  FD_TYPE_SECURE_VENDOR = 3,
  FD_TYPE_OTHER         = 4,
};

#define META_ITEM_NUM_MAX 8

struct MetaItem_t {
  int32_t fd;
  int32_t fd_type;
};

struct MetaInfo_t {
  uint64_t magic_num;
  uint32_t version;

  uint32_t meta_item_num;
  uint32_t meta_item_num_max;

  struct MetaItem_t meta_item_list[META_ITEM_NUM_MAX];
};


int
gst_meta_mvb_get_fd (GstMetaMvb* meta)
{
  if (meta == NULL)
    return -1;

  return meta->meta_fd;
}

uint64_t
gst_meta_mvb_get_magic_num (GstMetaMvb* meta)
{
  struct MetaInfo_t* pmetainfo;
  uint64_t magic_num;

  pmetainfo = (struct MetaInfo_t*) mvb_slot_mmap (meta->priv_slot_out);
  magic_num = pmetainfo->magic_num;
  mvb_slot_umap (meta->priv_slot_out);

  return magic_num;
}

uint32_t
gst_meta_mvb_get_version (GstMetaMvb* meta)
{
  struct MetaInfo_t* pmetainfo;
  uint64_t version;

  pmetainfo = (struct MetaInfo_t*) mvb_slot_mmap (meta->priv_slot_out);
  version = pmetainfo->version;
  mvb_slot_umap (meta->priv_slot_out);

  return version;
}

int32_t
gst_meta_mvb_get_internal_fd (GstMetaMvb* meta, int index)
{
  struct MetaInfo_t* pmetainfo;
  int internal_fd;

  pmetainfo = (struct MetaInfo_t*) mvb_slot_mmap (meta->priv_slot_out);
  internal_fd = pmetainfo->meta_item_list[index].fd;
  mvb_slot_umap (meta->priv_slot_out);

  return internal_fd;
}

int32_t
gst_meta_mvb_get_internal_fd_count (GstMetaMvb* meta)
{
  struct MetaInfo_t* pmetainfo;
  int internal_fd_count;

  pmetainfo = (struct MetaInfo_t*) mvb_slot_mmap (meta->priv_slot_out);
  internal_fd_count = pmetainfo->meta_item_num;
  mvb_slot_umap (meta->priv_slot_out);

  return internal_fd_count;
}

void
gst_meta_mvb_debug (GstMetaMvb* meta)
{
  uint8_t* data;
  struct MetaInfo_t* pmetainfo;

  pmetainfo = (struct MetaInfo_t*) mvb_slot_mmap (meta->priv_slot_out);
  GST_DEBUG ("mag %llu %u", pmetainfo->magic_num, pmetainfo->version);
  mvb_slot_umap (meta->priv_slot_out);

  data = (uint8_t*) mvb_slot_mmap (meta->priv_slot_internal_normal_basic);

  for (int i = 16; i < 16 + 40; i++)
  {
    GST_DEBUG ("byte %d => %x", i, data[i]);
  }

  mvb_slot_umap (meta->priv_slot_internal_normal_basic);
}

void
gst_buffer_refresh_meta_mvb (GstMetaMvb * mvb_meta)
{
  int   internal_normal_basic_fd = -1;
  int   internal_secure_basic_fd = -1;
  struct MetaInfo_t* pmetainfo;

  GST_DEBUG ("refreshing");

  if (mvb_meta == NULL) {
    GST_ERROR ("invalid mvb_meta");
    return;
  }
  if (mvb_meta->priv_slot_out == NULL) {
    GST_ERROR ("invalid priv_slot_out");
    return;
  }
  if (mvb_meta->priv_slot_internal_normal_basic == NULL) {
    GST_ERROR ("invalid priv_slot_internal_normal_basic");
    return;
  }
  if (mvb_meta->priv_slot_internal_secure_basic == NULL) {
    GST_ERROR ("invalid priv_slot_internal_secure_basic");
    return;
  }



  /* get meta fd and internal normal basic fd */
  mvb_meta->meta_fd        = mvb_slot_get_fd (mvb_meta->priv_slot_out);
  internal_normal_basic_fd = mvb_slot_get_fd (mvb_meta->priv_slot_internal_normal_basic);
  internal_secure_basic_fd = mvb_slot_get_fd (mvb_meta->priv_slot_internal_secure_basic);
  GST_DEBUG ("get outfd %d normal basic fd: %d secure fd: %d",
              mvb_meta->meta_fd,
              internal_normal_basic_fd,
              internal_secure_basic_fd);

  /* fill meta info */
  pmetainfo = (struct MetaInfo_t*) mvb_slot_mmap (mvb_meta->priv_slot_out);
  if (pmetainfo == NULL) {
    GST_DEBUG ("failed to get va of meta fd");
    return;
  }
  pmetainfo->magic_num         = 1;
  pmetainfo->version           = 1;
  pmetainfo->meta_item_num     = 2;
  pmetainfo->meta_item_num_max = META_ITEM_NUM_MAX;
  pmetainfo->meta_item_list[0].fd      = internal_normal_basic_fd;
  pmetainfo->meta_item_list[0].fd_type = FD_TYPE_NORMAL_BASIC;
  pmetainfo->meta_item_list[1].fd      = internal_secure_basic_fd;
  pmetainfo->meta_item_list[1].fd_type = FD_TYPE_SECURE_BASIC;

  GST_DEBUG ("meta mvb fd: %d", mvb_meta->meta_fd);
  GST_DEBUG ("meta mvb internal normal basic fd: %d", pmetainfo->meta_item_list[0].fd);
  GST_DEBUG ("meta mvb internal secure basic fd: %d", pmetainfo->meta_item_list[1].fd);

  mvb_slot_umap (mvb_meta->priv_slot_out);

  gst_meta_mvb_debug (mvb_meta);

  GST_DEBUG ("refresh done");
}

static gboolean
meta_mvb_init_func (GstMeta * meta, gpointer params, GstBuffer * buffer)
{
  GstMetaMvb *mvb_meta = (GstMetaMvb *) meta;

  GST_DEBUG ("meta mvb init");

  mvb_meta->priv_heap                       = mvb_heap_create (xstr(DEFAULT_MVB_HEAP_NAME));
  mvb_meta->priv_slot_out                   = mvb_heap_alloc_slot (mvb_meta->priv_heap, sizeof(struct MetaInfo_t), 0);
  mvb_meta->priv_slot_internal_normal_basic = mvb_heap_alloc_slot (mvb_meta->priv_heap, 4 * 1024,                  0);
  mvb_meta->priv_slot_internal_secure_basic = mvb_heap_alloc_slot (mvb_meta->priv_heap, 4 * 1024, MVB_HEAP_FLAG_SECURE);
  gst_buffer_refresh_meta_mvb (mvb_meta);
  GST_DEBUG ("init done");
  return TRUE;
}

static gboolean
meta_mvb_transform_func (GstBuffer * transbuf, GstMeta * meta,
    GstBuffer * buffer, GQuark type, gpointer data)
{
  GST_DEBUG ("meta mvb transform");
  return TRUE;
}

static void
meta_mvb_free_func (GstMeta * meta, GstBuffer * buffer)
{
  GstMetaMvb *mvb_meta = (GstMetaMvb *) meta;

  mvb_heap_free_slot (mvb_meta->priv_slot_internal_normal_basic);
  mvb_heap_free_slot (mvb_meta->priv_slot_internal_secure_basic);
  mvb_heap_free_slot (mvb_meta->priv_slot_out);

  mvb_heap_destroy (mvb_meta->priv_heap);

  GST_DEBUG ("meta mvb free");
}

GType
gst_meta_mvb_api_get_type (void)
{
  static volatile GType type;
  static const gchar *tags[] = { NULL };

  if (g_once_init_enter (&type)) {
    GType _type = gst_meta_api_type_register ("GstMetaMvbAPI", tags);
    g_once_init_leave (&type, _type);
  }
  return type;
}

const GstMetaInfo *
gst_meta_mvb_get_info (void)
{
  static const GstMetaInfo *meta_mvb_info = NULL;

  if (g_once_init_enter ((GstMetaInfo **) & meta_mvb_info)) {
    const GstMetaInfo *mi = gst_meta_register (GST_META_MVB_API_TYPE,
        "GstMetaMvb",
        sizeof (GstMetaMvb),
        meta_mvb_init_func, meta_mvb_free_func, meta_mvb_transform_func);
    g_once_init_leave ((GstMetaInfo **) & meta_mvb_info, (GstMetaInfo *) mi);
  }
  return meta_mvb_info;
}

GstMetaMvb *
gst_buffer_add_meta_mvb (GstBuffer * buffer)
{
  GstMetaMvb *meta;

  GST_DEBUG ("meta mvb adding");

  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);

  meta = (GstMetaMvb *) gst_buffer_add_meta (buffer, GST_META_MVB_INFO, NULL);

  GST_DEBUG ("meta mvb added");

  return meta;
}

GstMetaMvb *
gst_buffer_get_meta_mvb (GstBuffer * buffer)
{
  return (GstMetaMvb *) gst_buffer_get_meta (buffer, GST_META_MVB_API_TYPE);
}

