// Squawk messenger. 
// Copyright (C) 2019  Yury Gubich <blue@macaw.me>
// 
// 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 "vcardhandler.h"
#include "core/account.h"

Core::VCardHandler::VCardHandler(Account* account):
    QObject(),
    acc(account),
    avatarHash(),
    avatarType()
{
    QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
    path += "/" + acc->name;
    QDir dir(path);

    if (!dir.exists()) {
        bool res = dir.mkpath(path);
        if (!res) {
            qDebug() << "Couldn't create a cache directory for account" << acc->name;
            throw 22;
        }
    }

    QFile* avatar = new QFile(path + "/avatar.png");
    QString type = "png";
    if (!avatar->exists()) {
        delete avatar;
        avatar = new QFile(path + "/avatar.jpg");
        type = "jpg";
        if (!avatar->exists()) {
            delete avatar;
            avatar = new QFile(path + "/avatar.jpeg");
            type = "jpeg";
            if (!avatar->exists()) {
                delete avatar;
                avatar = new QFile(path + "/avatar.gif");
                type = "gif";
            }
        }
    }

    if (avatar->exists()) {
        if (avatar->open(QFile::ReadOnly)) {
            QCryptographicHash sha1(QCryptographicHash::Sha1);
            sha1.addData(avatar);
            avatarHash = sha1.result();
            avatarType = type;
        }
    }
}

Core::VCardHandler::~VCardHandler() {}

void Core::VCardHandler::initialize() {
    connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived);
    //for some reason it doesn't work, launching from common handler
    //connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived);

    if (avatarType.size() != 0) {
        acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
        acc->presence.setPhotoHash(avatarHash.toUtf8());
    } else {
        acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
    }
}

void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) {
    QString id = card.from();
    QStringList comps = id.split("/");
    QString jid = comps.front().toLower();
    QString resource("");
    if (comps.size() > 1) {
        resource = comps.back();
    }
    RosterItem* item = acc->rh->getRosterItem(jid);

    if (item == nullptr) {
        if (jid == acc->getBareJid())
            onOwnVCardReceived(card);
        else
            qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";

        return;
    }

    Shared::VCard vCard;
    item->handleResponseVCard(card, resource, vCard);
    acc->delay->receivedVCard(id, vCard);
}

void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) {
    QByteArray ava = card.photo();
    bool avaChanged = false;
    QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/";
    if (ava.size() > 0) {
        QCryptographicHash sha1(QCryptographicHash::Sha1);
        sha1.addData(ava);
        QString newHash(sha1.result());
        QMimeDatabase db;
        QMimeType newType = db.mimeTypeForData(ava);
        if (avatarType.size() > 0) {
            if (avatarHash != newHash) {
                QString oldPath = path + "avatar." + avatarType;
                QFile oldAvatar(oldPath);
                bool oldToRemove = false;
                if (oldAvatar.exists()) {
                    if (oldAvatar.rename(oldPath + ".bak")) {
                        oldToRemove = true;
                    } else {
                        qDebug() << "Received new avatar for account" << acc->name << "but can't get rid of the old one, doing nothing";
                    }
                }
                QFile newAvatar(path + "avatar." + newType.preferredSuffix());
                if (newAvatar.open(QFile::WriteOnly)) {
                    newAvatar.write(ava);
                    newAvatar.close();
                    avatarHash = newHash;
                    avatarType = newType.preferredSuffix();
                    avaChanged = true;
                } else {
                    qDebug() << "Received new avatar for account" << acc->name << "but can't save it";
                    if (oldToRemove) {
                        qDebug() << "rolling back to the old avatar";
                        if (!oldAvatar.rename(oldPath)) {
                            qDebug() << "Couldn't roll back to the old avatar in account" << acc->name;
                        }
                    }
                }
            }
        } else {
            QFile newAvatar(path + "avatar." + newType.preferredSuffix());
            if (newAvatar.open(QFile::WriteOnly)) {
                newAvatar.write(ava);
                newAvatar.close();
                avatarHash = newHash;
                avatarType = newType.preferredSuffix();
                avaChanged = true;
            } else {
                qDebug() << "Received new avatar for account" << acc->name << "but can't save it";
            }
        }
    } else {
        if (avatarType.size() > 0) {
            QFile oldAvatar(path + "avatar." + avatarType);
            if (!oldAvatar.remove()) {
                qDebug() << "Received vCard for account" << acc->name << "without avatar, but can't get rid of the file, doing nothing";
            } else {
                avatarType = "";
                avatarHash = "";
                avaChanged = true;
            }
        }
    }

    if (avaChanged) {
        QMap<QString, QVariant> change;
        if (avatarType.size() > 0) {
            acc->presence.setPhotoHash(avatarHash.toUtf8());
            acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
            change.insert("avatarPath", path + "avatar." + avatarType);
        } else {
            acc->presence.setPhotoHash("");
            acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto);
            change.insert("avatarPath", "");
        }
        acc->client.setClientPresence(acc->presence);
        emit acc->changed(change);
    }

    Shared::VCard vCard;
    initializeVCard(vCard, card);

    if (avatarType.size() > 0) {
        vCard.setAvatarType(Shared::Avatar::valid);
        vCard.setAvatarPath(path + "avatar." + avatarType);
    } else {
        vCard.setAvatarType(Shared::Avatar::empty);
    }

    if (acc->delay->isOwnVCardPending())
        acc->delay->receivedOwnVCard(vCard);
}

