#include <QtGui>
#include <QFileInfo>
#include <QList>
#include <QStringList>
#include <QPalette>
#include <QColor>

#include "searchresultmodel.h"
#include "dcguiutils.h"
#include "dciconloader.h"
#include "dcgui.h"

//#define _DEBUG_MODEL_

#ifdef _DEBUG_MODEL_
#include <QtDebug>
#endif

SearchListModel::SearchListModel(QObject *parent)
    : QAbstractItemModel(parent), dummy(QList<QVariant>(), NULL)
{
    QList<QVariant> rootData;
    rootData << tr("Count") << tr("File") << tr("Ext") << tr("Size")
             << tr("Exact size") << tr("TTH")   << tr("Path") << tr("Nick")
             << tr("Free slots") << tr("Total slots")
             << tr("IP") << tr("Hub") << tr("Host");

    rootItem = new SearchListItem(rootData);

    sortColumn = -1;
}

SearchListModel::~SearchListModel()
{
    delete rootItem;
}

int SearchListModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return static_cast<SearchListItem*>(parent.internalPointer())->columnCount();
    else
        return rootItem->columnCount();
}

QVariant SearchListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    SearchListItem *item = static_cast<SearchListItem*>(index.internalPointer());

    switch(role) {
        case Qt::DecorationRole: // icon
            if (index.column() == COLUMN_FILENAME)
                return item->icon;
            else if ((index.column() == COLUMN_COUNT) && (item->data(COLUMN_FREESLOTS).toInt() == 0))
                    return DCIconLoader::Instance()->GetPixmap(eiBALL_RED).scaled(14, 14);
            break;
        case Qt::DisplayRole:
            return item->data(index.column());
        case Qt::TextAlignmentRole: {
            const int i_column = index.column();
            bool align_center = (i_column == COLUMN_ALLSLOTS) || (i_column == COLUMN_EXTENSION) ||
                                (i_column == COLUMN_FREESLOTS);
            bool align_right  = (i_column == COLUMN_ESIZE) || (i_column == COLUMN_SIZE ) || (i_column == COLUMN_COUNT);

            if (align_center)
                return Qt::AlignCenter;
            else if (align_right)
                return Qt::AlignRight;

            break;
            }
        case Qt::ForegroundRole:
            if (item->exists_in_share && role == Qt::ForegroundRole)
                return QColor(0x00, 0x80, 0x42);
            break;
        case Qt::ToolTipRole:
            if ((index.column() == COLUMN_COUNT) && (item->data(COLUMN_FREESLOTS).toInt() == 0)){
                QString nick = item->data(COLUMN_NICK).toString();
                QString tooltip = QString(tr("%1 has no free slots.")).arg(nick);;

                return tooltip;
            }
            else if ((index.column() == COLUMN_FILENAME) && (item->exists_in_share)){
                QString file = item->data(COLUMN_FILENAME).toString();
                QString tooltip = QString(tr("%1 already in your share")).arg(file);

                return tooltip;
            }

    }

    return QVariant();
}

Qt::ItemFlags SearchListModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;

    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

QVariant SearchListModel::headerData(int section, Qt::Orientation orientation,
                               int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
        return rootItem->data(section);

    return QVariant();
}

QModelIndex SearchListModel::index(int row, int column, const QModelIndex &parent)
            const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    SearchListItem *parentItem;

    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<SearchListItem*>(parent.internalPointer());

    SearchListItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

QModelIndex SearchListModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    SearchListItem *childItem = static_cast<SearchListItem*>(index.internalPointer());
    SearchListItem *parentItem = childItem->parent();

    if (parentItem == rootItem)
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

int SearchListModel::rowCount(const QModelIndex &parent) const
{
    SearchListItem *parentItem;
    if (parent.column() > 0)
        return 0;

    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<SearchListItem*>(parent.internalPointer());

    return parentItem->childCount();
}

namespace {

template <Qt::SortOrder order>
struct Compare {
    void static sort(int col, QList<SearchListItem*>& items) {
        qStableSort(items.begin(), items.end(), getAttrComp(col));
    }

