/*
 * Copyright 2014 Mihai Niculescu <q.quark@gmail.com>
 *
 * This file is part of EqualX Project (https://launchpad.net/equalx/)
 *
 * EqualX 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.
 *
 * EqualX 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 <QDebug>
#include <QFile>
#include <QMimeData>
#include <QStringList>

#include "Library/Library.h"

#include "BookmarksPanel/BookmarkItem.h"
#include "BookmarksPanel/BookmarksItemModel.h"


BookmarksItemModel::BookmarksItemModel(LibraryManager *lib, QObject *parent) :
    QAbstractItemModel(parent)
{
    mLibrary = lib;

    LibraryModelData rootData;
    rootData.id=1;
    rootData.name = "ROOT ITEM";
    rootData.type = LibraryModelData::TypeFolder;
    mRootItem = new BookmarkItem(rootData, 0);
    mRootItem->mModel = this;

    connect(mLibrary, SIGNAL(bookmarkFolderChanged(QString)), this, SLOT(onFolderChanged(QString)) );

}

BookmarksItemModel::~BookmarksItemModel()
{
    delete mRootItem;
}

QVariant BookmarksItemModel::data(const QModelIndex &index, int role) const
{
    //qDebug() << "data() ";

    if (!index.isValid())
        return QVariant();

    if (role != Qt::DisplayRole)
        return QVariant();

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

    if(!item){
        qDebug() << "data() item does not exists";
    }

    if(item==mRootItem){
        qDebug() << "data() item == mRootItem";
    }

    // qDebug() << "data() returning value";

    return QVariant::fromValue(item->data());
}

bool BookmarksItemModel::canFetchMore(const QModelIndex &parent) const
{
    BookmarkItem *item = static_cast<BookmarkItem*>(parent.internalPointer());

    if(!item)
        item = mRootItem;


    //qDebug() << "canFetchMore("<< item->data().name <<") childCount=" << item->childCount() << " In DB= " <<  mLibrary->childrenCountInFolder(item->data().id);

    if(!item->data().isFolder()) return false;

    return (item->childCount() < mLibrary->childrenCountInFolder(item->data().id));
}

void BookmarksItemModel::fetchMore(const QModelIndex &parent)
{
    BookmarkItem* parentItem = static_cast<BookmarkItem*>(parent.internalPointer());

    if(!parentItem)
        parentItem = mRootItem;

    LibraryModelData curData = parentItem->data();
    LibraryRowsList rows = mLibrary->getChildren(curData.id);

    qDebug() << "fetchMore("<< parentItem->data().name <<") children: "<< parentItem->rowCount() <<" has in DB List size " << rows.size();

    beginInsertRows(parent, 0, rows.count());
    foreach(LibraryModelData row, rows){
        parentItem->appendChild(new BookmarkItem(row, parentItem) );
    }
    endInsertRows();

}

bool BookmarksItemModel::hasChildren(const QModelIndex &parent) const
{
    BookmarkItem *item = static_cast<BookmarkItem*>(parent.internalPointer());

    if(!item){
        item = mRootItem;
    }

    if(!item->data().isFolder()) return false;

    //qDebug() << "hasChildren("<< item->data().name <<") In DB= " << mLibrary->childrenCountInFolder(item->data().id);

    return mLibrary->childrenCountInFolder(item->data().id);
}

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

    Qt::ItemFlags itemFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
    LibraryModelData itemData = qvariant_cast<LibraryModelData>(data(index));

    if(itemData.type == LibraryModelData::TypeFolder)
        itemFlags = itemFlags | Qt::ItemIsDropEnabled;

    return itemFlags;
}

QStringList BookmarksItemModel::mimeTypes() const
{
    QStringList types;
    types << "application/x-equalx-library-modeldata";

    return types;
}

QMimeData *BookmarksItemModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData();
    QByteArray encodedData;

    QDataStream stream(&encodedData, QIODevice::WriteOnly);

    foreach (const QModelIndex &index, indexes) {
        if (index.isValid()) {
            LibraryModelData itemData = qvariant_cast<LibraryModelData>(data(index));
            stream << itemData;
        }
    }

    mimeData->setData("application/x-equalx-library-modeldata", encodedData);
    return mimeData;
}

bool BookmarksItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    qDebug() << "dropMimeData() row="<<row<<" column="<<column;

    if (action == Qt::IgnoreAction)
        return true;

    if (!data->hasFormat("application/x-equalx-library-modeldata"))
        return false;

    if (column > 0)
        return false;

    int beginRow;

    BookmarkItem* parentItem = static_cast<BookmarkItem*>(parent.internalPointer());

    if(!parentItem){
        parentItem = mRootItem;
    }

    if (row != -1)
        beginRow = row;
    else if (parent.isValid())
        beginRow = parentItem->childCount();
    else
        beginRow = rowCount(QModelIndex());

    qDebug() << "dropMimeData() beginRow="<<beginRow;

    QByteArray encodedData = data->data("application/x-equalx-library-modeldata");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);

    LibraryRowsList newItems;

    while (!stream.atEnd()) {
        LibraryModelData rowData;
        stream >> rowData;
        newItems.append(rowData);
    }

    qDebug() << "dropMimeData() on "<< parentItem->data().name <<"  From:"<<beginRow<<" to:"<<newItems.count();

    int i = beginRow;
    foreach (const LibraryModelData& rowData, newItems) {
        qDebug() << "dropMimeData() => insertChild"<<rowData;
        mLibrary->moveData(rowData, parentItem->data().id);
        ++i;
    }

    return true;
}

QVariant BookmarksItemModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
        return QVariant::fromValue(mRootItem->data());

    return QVariant();
}


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

    BookmarkItem *parentItem;

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

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

BookmarkItem *BookmarksItemModel::invisibleRootItem() const
{
    if(mRootItem)
        return mRootItem;

    return 0;
}

BookmarkItem *BookmarksItemModel::itemFromIndex(const QModelIndex &index) const
{
    if(!index.isValid())
        return mRootItem;

    return static_cast<BookmarkItem*>(index.internalPointer());
}

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

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

    if(!parentItem)
        return QModelIndex();

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

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


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

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

    return parentItem->childCount();
}

int BookmarksItemModel::columnCount(const QModelIndex &/*parent*/) const
{
    return 1;
}

