#include "qad_types.h"

#include <QApplication>
#include <QFontMetrics>
#include <QMetaEnum>
#include <QWidget>
#if QT_VERSION >= 0x050000
#  include <QScreen>
#  include <QWindow>
#endif


bool __QADTypesRegistrator__::_inited = false;


__QADTypesRegistrator__::__QADTypesRegistrator__(int) {
	instance()->_inited = true;
}

__QADTypesRegistrator__ * __QADTypesRegistrator__::instance() {
	static __QADTypesRegistrator__ ret;
	return &ret;
}

__QADTypesRegistrator__::__QADTypesRegistrator__() {
	if (_inited) return;
	_inited = true;

	qRegisterMetaType<QAD::Enumerator>("QAD::Enumerator");
	qRegisterMetaType<QAD::Enum>("QAD::Enum");
	qRegisterMetaType<QAD::File>("QAD::File");
	qRegisterMetaType<QAD::Dir>("QAD::Dir");
	qRegisterMetaType<QAD::IODevice>("QAD::IODevice");
	qRegisterMetaType<QAD::MathVector>("QAD::MathVector");
	qRegisterMetaType<QAD::MathMatrix>("QAD::MathMatrix");

#if QT_VERSION_MAJOR <= 5
	qRegisterMetaTypeStreamOperators<QAD::Enumerator>("QAD::Enumerator");
	qRegisterMetaTypeStreamOperators<QAD::Enum>("QAD::Enum");
	qRegisterMetaTypeStreamOperators<QAD::File>("QAD::File");
	qRegisterMetaTypeStreamOperators<QAD::Dir>("QAD::Dir");
	qRegisterMetaTypeStreamOperators<QAD::IODevice>("QAD::IODevice");
	qRegisterMetaTypeStreamOperators<QAD::MathVector>("QAD::MathVector");
	qRegisterMetaTypeStreamOperators<QAD::MathMatrix>("QAD::MathMatrix");
#endif

#if QT_VERSION >= 0x050200
	QMetaType::registerConverter<QAD::Enum, int>(&QAD::Enum::selectedValue);
	QMetaType::registerConverter<QAD::Enum, QString>(&QAD::Enum::selectedName);
	QMetaType::registerConverter<QAD::File, QString>(&QAD::File::toString);
	QMetaType::registerConverter<QAD::Dir, QString>(&QAD::Dir::toString);
	QMetaType::registerConverter<QAD::IODevice, QString>(&QAD::IODevice::toString);
#endif
}

__QADTypesRegistrator__ __registrator__(1);


// QAD::Enum

QAD::Enum::Enum(const QMetaEnum & meta, int selected) {
	enum_name = meta.name();
	for (int i = 0; i < meta.keyCount(); ++i) {
		enum_list << QAD::Enumerator(meta.value(i), meta.key(i));
	}
	selectValue(selected);
}


int QAD::Enum::selectedValue() const {
	for (const Enumerator & e: enum_list) {
		if (e.name == selected) return e.value;
	}
	return 0;
}


bool QAD::Enum::selectValue(int v) {
	for (const Enumerator & e: enum_list) {
		if (e.value == v) {
			selected = e.name;
			return true;
		}
	}
	return false;
}


bool QAD::Enum::selectName(const QString & n) {
	for (const Enumerator & e: enum_list) {
		if (e.name == n) {
			selected = e.name;
			return true;
		}
	}
	return false;
}


int QAD::Enum::value(const QString & n) const {
	for (const Enumerator & e: enum_list) {
		if (e.name == n) return e.value;
	}
	return 0;
}


QString QAD::Enum::name(int v) const {
	for (const Enumerator & e: enum_list) {
		if (e.value == v) return e.name;
	}
	return QString();
}


QList<int> QAD::Enum::values() const {
	QList<int> ret;
	for (const Enumerator & e: enum_list)
		ret << e.value;
	return ret;
}


QStringList QAD::Enum::names() const {
	QStringList ret;
	for (const Enumerator & e: enum_list)
		ret << e.name;
	return ret;
}


QAD::Enum & QAD::Enum::operator<<(const QAD::Enumerator & v) {
	enum_list << v;
	return *this;
}


QAD::Enum & QAD::Enum::operator<<(const QString & v) {
	enum_list << Enumerator(enum_list.size(), v);
	return *this;
}


QAD::Enum & QAD::Enum::operator<<(const QStringList & v) {
	for (const QString & s: v)
		(*this) << s;
	return *this;
}


// QAD::IODevice

QString QAD::IODevice::toString() const {
	QString s;
	if (__QADTypesRegistrator__::instance()->toString_funcs.contains(qMetaTypeId<QAD::IODevice>())) {
		QVariant v;
		v.setValue(*this);
		(*(__QADTypesRegistrator__::instance()->toString_funcs[qMetaTypeId<QAD::IODevice>()]))(v, s);
	}
	return s;
}


// QAD::CursorOverrider