    void static insertSorted(int col, QList<SearchListItem*>& items, SearchListItem* item) {
        QList<SearchListItem*>::iterator it = qLowerBound(items.begin(), items.end(), item, getAttrComp(col));
        items.insert(it, item);
#ifdef _DEBUG_MODEL_
        qDebug() << "Item inserted at " << items.indexOf(*it) << " position";
#endif
    }

    private:
        typedef bool (*AttrComp)(const SearchListItem * l, const SearchListItem * r);
        AttrComp static getAttrComp(int column) {
            switch (column){
                case COLUMN_FILENAME:
                    return AttrCmp<COLUMN_FILENAME>;
                case COLUMN_EXTENSION:
                    return AttrCmp<COLUMN_EXTENSION>;
                case COLUMN_TTH:
                    return AttrCmp<COLUMN_TTH>;
                case COLUMN_PATH:
                    return AttrCmp<COLUMN_PATH>;
                case COLUMN_NICK:
                    return AttrCmp<COLUMN_NICK>;
                case COLUMN_IP:
                    return AttrCmp<COLUMN_IP>;
                case COLUMN_HUB:
                    return AttrCmp<COLUMN_HUB>;
                case COLUMN_HOST:
                    return AttrCmp<COLUMN_HOST>;
                case COLUMN_SIZE:
                    return AttrCmp<qulonglong, &SearchListItem::size>;
                case COLUMN_ESIZE:
                    return AttrCmp<qulonglong, &SearchListItem::size>;
                case COLUMN_COUNT:
                    return AttrCmp<unsigned,   &SearchListItem::count>;
                case COLUMN_FREESLOTS:
                    return AttrCmp<unsigned,   &SearchListItem::slots_free>;
                case COLUMN_ALLSLOTS:
                    return AttrCmp<unsigned,   &SearchListItem::slots_all>;
            }

            Q_ASSERT_X(false, "getAttrComp", QString("Inncorrect column %1").arg(column).toAscii().constData());
            return 0;
        }
        template <int i>
        bool static AttrCmp(const SearchListItem * l, const SearchListItem * r) {
            return Cmp(QString::localeAwareCompare(l->data(i).toString(), r->data(i).toString()), 0);
        }
        template <typename T, T (SearchListItem::*attr)>
        bool static AttrCmp(const SearchListItem * l, const SearchListItem * r) {
            return Cmp(l->*attr, r->*attr);
       }
        template <typename T>
        bool static Cmp(const T& l, const T& r);
};

template <> template <typename T>
bool inline Compare<Qt::AscendingOrder>::Cmp(const T& l, const T& r) {
    return l < r;
}

template <> template <typename T>
bool inline Compare<Qt::DescendingOrder>::Cmp(const T& l, const T& r) {
    return l > r;
}

} //namespace

void SearchListModel::sort(int column, Qt::SortOrder order) {
    sortColumn = column;
    sortOrder = order;
#ifdef _DEBUG_MODEL_
    if (sortColumn != column)
        qDebug() << "Sorting by " << column << " column and " << DCGuiUtils::SortOrderName(order) << " order.";
#endif
    if (column == -1)
        return;

    emit layoutAboutToBeChanged();
    if (order == Qt::AscendingOrder)
        Compare<Qt::AscendingOrder>().sort(column, rootItem->childItems);
    else if (order == Qt::DescendingOrder)
        Compare<Qt::DescendingOrder>().sort(column, rootItem->childItems);

    emit layoutChanged();
}

