/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * qt_event_dispatcher.cpp - qcam - Qt-based event dispatcher
 */

#include <chrono>
#include <iostream>

#include <QAbstractEventDispatcher>
#include <QCoreApplication>
#include <QSocketNotifier>
#include <QTimerEvent>

#include <libcamera/event_notifier.h>
#include <libcamera/timer.h>

#include "qt_event_dispatcher.h"

using namespace libcamera;

QtEventDispatcher::QtEventDispatcher()
{
}

QtEventDispatcher::~QtEventDispatcher()
{
	for (auto &it : notifiers_) {
		NotifierSet &set = it.second;
		delete set.read.qnotifier;
		delete set.write.qnotifier;
		delete set.exception.qnotifier;
	}
}

void QtEventDispatcher::registerEventNotifier(EventNotifier *notifier)
{
	NotifierSet &set = notifiers_[notifier->fd()];
	QSocketNotifier::Type qtype;
	void (QtEventDispatcher::*method)(int);
	NotifierPair *pair;

	switch (notifier->type()) {
	case EventNotifier::Read:
	default:
		qtype = QSocketNotifier::Read;
		method = &QtEventDispatcher::readNotifierActivated;
		pair = &set.read;
		break;

	case EventNotifier::Write:
		qtype = QSocketNotifier::Write;
		method = &QtEventDispatcher::writeNotifierActivated;
		pair = &set.write;
		break;

	case EventNotifier::Exception:
		qtype = QSocketNotifier::Exception;
		method = &QtEventDispatcher::exceptionNotifierActivated;
		pair = &set.exception;
		break;
	}

	QSocketNotifier *qnotifier = new QSocketNotifier(notifier->fd(), qtype);
	connect(qnotifier, &QSocketNotifier::activated, this, method);
	pair->notifier = notifier;
	pair->qnotifier = qnotifier;
}

void QtEventDispatcher::unregisterEventNotifier(EventNotifier *notifier)
{
	NotifierSet &set = notifiers_[notifier->fd()];
	NotifierPair *pair;

	switch (notifier->type()) {
	case EventNotifier::Read:
	default:
		pair = &set.read;
		break;

	case EventNotifier::Write:
		pair = &set.write;
		break;

	case EventNotifier::Exception:
		pair = &set.exception;
		break;
	}

	delete pair->qnotifier;
	pair->qnotifier = nullptr;
	pair->notifier = nullptr;
}

void QtEventDispatcher::readNotifierActivated(int socket)
{
	EventNotifier *notifier = notifiers_[socket].read.notifier;
	notifier->activated.emit(notifier);
}

void QtEventDispatcher::writeNotifierActivated(int socket)
{
	EventNotifier *notifier = notifiers_[socket].write.notifier;
	notifier->activated.emit(notifier);
}

void QtEventDispatcher::exceptionNotifierActivated(int socket)
{
	EventNotifier *notifier = notifiers_[socket].exception.notifier;
	notifier->activated.emit(notifier);
}

void QtEventDispatcher::registerTimer(Timer *timer)
{
	std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
	std::chrono::steady_clock::duration duration = timer->deadline() - now;
	std::chrono::milliseconds msec =
		std::chrono::duration_cast<std::chrono::milliseconds>(duration);
	int timerId = startTimer(msec.count());
	timers_[timerId] = timer;
	timerIds_[timer] = timerId;
}

void QtEventDispatcher::unregisterTimer(Timer *timer)
{
	auto it = timerIds_.find(timer);
	if (it == timerIds_.end())
		return;

	timers_.erase(it->second);
	killTimer(it->second);
	timerIds_.erase(it);
}

void QtEventDispatcher::timerEvent(QTimerEvent *event)
{
	Timer *timer = timers_[event->timerId()];
	timer->stop();
	timer->timeout.emit(timer);
}

void QtEventDispatcher::processEvents()
{
	std::cout << "QtEventDispatcher::processEvents() should not be called"
		  << std::endl;
}

void QtEventDispatcher::interrupt()
{
	QCoreApplication::eventDispatcher()->interrupt();
}