#include <obs-frontend-api.h>
#include <QMainWindow>
#include <QAction>
#include <obs.hpp>
#include "decklink-captions.h"

OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("decklink-captons", "en-US")

struct obs_captions {
	std::string source_name;
	OBSWeakSource source;

	void start();
	void stop();

	obs_captions();
	inline ~obs_captions() { stop(); }
};

obs_captions::obs_captions() {}

static obs_captions *captions = nullptr;

DecklinkCaptionsUI::DecklinkCaptionsUI(QWidget *parent)
	: QDialog(parent),
	  ui(new Ui_CaptionsDialog)
{
	ui->setupUi(this);

	setSizeGripEnabled(true);

	setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

	auto cb = [this](obs_source_t *source) {
		uint32_t caps = obs_source_get_output_flags(source);
		QString name = obs_source_get_name(source);

		if (caps & OBS_SOURCE_CEA_708)
			ui->source->addItem(name);

		OBSWeakSource weak = OBSGetWeakRef(source);
		if (weak == captions->source)
			ui->source->setCurrentText(name);
		return true;
	};

	using cb_t = decltype(cb);

	ui->source->blockSignals(true);
	ui->source->addItem(QStringLiteral(""));
	ui->source->setCurrentIndex(0);
	obs_enum_sources(
		[](void *data, obs_source_t *source) {
			return (*static_cast<cb_t *>(data))(source);
		},
		&cb);
	ui->source->blockSignals(false);
}

void DecklinkCaptionsUI::on_source_currentIndexChanged(int)
{
	captions->stop();

	captions->source_name = ui->source->currentText().toUtf8().constData();
	captions->source = GetWeakSourceByName(captions->source_name.c_str());

	captions->start();
}

static void caption_callback(void * /* param */, obs_source_t * /* source */,
			     const struct obs_source_cea_708 *captions)
{
	obs_output *output = obs_frontend_get_streaming_output();
	if (output) {
		if (obs_frontend_streaming_active() &&
		    obs_output_active(output)) {
			obs_output_caption(output, captions);
		}
		obs_output_release(output);
	}
}

void obs_captions::start()
{
	OBSSource s = OBSGetStrongRef(source);
	if (!s) {
		//warn("Source invalid");
		return;
	}
	obs_source_add_caption_callback(s, caption_callback, nullptr);
}

void obs_captions::stop()
{
	OBSSource s = OBSGetStrongRef(source);
	if (s)
		obs_source_remove_caption_callback(s, caption_callback,
						   nullptr);
}

static void save_decklink_caption_data(obs_data_t *save_data, bool saving,
				       void *)
{
	if (saving) {
		obs_data_t *obj = obs_data_create();

		obs_data_set_string(obj, "source",
				    captions->source_name.c_str());

		obs_data_set_obj(save_data, "decklink_captions", obj);
		obs_data_release(obj);
	} else {
		captions->stop();

		obs_data_t *obj =
			obs_data_get_obj(save_data, "decklink_captions");
		if (!obj)
			obj = obs_data_create();

		captions->source_name = obs_data_get_string(obj, "source");
		captions->source =
			GetWeakSourceByName(captions->source_name.c_str());
		obs_data_release(obj);

		captions->start();
	}
}

void addOutputUI(void)
{
	QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
		obs_module_text("Decklink Captions"));

	captions = new obs_captions;

	auto cb = []() {
		obs_frontend_push_ui_translation(obs_module_get_string);

		QWidget *window = (QWidget *)obs_frontend_get_main_window();

		DecklinkCaptionsUI dialog(window);
		dialog.exec();

		obs_frontend_pop_ui_translation();
	};

	obs_frontend_add_save_callback(save_decklink_caption_data, nullptr);

	action->connect(action, &QAction::triggered, cb);
}

bool obs_module_load(void)
{
	return true;
}

void obs_module_post_load(void)
{
	if (!obs_get_module("decklink"))
		return;

	addOutputUI();
}