void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_presence) {
    if (!acc->delay->isOwnVCardPending()) {
        switch (p_presence.vCardUpdateType()) {
            case QXmppPresence::VCardUpdateNone:            //this presence has nothing to do with photo
                break;
            case QXmppPresence::VCardUpdateNotReady:        //let's say the photo didn't change here
                break;
            case QXmppPresence::VCardUpdateNoPhoto:         //there is no photo, need to drop if any
                if (avatarType.size() > 0)
                    acc->delay->getOwnVCard();
                break;
            case QXmppPresence::VCardUpdateValidPhoto:      //there is a photo, need to load
                if (avatarHash != p_presence.photoHash())
                    acc->delay->getOwnVCard();
                break;
        }
    }
}

void Core::VCardHandler::uploadVCard(const Shared::VCard& card) {
    QXmppVCardIq iq;
    initializeQXmppVCard(iq, card);

    if (card.getAvatarType() != Shared::Avatar::empty) {
        QString newPath = card.getAvatarPath();
        QString oldPath = getAvatarPath();
        QByteArray data;
        QString type;
        if (newPath != oldPath) {
            QFile avatar(newPath);
            if (!avatar.open(QFile::ReadOnly)) {
                qDebug() << "An attempt to upload new vCard to account" << acc->name
                << "but it wasn't possible to read file" << newPath
                << "which was supposed to be new avatar, uploading old avatar";
                if (avatarType.size() > 0) {
                    QFile oA(oldPath);
                    if (!oA.open(QFile::ReadOnly)) {
                        qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar";
                    } else {
                        data = oA.readAll();
                    }
                }
            } else {
                data = avatar.readAll();
            }
        } else {
            if (avatarType.size() > 0) {
                QFile oA(oldPath);
                if (!oA.open(QFile::ReadOnly))
                    qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar";
                else
                    data = oA.readAll();
            }
        }

        if (data.size() > 0) {
            QMimeDatabase db;
            type = db.mimeTypeForData(data).name();
            iq.setPhoto(data);
            iq.setPhotoType(type);
        }
    }

    acc->vm->setClientVCard(iq);
    onOwnVCardReceived(iq);
}

QString Core::VCardHandler::getAvatarPath() const {
    if (avatarType.size() == 0)
        return "";
    else
        return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType;
}