#include "uipriv_qt5.hpp"

#include <QApplication>
#include <QTimer>
#include <QWidget>
#include <QThread>
#include <QObject>

#include <functional>

const char *styleSheet = R"qcss(
QGroupBox {
	font: bold;
	color: #333;
	/*border: none;*/
}
)qcss";

const char *uiInit(uiInitOptions *o)
{
	Q_UNUSED(o); // don't care about something that isn't used

	if (QCoreApplication::instance() != nullptr) {
		return "another QApplication instance already exists";
	}

	static int argc = 0;
	static const char *argv[] = {"libui"};
	auto app = new QApplication(argc,(char **)argv);

	if (app == nullptr) {
		return "failed to allocate new QApplication";
	}

	qSetMessagePattern("%{file}:%{line} %{function}(): %{message}");

	// few consistency things
	app->setQuitOnLastWindowClosed(false);
	app->setStyleSheet(styleSheet);

	uiprivInitAlloc();

	return NULL;
}

void uiUninit(void)
{
	auto app = static_cast<QApplication*>(QCoreApplication::instance());

	// not strictly necessary, just so we don't get needless nag from uninitAlloc()
	// becasue of cascading it's not possible to iterate over the list (copy)
	while (app->topLevelWidgets().count()) {
		delete app->topLevelWidgets().first();
	}

	uiprivUninitAlloc();

	delete app;
}

void uiFreeInitError(const char *err)
{
	Q_UNUSED(err); // we only return static constants for errors
}

void uiMain(void)
{
	if (QApplication::instance()) {
		QApplication::instance()->exec();
	}
}

int uiMainStep(int wait)
{
	if (QApplication::instance()) {
		if (wait) {
			// processEvents will always return immediately if there are no
			// immediate events to process

			// we would have to install a event listener and run in while loop
			// that seems very questionable.. don't understand the use case
			qWarning("TODO: wait - no equivalent?");
		}
		QApplication::instance()->processEvents();
	}
	return 0;
}

void uiMainSteps(void)
{
	// don't need to do anything here
}

void uiQuit(void)
{
	QTimer::singleShot(0, QApplication::instance(), &QApplication::quit);
}

void uiQueueMain(void (*f)(void *data), void *data)
{
	if (!QCoreApplication::instance()) {
		// no instance...?
		return;
	}

	if (QThread::currentThread()->eventDispatcher()) {
		// the simple way to queue something to run on main thread
		QTimer::singleShot(0,QCoreApplication::instance(), [f,data]{
			f(data);
		});
	} else {
		// very dirty workaround, spawn a new thread.. with event dispatcher to deliver this

		// this could be done with custom signal, but since I decided to do
		// this without using moc this is the simplest solution I could think of.

		// sort of works, but not very light weight and will block to ensure resource cleanup
		class Thread : public QThread
		{
			std::function<void ()> taskForMain_;

			void run()
			{
				QTimer::singleShot(0,QCoreApplication::instance(), taskForMain_);
				exec(); // start event loop
			}
		public:
			Thread(std::function<void ()> taskForMain)
				: taskForMain_(taskForMain)
			{
				start();
			}
			~Thread()
			{
				quit();
				wait();
			}
		};
		Thread([f,data]{
			f(data);
		});
	}
}

void uiTimer(int milliseconds, int (*f)(void* data), void* data)
{
    QTimer *timer = new QTimer(QCoreApplication::instance());
    QObject::connect(timer, &QTimer::timeout, timer, [f, data, timer]() {
        if (!(f(data))) {
            timer->stop();
        }
    });
    timer->start(milliseconds);
}
