extern "C" {
#include <libavformat/version.h>
#include <libavutil/version.h>
}
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <epoxy/gl.h>  // IWYU pragma: keep
#include <QApplication>
#include <QCoreApplication>
#include <QGL>
#include <QSize>
#include <QSurfaceFormat>
#include <string>

#ifdef HAVE_CEF
#include <cef_app.h>
#endif

#ifdef HAVE_SRT
#include <srt/srt.h>
#endif

#include "basic_stats.h"
#ifdef HAVE_CEF
#include "nageru_cef_app.h"
#endif
#include "shared/context.h"
#include "flags.h"
#include "mainwindow.h"
#include "mixer.h"
#include "quicksync_encoder.h"

#ifdef HAVE_CEF
CefRefPtr<NageruCefApp> cef_app;
#endif

int main(int argc, char *argv[])
{
#ifdef HAVE_CEF
	// Let CEF have first priority on parsing the command line, because we might be
	// launched as a CEF sub-process.
	CefMainArgs main_args(argc, argv);
	cef_app = CefRefPtr<NageruCefApp>(new NageruCefApp());
	int err = CefExecuteProcess(main_args, cef_app.get(), nullptr);
	if (err >= 0) {
		return err;
	}

	// CEF wants to use GLib for its main loop, which interferes with Qt's use of it.
	// The alternative is trying to integrate CEF into Qt's main loop, but that requires
	// fairly extensive cross-thread communication and that parts of CEF runs on Qt's UI
	// thread.
	setenv("QT_NO_GLIB", "1", 0);
#endif

	parse_flags(PROGRAM_NAGERU, argc, argv);

	if (global_flags.va_display.empty() && !global_flags.x264_video_to_disk) {
		// The user didn't specify a VA-API display, but we need one.
		// See if the default works, and if not, let's try to help
		// the user by seeing if there's any that would work automatically.
		global_flags.va_display = QuickSyncEncoder::get_usable_va_display();
	}

	// The OpenGL widgets do not work well with the native Wayland integration.
	setenv("QT_QPA_PLATFORM", "xcb", 0);

	if ((global_flags.va_display.empty() ||
	     global_flags.va_display[0] != '/') && !global_flags.x264_video_to_disk) {
		// We normally use EGL for zerocopy, but if we use VA against DRM
		// instead of against X11, we turn it off, and then don't need EGL.
		setenv("QT_XCB_GL_INTEGRATION", "xcb_egl", 0);
	}
	setlinebuf(stdout);
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
	av_register_all();
#endif

#ifdef HAVE_SRT
	if (global_flags.srt_port >= 0 || !global_flags.srt_destination_host.empty()) {
		srt_startup();
	}
#endif

	QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);

	QSurfaceFormat fmt;
	fmt.setDepthBufferSize(0);
	fmt.setStencilBufferSize(0);
	fmt.setProfile(QSurfaceFormat::CoreProfile);
	fmt.setMajorVersion(3);
	fmt.setMinorVersion(1);

	// Turn off vsync, since Qt generally gives us at most frame rate
	// (display frequency) / (number of QGLWidgets active).
	fmt.setSwapInterval(0);

	QSurfaceFormat::setDefaultFormat(fmt);

	QGLFormat::setDefaultFormat(QGLFormat::fromSurfaceFormat(fmt));

	QApplication app(argc, argv);
	global_share_widget = new QGLWidget();
	if (!global_share_widget->isValid()) {
		fprintf(stderr, "Failed to initialize OpenGL. Nageru needs at least OpenGL 3.1 to function properly.\n");
		abort();
	}

	MainWindow mainWindow;
	mainWindow.resize(QSize(1500, 910));
	mainWindow.show();

	app.installEventFilter(&mainWindow);  // For white balance color picking.

	// Even on an otherwise unloaded system, it would seem writing the recording
	// to disk (potentially terabytes of data as time goes by) causes Nageru
	// to be pushed out of RAM. If we have the right privileges, simply lock us
	// into memory for better realtime behavior.
	if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
		perror("mlockall()");
		fprintf(stderr, "Failed to lock Nageru into RAM. You probably want to\n");
		fprintf(stderr, "increase \"memlock\" for your user in limits.conf\n");
		fprintf(stderr, "for better realtime behavior.\n");
		uses_mlock = false;
	} else {
		uses_mlock = true;
	}

	int rc = app.exec();
	delete global_mixer;
#ifdef HAVE_SRT
	if (global_flags.srt_port >= 0) {
		srt_cleanup();
	}
#endif
	return rc;
}
