#include "rtp_time.hpp"
#include <cassert>
#include <cstring>

int main(int argc, char** argv) {
    gst_init(&argc, &argv);
    const bool enabled = argc > 1 && strcmp(argv[1], "off");
    const bool latest = argc > 2;
    g_setenv("CONTEXTRA_INPUT_TIMING_SECONDS", argc > 3 ? argv[3] : (enabled ? "30" : "0"), TRUE);
    g_setenv("CONTEXTRA_SOURCE_LATEST_TRIAL", latest ? "true" : "false", TRUE);
    GstBuffer* packet = gst_rtp_buffer_new_allocate(1, 0, 0);
    GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
    assert(gst_rtp_buffer_map(packet, GST_MAP_WRITE, &rtp));
    gst_rtp_buffer_set_timestamp(&rtp, 9000);
    gst_rtp_buffer_set_ssrc(&rtp, 1);
    gst_rtp_buffer_unmap(&rtp);
    GstCaps* caps = gst_caps_new_simple("application/x-rtp", "clock-rate", G_TYPE_INT, 90000, nullptr);
    assert(contextra_rtp::attach_packet_time(packet, caps));
    assert(contextra_rtp::attach_frame_time(packet));
    gpointer cursor = nullptr;
    GstMeta* meta;
    int public_count = 0, private_count = 0;
    while ((meta = gst_buffer_iterate_meta(packet, &cursor))) {
        if (meta->info->api != NVDS_META_API_TYPE) continue;
        auto* nv = reinterpret_cast<NvDsMeta*>(meta);
        if (nv->meta_type == contextra_rtp::meta_type()) {
            ++public_count;
            assert(!strcmp(static_cast<const char*>(nv->meta_data),
                           "{\"timestamp\":9000,\"clock_rate\":90000,\"ssrc\":1}"));
        } else {
            ++private_count;
            assert(strstr(static_cast<const char*>(nv->meta_data), "depay_first_ns"));
        }
    }
    assert(public_count == 1 && private_count == int(enabled));
    GstBuffer* copied = gst_buffer_copy(packet);
    gst_buffer_unref(packet);
    assert(gst_buffer_get_nvds_meta(copied));
    gst_buffer_unref(copied);
    gst_caps_unref(caps);
    GstElement* queue = gst_element_factory_make("queue", "fixture-queue");
    GstPad* sink = gst_element_get_static_pad(queue, "sink");
    for (int kind = 0; kind < 3; ++kind) {
        GstCaps* c = gst_caps_new_empty_simple(kind == 0 ? "video/x-h264" : "video/x-raw");
        if (kind == 2) gst_caps_set_features(c, 0, gst_caps_features_new("memory:NVMM", nullptr));
        GstEvent* event = gst_event_new_caps(c);
        GstPadProbeInfo info{};
        info.type = GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM;
        info.data = event;
        contextra_rtp::queue_caps(sink, &info, nullptr);
        gint leaky = 0;
        guint capacity = 0;
        g_object_get(queue, "leaky", &leaky, "max-size-buffers", &capacity, nullptr);
        assert(leaky == (latest && kind == 2 ? 2 : 0));
        if (latest && kind == 2) assert(capacity == 1);
        gst_event_unref(event);gst_caps_unref(c);
    }
    if (latest) {
        GstBuffer* frame = gst_rtp_buffer_new_allocate(1, 0, 0);
        GstCaps* rtp_caps = gst_caps_new_simple("application/x-rtp", "clock-rate", G_TYPE_INT, 90000, nullptr);
        assert(contextra_rtp::attach_packet_time(frame, rtp_caps));
        GST_BUFFER_FLAG_SET(frame, GST_BUFFER_FLAG_DISCONT);
        GstPad* src = gst_element_get_static_pad(queue, "src");
        GstPadProbeInfo info{};
        info.type = GST_PAD_PROBE_TYPE_BUFFER;info.data = frame;
        contextra_rtp::queue_timing(src, &info, const_cast<char*>("raw_out"));
        frame = GST_PAD_PROBE_INFO_BUFFER(&info);
        assert(contextra_rtp::attach_frame_time(frame));
        gpointer it = nullptr;bool gap_found = false;
        while (auto* m = gst_buffer_iterate_meta(frame, &it)) {
            if (m->info->api != NVDS_META_API_TYPE) continue;
            auto* nv = reinterpret_cast<NvDsMeta*>(m);
            if (nv->meta_type == nvds_get_user_meta_type((gchar*)"CONTEXTRA.INPUT.GAP.V1"))
                gap_found = !strcmp(static_cast<const char*>(nv->meta_data), "{\"gap\":true}");
        }
        assert(gap_found);  // The policy must keep gap protection after timing collection stops.
        gst_object_unref(src);gst_buffer_unref(frame);gst_caps_unref(rtp_caps);
    }
    gst_object_unref(sink);gst_object_unref(queue);
}
