#include "RtspServer.h"
#include "Core/Logger.h"
#include <boost/asio/io_context.hpp>
#include <boost/asio/strand.hpp>
#include <chrono>
#include <cstring>
#include <mk_common.h>
#include <mk_h264_splitter.h>
#include <mk_media.h>
#include <thread>

constexpr auto iniConfig = R"(
[rtsp]
lowLatency=1
)";
class RtspServerPrivate {
public:
    RtspServerPrivate(boost::asio::io_context &ioContext) : strand{ioContext.get_executor()} {
    }
    boost::asio::strand<boost::asio::io_context::executor_type> strand;
    mk_media media;
    mk_h264_splitter splitter;
};

static void on_h264_frame(void *user_data, mk_h264_splitter splitter, const char *data, int size) {
    using namespace std::chrono;
    using namespace std::chrono_literals;
    int pts = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    mk_frame frame = mk_frame_create(MKCodecH264, pts, pts, data, size, NULL, NULL);
    mk_media_input_frame((mk_media)user_data, frame);
    mk_frame_unref(frame);
}

RtspServer::RtspServer(boost::asio::io_context &ioContext) : m_d(new RtspServerPrivate(ioContext)) {
    mk_config config;
    std::memset(&config, 0, sizeof(mk_config));
    config.ini = iniConfig;
    config.ini_is_path = 0;
    config.log_mask = LOG_CONSOLE;
    config.ssl_is_path = 1;
    mk_env_init(&config);

    uint16_t status = mk_rtsp_server_start(554, 0);

    status = mk_rtc_server_start(7764);
    if (status == 0) {
        LOG(error) << "mk_rtc_server_start() failed.";
    }

    m_d->media = mk_media_create("__defaultVhost__", "live", "video", 0, 0, 0);

    codec_args v_args = {0};
    mk_track v_track = mk_track_create(MKCodecH264, &v_args);
    mk_media_init_track(m_d->media, v_track);
    mk_media_init_complete(m_d->media);
    mk_track_unref(v_track);

    m_d->splitter = mk_h264_splitter_create(on_h264_frame, m_d->media, 0);
}

RtspServer::~RtspServer() {
    mk_h264_splitter_release(m_d->splitter);
    mk_media_release(m_d->media);
    mk_stop_all_server();
    if (m_d != nullptr) {
        delete m_d;
    }
}

void RtspServer::push(const uint8_t *data, uint32_t size) {
    boost::asio::post(m_d->strand, [this, frame = std::vector<uint8_t>(data, data + size)]() {
        mk_h264_splitter_input_data(m_d->splitter, reinterpret_cast<const char *>(frame.data()), frame.size());
    });
}
