/*
 * Copyright (C) 2010 Rion
 *
 * 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
 * Lesser 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 <QCryptographicHash>
#include <QFile>

#include "xmpp_bitsofbinary.h"
#include "xmpp_xmlcommon.h"
#include "xmpp_client.h"
#include "xmpp_tasks.h"

using namespace XMPP;

class BoBData::Private : public QSharedData
{
public:
    QByteArray data;
    QString type;
    QString cid;
    unsigned int maxAge;
};

BoBData::BoBData()
    : d(new Private)
{

}

BoBData::BoBData(const BoBData &other)
    : d(other.d)
{

}

BoBData::BoBData(const QDomElement &e)
    : d(new Private)
{
    fromXml(e);
}

BoBData::~BoBData()
{

}

BoBData & BoBData::operator=(const BoBData &other)
{
    if (this==&other) return *this; //Protect against self-assignment
    d = other.d;
    return *this;
}

bool BoBData::isNull() const
{
    return d->cid.isEmpty() || d->data.isNull();
}

QString BoBData::cid() const
{
    return d->cid;
}

void BoBData::setCid(const QString &cid)
{
    d->cid = cid;
}

QByteArray BoBData::data() const
{
    return d->data;
}

void BoBData::setData(const QByteArray &data)
{
    d->data = data;
}

QString BoBData::type() const
{
    return d->type;
}

void BoBData::setType(const QString &type)
{
    d->type = type;
}

unsigned int BoBData::maxAge() const
{
    return d->maxAge;
}

void BoBData::setMaxAge(unsigned int maxAge)
{
    d->maxAge = maxAge;
}

void BoBData::fromXml(const QDomElement &data)
{
    d->cid = data.attribute("cid");
    d->maxAge = data.attribute("max-age").toInt();
    d->type = data.attribute("type");
    d->data = QCA::Base64().stringToArray(data.text().replace("\n",""))
            .toByteArray();
}

QDomElement BoBData::toXml(QDomDocument *doc) const
{
    QDomElement data = doc->createElement("data");
    data.setAttribute("xmlns", "urn:xmpp:bob");
    data.setAttribute("cid", d->cid);
    data.setAttribute("max-age", d->maxAge);
    data.setAttribute("type", d->type);
    data.appendChild(doc->createTextNode(QCA::Base64().arrayToString(d->data)));
    return data;
}



// ---------------------------------------------------------
// BoBCache
// ---------------------------------------------------------
BoBCache::BoBCache(QObject *parent)
    : QObject(parent)
{

}


//------------------------------------------------------------------------------
// BoBManager
//------------------------------------------------------------------------------
BoBManager::BoBManager(Client *client)
    : QObject(client)
    , _cache(0)
{
    new JT_BoBServer(client->rootTask());
}

void BoBManager::setCache(BoBCache *cache)
{
    _cache = cache;
}

BoBData BoBManager::bobData(const QString &cid)
{
    BoBData bd;
    if (_cache) {
        bd = _cache->get(cid);
    }
    if (bd.isNull() && _localFiles.contains(cid)) {
        QPair<QString, QString> fileData = _localFiles.value(cid);
        QFile file(fileData.first);
        if (file.open(QIODevice::ReadOnly)) {
            bd.setCid(cid);
            bd.setData(file.readAll());
            bd.setMaxAge(0);
            bd.setType(fileData.second);
        }
    }
    return bd;
}

BoBData BoBManager::append(const QByteArray &data, const QString &type,
                                unsigned int maxAge)
{
    BoBData b;
    b.setCid(QString("sha1+%1@bob.xmpp.org").arg(QString(
        QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex())));
    b.setData(data);
    b.setMaxAge(maxAge);
    b.setType(type);
    if (_cache) {
        _cache->put(b);
    }
    return b;
}

QString BoBManager::append(QFile &file, const QString &type)
{
    bool isOpen = file.isOpen();
    if (isOpen || file.open(QIODevice::ReadOnly)) {
        QString cid = QString("sha1+%1@bob.xmpp.org").arg(
            QString(QCryptographicHash::hash(file.readAll(),
                                            QCryptographicHash::Sha1).toHex()));
        _localFiles[cid] = QPair<QString,QString>(file.fileName(), type);
        if (!isOpen) {
            file.close();
        }
        return cid;
    }
    return QString();
}

void BoBManager::append(const BoBData &data)
{
    if (!data.isNull() && _cache) {
        _cache->put(data);
    }
}
