/*
 * chatviewthemeprovider_priv.cpp
 * Copyright (C) 2010-2017  Sergey Ilinykh
 *
 * 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 2
 * 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */

#include <QPointer>
#include <QBuffer>
#include <QUrlQuery>

#include "psicon.h"
#include "chatviewthemeprovider_priv.h"
#include "psithemeprovider.h"
#include "psiiconset.h"
#ifdef WEBENGINE
# include <QWebEngineProfile>
# include "themeserver.h"
#else
# include "networkaccessmanager.h"
# include <QNetworkRequest>
#endif
#include "xmpp_vcard.h"
#include "avatars.h"

static QPointer<ChatViewCon> cvCon;

#ifdef WEBENGINE

ChatViewUrlRequestInterceptor::ChatViewUrlRequestInterceptor(QObject *parent) :
    QWebEngineUrlRequestInterceptor(parent) {}

void ChatViewUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info)
{
    QString q = info.firstPartyUrl().query();
    if (q.startsWith(QLatin1String("psiId="))) {
        // handle urls like this http://127.0.0.1:12345/?psiId=ab4ba
        info.setHttpHeader(QByteArray("PsiId"), q.mid(sizeof("psiId=")-1).toUtf8());
    }
}
#else
class AvatarHandler : public NAMDataHandler
{
public:
    bool data(const QNetworkRequest &req, QByteArray &data, QByteArray &mime) const
    {
        QString path = req.url().path();
        if (path.startsWith(QLatin1String("/psiglobal/avatar/"))) {
            QString hash = path.mid(sizeof("/psiglobal/avatar")); // no / because of null pointer
            if (hash == QLatin1String("default.png")) {
                QPixmap p;
                QBuffer buffer(&data);
                buffer.open(QIODevice::WriteOnly);
                p = IconsetFactory::icon(QLatin1String("psi/default_avatar")).pixmap();
                if (p.save(&buffer, "PNG")) {
                    mime = "image/png";
                    return true;
                }
            } else {
                AvatarFactory::AvatarData ad = AvatarFactory::avatarDataByHash(hash);
                if (!ad.data.isEmpty()) {
                    data = ad.data;
                    mime = ad.metaType.toLatin1();
                    return true;
                }
            }
        }
        return false;
    }
};

class IconHandler : public NAMDataHandler
{
public:
    bool data(const QNetworkRequest &req, QByteArray &data, QByteArray &mime) const
    {
        QUrl url = req.url();
        QString path = url.path();
        if (!path.startsWith(QLatin1String("/psiicon/"))) {
            return false;
        }
        QString iconId = path.mid(sizeof("/psiicon/") - 1);
        int w = QUrlQuery(url.query()).queryItemValue("w").toInt();
        int h = QUrlQuery(url.query()).queryItemValue("h").toInt();
        PsiIcon icon = IconsetFactory::icon(iconId);
        if (w && h && !icon.isAnimated()) {
            QBuffer buffer(&data);
            buffer.open(QIODevice::WriteOnly);
            if (icon.pixmap().scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation)
                    .toImage().save(&buffer, "PNG") && data.size()) {
                mime = "image/png";
                return true;
            }
        } else { //scaling impossible, return as is. do scaling with help of css or html attributes
            data = icon.raw();
            if (!data.isEmpty()) {
                mime = image2type(data).toLatin1();
                return true;
            }
        }

        return false;
    }
};

class ThemesDirHandler : public NAMDataHandler
{
public:
    bool data(const QNetworkRequest &req, QByteArray &data, QByteArray &mime) const
    {
        QString path = req.url().path();
        if (path.startsWith("/psithemes/")) {
            QString fn = path.mid(sizeof("/psithemes"));
            fn.replace("..", ""); // a little security
            fn = PsiThemeProvider::themePath(fn);

            if (!fn.isEmpty()) {
                QFile f(fn);
                if (f.open(QIODevice::ReadOnly)) {
                    if (fn.endsWith(QLatin1String(".js"))) {
                        mime = "application/javascript;charset=utf-8";
                    }
                    data = f.readAll();
                    f.close();
                    return true;
                }
            }
        }
        return false;
    }
};

