/*******************************************************************************
 **
 ** smolbote: yet another qute browser
 ** Copyright (C) 2017  Xian Nox
 **
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License as published by
 ** the Free Software Foundation, either version 3 of the License, or
 ** (at your option) any later version.
 **
 ** This program is distributed in the hope that it will be useful,
 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ** GNU General Public License for more details.
 **
 ** You should have received a copy of the GNU General Public License
 ** along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **
 ******************************************************************************/

#include "browser.h"
#include "mainwindow.h"
#include <QtWebEngine>
#include <QMessageBox>

Browser::Browser(int &argc, char *argv[]) :
    QApplication(argc, argv)
{
    setApplicationName("smolbote");

    // This lets the web view automatically scale on high-dpi displays.
    setAttribute(Qt::AA_EnableHighDpiScaling);

    m_settings = nullptr;
    m_localServer = nullptr;

    m_networkAccessManager = nullptr;
    m_urlRequestInterceptor = nullptr;
    m_bookmarksManager = nullptr;
    m_downloadManager = nullptr;
    m_blocklistManager = nullptr;
}

Browser::~Browser()
{
    qDeleteAll(m_windows);
    m_windows.clear();

    if(m_networkAccessManager) {
        delete m_networkAccessManager;
    }

    if(m_bookmarksManager) {
        delete m_bookmarksManager;
    }

    if(m_downloadManager) {
        delete m_downloadManager;
    }
}

QString Browser::applicationLongVersion() const
{
#ifdef GIT_DESCRIBE
    return QString(GIT_DESCRIBE);
#else
    return applicationVersion();
#endif
}

bool Browser::prepare(QStringList urls)
{
    if(m_settings->value("browser.singleInstance", true).toBool()) {
        QString serverName = m_settings->value("browser.localSocket", "smolbote-singlelock").toString();

        // Check for other running instance
        QLocalSocket socket;
        socket.connectToServer(serverName);
        if(socket.waitForConnected(500)) {
            QTextStream stream(&socket);
            stream << urls.join('|');
            stream.flush();
            socket.waitForBytesWritten();
            return false;
        }

        // There is no other instance
        m_localServer = new QLocalServer(this);
        connect(m_localServer, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
        if (!m_localServer->listen(serverName) && m_localServer->serverError() == QAbstractSocket::AddressInUseError) {
            // Could not create local server because the socket is already in use
            QLocalServer::removeServer(serverName);
            if (!m_localServer->listen(serverName)) {
                // Couldn't free the socket
                qWarning("Could not create local socket %s.", qPrintable(serverName));
            }
        } else {
            qDebug("Created local socket.");
        }
    }

    if(m_settings->isEmpty()) {
        // There are no keys in the settings
        QMessageBox::information(0,
                                 tr("Configuration is empty"),
                                 tr("The configuration file <i>%1</i> is empty. Using default values").arg(m_settings->filePath()));
    }

    m_networkAccessManager = new QNetworkAccessManager();
    m_bookmarksManager = new BookmarksWidget;
    m_downloadManager = new DownloadsWidget;
    m_blocklistManager = new BlockerManager;

    QtWebEngine::initialize();

    return true;
}

Browser *Browser::instance()
{
    return static_cast<Browser *>(QCoreApplication::instance());
}

Settings *Browser::settings()
{
    return m_settings;
}

QNetworkAccessManager *Browser::network()
{
    return m_networkAccessManager;
}

BookmarksWidget *Browser::bookmarks()
{
    return m_bookmarksManager;
}

DownloadsWidget *Browser::downloads()
{
    return m_downloadManager;
}

BlockerManager *Browser::blocklists()
{
    return m_blocklistManager;
}

void Browser::setConfigPath(const QString &path)
{
    // set custom config path if any
    if(!path.isEmpty()) {
        m_settings = new Settings(path);
        return;
    }

    // check if config file exists for this user
    QString cpath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/poi.conf";
    if(QFile::exists(cpath)) {
        m_settings = new Settings(cpath);
    }

    // check system-specific locations
#ifdef Q_OS_LINUX
    else if(QFile::exists("/usr/share/smolbote/poi.conf")) {
        m_settings = new Settings("/usr/share/smolbote/poi.conf");
    } else if(QFile::exists("/usr/local/share/smolbote/poi.conf")) {
        m_settings = new Settings("/usr/local/share/smolbote/poi.conf");
    }
#endif

    else {
        m_settings = new Settings();
    }
}

void Browser::addWindow(MainWindow *window)
{
    if(m_windows.contains(window)) {
        return;
    }

    m_windows.append(window);
    connect(window, &QObject::destroyed, [this, window]() {
        this->removeWindow(window);
    });

    window->show();
}

MainWindow *Browser::mainWindow()
{
    if(m_windows.isEmpty()) {
        addWindow(new MainWindow());
    }
    return m_windows.first();
}

void Browser::removeWindow(MainWindow *window)
{
    m_windows.removeOne(window);
}

WebEngineProfile* Browser::profile(const QString name)
{
    if(!m_profiles.contains(name)) {
        // name can be empty --> off-the-record profile
        if(name.isEmpty()) {
            m_profiles.insert(name, new WebEngineProfile(this));
        } else {
            m_profiles.insert(name, new WebEngineProfile(name, this));
        }

        if(!m_urlRequestInterceptor) {
            m_urlRequestInterceptor = new UrlRequestInterceptor(this);
            m_urlRequestInterceptor->setSubscription(m_blocklistManager);
        }

        m_profiles[name]->setRequestInterceptor(m_urlRequestInterceptor);

        connect(m_profiles[name], SIGNAL(downloadRequested(QWebEngineDownloadItem*)), downloads(), SLOT(addDownload(QWebEngineDownloadItem*)));
    }

    return m_profiles[name];
}

void Browser::handleNewConnection()
{
    QLocalSocket *socket = m_localServer->nextPendingConnection();
    if(!socket) {
        // null socket -> return
        return;
    }

    socket->waitForReadyRead();
    const QStringList urls = QString(socket->readAll()).split('|');
    delete socket;

    QStringList::const_iterator i;
    for(i = urls.constBegin(); i != urls.constEnd(); ++i) {
        mainWindow()->addNewTab(QUrl::fromUserInput(*i));
    }
}