void SearchListModel::addResult
        (
        const QString file,
        const QPixmap icon,
        qulonglong size,
        const QString tth,
        const QString path,
        const QString nick,
        const int free_slots,
        const int all_slots,
        const QString ip,
        const QString hub,
        const QString host,
        const bool exists
        )
{
    SearchListItem *item;

    QFileInfo file_info(file);
    QString ext = "";

    if (size > 0)
        ext = file_info.suffix().toUpper();

    SearchListItem * parent = NULL;

    if (tths.contains(tth))
        parent = tths[tth];
    else
        parent = rootItem;

    item = new SearchListItem(QList<QVariant>()
                                      << QVariant() << file << ext << DCGuiUtils::GetSizeString(size)
                                      << size << tth << path << nick << free_slots
                                      << all_slots << ip << hub << host,
                              parent);

    item->icon = icon;
    item->size = size;
    item->slots_all = all_slots;
    item->slots_free = free_slots;
    item->exists_in_share = exists;

    if (parent == rootItem)
        tths.insert(tth, item);

#ifdef _DEBUG_MODEL_
    qDebug() << sortColumn << "; " << rootItem->childCount();
#endif

    if (sortColumn == COLUMN_COUNT && parent != rootItem && rootItem->childItems.contains(parent)) {
        parent->appendChild(item);
        rootItem->childItems.removeAt(rootItem->childItems.indexOf(parent));
        item = parent;
    }
    else if (parent != rootItem && rootItem->childItems.contains(parent)){
        parent->appendChild(item);

        emit layoutChanged();

        return;
    }

#ifdef _DEBUG_MODEL_
    qDebug() << "Inserting new item";
#endif

    if (sortOrder == Qt::AscendingOrder)
        Compare<Qt::AscendingOrder>().insertSorted(sortColumn, rootItem->childItems, item);
    else if (sortOrder == Qt::DescendingOrder)
        Compare<Qt::DescendingOrder>().insertSorted(sortColumn, rootItem->childItems, item);

    emit layoutChanged();
}

int SearchListModel::getSortColumn() const {
    return sortColumn;
}

void SearchListModel::setSortColumn(int c) {
    sortColumn = c;
}

Qt::SortOrder SearchListModel::getSortOrder() const {
    return sortOrder;
}

void SearchListModel::setSortOrder(Qt::SortOrder o) {
    sortOrder = o;
}

const QList<SearchListItem*> &SearchListModel::getRowItems(){
    return rootItem->childItems;
}

bool SearchListModel::isTTHRoot(const QString &hub, const QString &host, const QString &nick, const QString &tth){
    if (!tths.contains(tth))
        return false;

    SearchListItem *item = tths[tth];

    if (!item)
        return false;

    QString i_nick, i_hub, i_host;

    i_nick = item->data(COLUMN_NICK).toString();
    i_hub  = item->data(COLUMN_HUB).toString();
    i_host = item->data(COLUMN_HOST).toString();

    return ((i_nick == nick) && (i_hub == hub) && (i_host == host));
}

const SearchListItem &SearchListModel::getTTHItems(const QString &hub, const QString &host, const QString &nick, const QString &tth){
    if (!isTTHRoot(hub, host, nick, tth))
        return dummy;

    return *tths[tth];
}

void SearchListModel::clearModel(){

#ifdef _DEBUG_MODEL_
    qDebug() << "Cleaning the model.";
#endif
    QList<SearchListItem*> childs = rootItem->childItems;   //Copying list in another place
    rootItem->childItems.clear();                           //Cleaning root of the model. Do not delete items directly from the root item

    qDeleteAll(childs);

    rootItem->childItems.clear();
    tths.clear();

    reset();

    emit layoutChanged();

#ifdef _DEBUG_MODEL_
    qDebug() << "Cleaning done";
#endif
}

SearchListModel::FilterRole SearchListModel::getFilterRole() const{
    return filterRole;
}

void SearchListModel::setFilterRole(SearchListModel::FilterRole role){
    filterRole = role;
}

SearchListItem::SearchListItem(const QList<QVariant> &data, SearchListItem *parent) :
    size(0), count(0), slots_all(0), slots_free(0),
    hide(false), exists_in_share(false),
    itemData(data), parentItem(parent)
{
}

SearchListItem::~SearchListItem()
{
    if (childItems.size() > 0)
        qDeleteAll(childItems);
}

void SearchListItem::appendChild(SearchListItem *item) {
    childItems.append(item);
    count = childItems.size();
}

SearchListItem *SearchListItem::child(int row) {
    return childItems.value(row);
}

int SearchListItem::childCount() const {
    return childItems.count();
}

int SearchListItem::columnCount() const {
    return itemData.count();
}

QVariant SearchListItem::data(int column) const {
    if (column == COLUMN_COUNT && childItems.size() > 0 && parentItem != 0)
        return childItems.size()+1;

    return itemData.value(column);
}

SearchListItem *SearchListItem::parent() {
    return parentItem;
}

int SearchListItem::row() const {
    if (parentItem)
        return parentItem->childItems.indexOf(const_cast<SearchListItem*>(this));

    return 0;
}