#endif

ChatViewCon::ChatViewCon(PsiCon *pc) : QObject(pc), pc(pc)
{
#ifdef WEBENGINE
    // init something here?
    themeServer = new ThemeServer(this);

    // handler reading data from themes directory
    ThemeServer::Handler themesDirHandler = [&](qhttp::server::QHttpRequest* req, qhttp::server::QHttpResponse* res) -> bool
    {
        QString fn = req->url().path().mid(sizeof("/psithemes"));
        fn.replace("..", ""); // a little security
        fn = PsiThemeProvider::themePath(fn);

        if (!fn.isEmpty()) {
            QFile f(fn);
            if (f.open(QIODevice::ReadOnly)) {
                res->setStatusCode(qhttp::ESTATUS_OK);
                if (fn.endsWith(QLatin1String(".js"))) {
                    res->headers().insert("Content-Type", "application/javascript;charset=utf-8");
                }
                if (fn.endsWith(QLatin1String(".css"))) {
                    res->headers().insert("Content-Type", "text/css;charset=utf-8");
                }
                res->end(f.readAll());
                f.close();
                return true;
            }
        }
        return false;
    };

    ThemeServer::Handler iconsHandler = [&](qhttp::server::QHttpRequest* req, qhttp::server::QHttpResponse* res) -> bool
    {
        QString name = req->url().path().mid(sizeof("/psiicon"));
        QByteArray ba = IconsetFactory::raw(name);

        if (!ba.isEmpty()) {
            res->setStatusCode(qhttp::ESTATUS_OK);
            res->end(ba);
            return true;
        }
        return false;
    };

    ThemeServer::Handler avatarsHandler = [&](qhttp::server::QHttpRequest* req, qhttp::server::QHttpResponse* res) -> bool
    {
        QString hash = req->url().path().mid(sizeof("/psiglobal/avatar")); // no / because of null pointer
        QByteArray ba;
        if (hash == QLatin1String("default.png")) {
            QPixmap p;
            QBuffer buffer(&ba);
            buffer.open(QIODevice::WriteOnly);
            p = IconsetFactory::icon("psi/default_avatar").pixmap();
            if (p.save(&buffer, "PNG")) {
                res->setStatusCode(qhttp::ESTATUS_OK);
                res->headers().insert("Content-Type", "image/png");
                res->end(ba);
                return true;
            }
        } else {
            AvatarFactory::AvatarData ad = AvatarFactory::avatarDataByHash(hash);
            if (!ad.data.isEmpty()) {
                res->setStatusCode(qhttp::ESTATUS_OK);
                res->headers().insert("Content-Type", ad.metaType.toLatin1());
                res->end(ad.data);
                return true;
            }
        }

        return false;
    };

    themeServer->registerPathHandler("/psithemes/", themesDirHandler);
    themeServer->registerPathHandler("/psiicon/", iconsHandler);
    themeServer->registerPathHandler("/psiglobal/avatar/", avatarsHandler);

    requestInterceptor = new ChatViewUrlRequestInterceptor(this);
    QWebEngineProfile::defaultProfile()->setRequestInterceptor(requestInterceptor);
#else
    pc->networkAccessManager()->registerPathHandler(QSharedPointer<NAMDataHandler>(new ThemesDirHandler()));
    pc->networkAccessManager()->registerPathHandler(QSharedPointer<NAMDataHandler>(new IconHandler()));
    pc->networkAccessManager()->registerPathHandler(QSharedPointer<NAMDataHandler>(new AvatarHandler()));
#endif
}

ChatViewCon::~ChatViewCon()
{
#ifdef WEBENGINE
    QWebEngineProfile::defaultProfile()->setRequestInterceptor(0);
    delete requestInterceptor;
    delete themeServer;
#endif
}

ChatViewCon *ChatViewCon::instance()
{
    return cvCon.data();
}

void ChatViewCon::init(PsiCon *pc) {
    if (!cvCon) {
        cvCon = new ChatViewCon(pc);
    }
}

bool ChatViewCon::isReady()
{
    return cvCon;
}