bool BookmarksItemModel::insertRow(int row, const QModelIndex &parent)
{
    return insertRows(row, 1, parent);
}

bool BookmarksItemModel::insertRows(int row, int count, const QModelIndex &parent)
{
    qDebug() << "insertRows() row=" << row<<" count="<<count;

    BookmarkItem* parentItem = static_cast<BookmarkItem*>(parent.internalPointer());

    if(!parent.isValid())
        parentItem = mRootItem;

    LibraryModelData rowData;
    rowData.id = -1;
    rowData.name=tr("Undefined Item");
    rowData.type=LibraryModelData::TypeUndefined;

    beginInsertRows(parentItem->index(),row, row+count);
    for(int i=row; i<row+count; i++){
        parentItem->insertChild(row, new BookmarkItem(rowData, parentItem));
    }
    endInsertRows();

    return true;
}

bool BookmarksItemModel::removeRow(int row, const QModelIndex &parent)
{
    return removeRows(row,1,parent);
}

bool BookmarksItemModel::removeRows(int row, int count, const QModelIndex &parent)
{
    qDebug() << "removeRows() row=" << row<<" count="<<count;

    QModelIndex parentIdx = parent;
    BookmarkItem *parentItem;
    if(!parent.isValid()){
        qDebug() << "removeRows() Invalid Parent Index!!!";
        parentIdx = QModelIndex();
        parentItem = mRootItem;
        //return false;
    }
    else{
        parentItem = static_cast<BookmarkItem*>(parentIdx.internalPointer());
    }

    qDebug() << "removeRows() row=" << row<<" count="<<count << " from " << parentItem->data().name;

    beginRemoveRows(parentIdx,row,row+count);
    for(int i=row; i<(row+count); i++){
        qDebug() << "removeRows() Remove Row:" << i;
        BookmarkItem *child = parentItem->child(i);
        if(!child){
            qDebug() << "removeRows() No child in "<< parentItem->data().name << " at row:" << i;
            continue;
        }

    }
    endRemoveRows();

    return true;
}

Qt::DropActions BookmarksItemModel::supportedDropActions() const
{
    return Qt::CopyAction | Qt::MoveAction;
}

Qt::DropActions BookmarksItemModel::supportedDragActions() const
{
    return Qt::CopyAction | Qt::MoveAction;
}

QVariant BookmarksItemModel::getCompleteData(const QModelIndex &index) const
{
    LibraryModelData row = qvariant_cast<LibraryModelData>(data(index));

    qDebug() << "getCompleteData for row " << row.id << " name:" << row.name;

    switch(row.type){
    case LibraryModelData::TypeFolder:{
        return QVariant::fromValue(mLibrary->getBookmarkFolder(row.id));
    }
    case LibraryModelData::TypeBookmark:
        return QVariant::fromValue(mLibrary->getBookmark(row.id));
        break;
    default:
        ;
    }

    return QVariant();

}

void BookmarksItemModel::onFolderChanged(const QString &dirPath)
{
    qDebug() << "BookmarksItemModel::onFolderChanged() Folder changed of dirPath="<<dirPath;

    QModelIndex folderIndex = findItem(dirPath);
    BookmarkItem *folderItem;

    if(!folderIndex.isValid()){
        folderItem = mRootItem;
    }
    else{
        folderItem = static_cast<BookmarkItem*>(folderIndex.internalPointer());
    }

    qDebug() << "BookmarksItemModel::onFolderChanged() Found Folder Changed "<<folderItem->data().name;

    beginRemoveRows(folderIndex,0, folderItem->rowCount());
    folderItem->clear();
    endRemoveRows();

}

BookmarkItem* __findItemWithId(BookmarkItem* item, int id)
{
    if(!item) return 0;

    if(item->data().id==id) return item;

    for(int i=0; i<item->rowCount(); ++i){
        BookmarkItem *childItem = item->child(i);
        if(__findItemWithId(childItem, id)!=0)
            return childItem;
    }

    return 0;
}


QModelIndex BookmarksItemModel::findItem(const QString &dirPath)
{
    qDebug() << "BookmarksItemModel::findItem() Searching for item at dirPath=["<<dirPath<<"]";

    QStringList sl = dirPath.split("/");

    qDebug() << "BookmarksItemModel::findItem() parents ="<<sl.size();

    if(sl.size()==1) return mRootItem->index();

    BookmarkItem* searchItem=mRootItem;
    for(int i=0; i<sl.size(); ++i){
        qDebug() << "searching item with id="<<sl.at(i).toInt();
        searchItem = __findItemWithId(searchItem, sl.at(i).toInt());
    }

    if(!searchItem || searchItem==mRootItem) return QModelIndex();

    return searchItem->index();
}
