#include "main-screen.h"
#include <QClipboard>
#include <QDir>
#include <QEventLoop>
#include <QGuiApplication>
#include <QMimeDatabase>
#include <QSettings>
#include <QStandardPaths>
#include "downloader/image-downloader.h"
#include "functions.h"
#include "logger.h"
#include "models/favorite.h"
#include "models/image.h"
#include "models/page.h"
#include "models/profile.h"
#include "models/site.h"
#include "models/site-factory.h"
#include "models/source.h"
#include "settings.h"
#include "share/share-utils.h"
#include "utils/logging.h"
#include "models/qml-site.h"


MainScreen::MainScreen(Profile *profile, ShareUtils *shareUtils, QObject *parent)
	: QObject(parent), m_profile(profile), m_shareUtils(shareUtils)
{
	connect(&Logger::getInstance(), &Logger::newLog, this, &MainScreen::newLog);
	logSystemInformation(m_profile);

	refreshSites();
	refreshSources();
	refreshFavorites();

	connect(m_profile, &Profile::sitesChanged, this, &MainScreen::refreshSites);
	connect(m_profile, &Profile::favoritesChanged, this, &MainScreen::refreshFavorites);
}

void MainScreen::refreshSites()
{
	qDeleteAll(m_sites);
	m_sites.clear();

	for (Site *site : m_profile->getSites().values()) {
		Source *source = m_profile->getSources().value(site->type());
		m_sites.append(new QmlSite(site, source, this));
	}

	emit sitesChanged();
}

void MainScreen::refreshSources()
{
	m_sources.clear();
	for (Source *source : m_profile->getSources().values()) {
		m_sources.append(source->getName());
	}
	emit sourcesChanged();
}

void MainScreen::refreshFavorites()
{
	m_favorites.clear();
	for (const Favorite &fav : m_profile->getFavorites()) {
		m_favorites.append(fav.getName(false));
	}
	emit favoritesChanged();
}

void MainScreen::newLog(const QString &message)
{
	if (!m_log.isEmpty()) {
		m_log += "<br/>";
	}
	m_log += logToHtml(message);

	emit logChanged();
}

void MainScreen::downloadImage(const QSharedPointer<Image> &image)
{
	const QSettings *settings = m_profile->getSettings();
	const QString filename = settings->value("Save/filename", "%md5%.%ext%").toString();
	const QString path = QDir::toNativeSeparators(settings->value("Save/path", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).toString());

	if (filename.isEmpty() || path.isEmpty()) {
		LOG("Empty save filename or directory", Logger::Error);
		return;
	}

	auto *downloader = new ImageDownloader(m_profile, image, filename, path, 1, true, true, this, false);
	connect(downloader, &ImageDownloader::saved, downloader, &ImageDownloader::deleteLater);
	downloader->save();
}

void MainScreen::shareImage(const QSharedPointer<Image> &image)
{
	const QString filename = "image_to_share.%ext%";
	const QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);

	ImageDownloader downloader(m_profile, image, filename, path, 0, false, false, this);

	QEventLoop loop;
	QObject::connect(&downloader, &ImageDownloader::saved, &loop, &QEventLoop::quit, Qt::QueuedConnection);
	downloader.save();
	loop.exec();

	const QString savePath = image->savePath();
	const QString mimeType = QMimeDatabase().mimeTypeForFile(savePath).name();
	m_shareUtils->sendFile(savePath, mimeType, "Share image");
}

QString MainScreen::addSite(const QString &type, const QString &host, bool https)
{
	const auto sources = m_profile->getSources().values();

	// Find the source
	Source *source = nullptr;
	for (Source *src : sources) {
		if (src->getName() == type) {
			source = src;
			break;
		}
	}
	if (source == nullptr) {
		return "Invalid source";
	}

	// Add site
	Site *site = SiteFactory::fromUrl(host, source, m_profile);
	m_profile->addSite(site);

	return QString();
}

void MainScreen::addFavorite(const QString &query, const QString &siteUrl)
{
	Favorite fav(query);
	fav.setSites({ m_profile->getSites().value(siteUrl) });
	m_profile->addFavorite(fav);
}
void MainScreen::removeFavorite(const QString &query)
{
	m_profile->removeFavorite(Favorite(query));
}

void MainScreen::loadSuggestions(const QString &prefix, int limit)
{
	m_autoComplete.clear();

	// Ignore empty searches or completions right after a space
	if (prefix.isEmpty()) {
		emit autoCompleteChanged();
		return;
	}

	// Get the first element for which the comparison "elt < prefix" is wrong
	const auto &cmp = m_profile->getAutoComplete();
	auto it = std::lower_bound(cmp.constBegin(), cmp.constEnd(), prefix);

	// Get max $limit results starting with the prefix
	while (it != cmp.constEnd() && it->startsWith(prefix) && m_autoComplete.count() < limit) {
		m_autoComplete.append(*it);
		++it;
	}

	emit autoCompleteChanged();
}

bool MainScreen::exportSettings(const QString &dest)
{
	return QFile::copy(m_profile->getSettings()->fileName(), dest);
}

bool MainScreen::importSettings(const QString &source)
{
	QSettings sourceSettings(source, QSettings::IniFormat);
	if (sourceSettings.status() != QSettings::NoError) {
		return false;
	}

	QSettings *settings = m_profile->getSettings();
	settings->clear();

	for (const QString &key : sourceSettings.allKeys()) {
		settings->setValue(key, sourceSettings.value(key));
	}

	settings->sync();
	emit settingsChanged();

	return true;
}

bool MainScreen::removeSite(QmlSite *site)
{
	m_profile->removeSite(site->rawSite());
	m_sites.removeAll(site);
	emit sitesChanged();
	return true;
}

void MainScreen::setClipboardText(const QString &text)
{
	QGuiApplication::clipboard()->setText(text);
}

QString MainScreen::toLocalFile(const QString &url)
{
	if (url.startsWith("file:")) {
		QUrl u(url);
		if (u.isValid()) {
			return u.toLocalFile();
		}
	} else if (url.startsWith("content://com.android.externalstorage.documents")) {
		const QString part = QUrl::fromPercentEncoding(url.mid(47).toLatin1());
		const int index = part.indexOf(':');
		if (index != -1) {
			return "/storage/emulated/0/" + part.mid(index + 1);
		}
	}
	return url;
}

QString MainScreen::settingsFileName() const
{
	return m_profile->getSettings()->fileName();
}

QString MainScreen::getBlacklist()
{
	return m_profile->getBlacklist().toString();
}

void MainScreen::setBlacklist(const QString &text)
{
	Blacklist blacklist;
	for (const QString &tags : text.split("\n", Qt::SkipEmptyParts)) {
		blacklist.add(tags.trimmed().split(' ', Qt::SkipEmptyParts));
	}
	m_profile->setBlacklistedTags(blacklist);
}

QString MainScreen::getIgnored()
{
	return m_profile->getIgnored().join('\n');
}

void MainScreen::setIgnored(const QString &ignored)
{
	m_profile->setIgnored(ignored.split('\n', Qt::SkipEmptyParts));
}