QAD::CursorOverrider::CursorOverrider(const QCursor & c, bool condition): cond(condition) {
	if (!cond) return;
	QApplication::setOverrideCursor(c);
}


QAD::CursorOverrider::~CursorOverrider() {
	if (!cond) return;
	restore();
}


void QAD::CursorOverrider::restore() {
	if (!cond) return;
	if (!on) return;
	on = false;
	QApplication::restoreOverrideCursor();
}


// global

#if QT_VERSION_MAJOR <= 5
QVariant::Type
#else
QMetaType
#endif
typeFromLetter(const QString & l) {
#if QT_VERSION_MAJOR <= 5
	if (l.isEmpty()) return QVariant::String;
	QString ft = l.left(1);
	if (ft == "l") return QVariant::StringList;
	if (ft == "b") return QVariant::Bool;
	if (ft == "n") return QVariant::Int;
	if (ft == "f") return QVariant::Double;
	if (ft == "c") return QVariant::Color;
	if (ft == "r") return QVariant::Rect;
	if (ft == "a") return QVariant::RectF;
	if (ft == "p") return QVariant::Point;
	if (ft == "v") return QVariant::PointF;
	if (ft == "e") return (QVariant::Type)qMetaTypeId<QAD::Enum>();
	if (ft == "F") return (QVariant::Type)qMetaTypeId<QAD::File>();
	if (ft == "D") return (QVariant::Type)qMetaTypeId<QAD::Dir>();
	if (ft == "d") return (QVariant::Type)qMetaTypeId<QAD::IODevice>();
	if (ft == "V") return (QVariant::Type)qMetaTypeId<QAD::MathVector>();
	if (ft == "M") return (QVariant::Type)qMetaTypeId<QAD::MathMatrix>();
	return QVariant::String;
#else
	if (l.isEmpty()) return QMetaType::fromType<QString>();
	QString ft = l.left(1);
	if (ft == "l") return QMetaType::fromType<QStringList>();
	if (ft == "b") return QMetaType::fromType<bool>();
	if (ft == "n") return QMetaType::fromType<int>();
	if (ft == "f") return QMetaType::fromType<double>();
	if (ft == "c") return QMetaType::fromType<QColor>();
	if (ft == "r") return QMetaType::fromType<QRect>();
	if (ft == "a") return QMetaType::fromType<QRectF>();
	if (ft == "p") return QMetaType::fromType<QPoint>();
	if (ft == "v") return QMetaType::fromType<QPointF>();
	if (ft == "e") return QMetaType::fromType<QAD::Enum>();
	if (ft == "F") return QMetaType::fromType<QAD::File>();
	if (ft == "D") return QMetaType::fromType<QAD::Dir>();
	if (ft == "d") return QMetaType::fromType<QAD::IODevice>();
	if (ft == "V") return QMetaType::fromType<QAD::MathVector>();
	if (ft == "M") return QMetaType::fromType<QAD::MathMatrix>();
	return QMetaType::fromType<QString>();
#endif
}


QString uniqueName(QString n, const QStringList & names) {
	if (!names.contains(n)) return n;
	QString num;
	while (!n.isEmpty()) {
		if (n.right(1)[0].isDigit()) {
			num.push_front(n.right(1));
			n.chop(1);
		} else
			break;
	}
	if (!n.endsWith('_') && num.isEmpty()) n += '_';
	int in     = num.toInt() + 1;
	QString nn = n + QString::number(in);
	while (names.contains(nn))
		nn = n + QString::number(++in);
	return nn;
}


int fontHeight(const QWidget * window) {
#ifdef Q_OS_ANDROID
	static int ret = QApplication::fontMetrics().height();
	return ret;
#else
#  if QT_VERSION_MAJOR == 6
	double add_scale = 0.8;
#  else
	double add_scale = 1.0;
#  endif
	// qDebug() << "fontHeight" << w;
	if (window) {
		QWidget * pw = window->window();
		if (pw) return QFontMetrics(pw->font()).height() * add_scale;
	}
	return QFontMetrics(QApplication::font()).height() * add_scale;
#endif
}


int lineThickness(const QWidget * window) {
	return qMax<int>(qRound(fontHeight(window) / 15.), 1);
}


QSize preferredIconSize(float x, const QWidget * window) {
	int s = qMax<int>(8, qRound(fontHeight(window) * x));
#ifdef Q_OS_MACOS
	s /= 1.25;
#endif
#ifdef Q_OS_ANDROID
	s /= 1.4;
#endif
	return QSize(s, s);
}


double appScale(const QWidget * window) {
	return qMax<double>(fontHeight(window) / 15., 1.);
}


void enableHighDPI() {
#if QT_VERSION_MAJOR == 5
	QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
#endif
}


const QMetaObject * getQtMetaObject() {
#if QT_VERSION_MAJOR <= 5
	return qt_getQtMetaObject();
#endif
#if QT_VERSION_MAJOR == 6
	return &(Qt::staticMetaObject);
#endif
	return nullptr;
}
