/****************************************************************************
**
** Copyright (C) 2012 Róbert Márki
**
** This file is part of Web Feeds.
**
** Web Feeds 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.
**
** Web Feeds 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 Web Feeds.  If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/

#include "pageitem.h"
#include <QDebug>

/*!
  \class PageItem
  \brief The PageItem class provides an item type for building a tree sturcture,
  of hierarchically organized items.

  The tree stucture of these items can be easily exposed to any kind of UI through
  an item model. AbstractFeedAggregatorService implementation should implement their
  own set of custom item types based on PageItem. Members variables exposed as
  properties will be accessible from the item model.
*/
/*!
  \property PageItem::active
  True if the item is on the active branch of the object tree, otherwise false.
*/
/*!
  \property PageItem::parent
  The item's parent item.
*/
/*!
  \property PageItem::title
  The items display name.
*/
/*!
  \property PageItem::iconSource
  The url of the icon associated with this item.
*/
/*!
  \property PageItem::itemId
  The identifier of the item. Item id properties are not necessarily unique,
  not even within their own parent.
*/
/*!
  \property PageItem::busy
  Genaral property to indicate that some operation is in progress related
  to this item.
*/
/*!
  \property PageItem::childrenBusy
  General property to indicate that some operation is in progress related
  to this item's child items.
*/
/*!
  \property PageItem::service
  The AbstractFeedAggreagatorService implementation instance associated
  with this item.
*/
/*!
  \property PageItem::enabled
  Indicates whether interaction should be enabled with the given item.
*/
/*!
  \property PageItem::childCount
  The number of child items.
*/
/*!
  \fn PageItem::activityStatusChanged
  This signal is emitted when the \a active property of this \a item
  changes.
*/
/*!
  \fn PageItem::aboutToInsertChildren
  This signal is emitted when child items are going to be inserted to this item
  from the \a start to the \a end row. \a parent ponints to this item.
*/
/*!
  \fn PageItem::aboutToRemoveChildren
  This signal is emitted when child items are going to be removed from this item
  from the \a start to the \a end row. \a parent ponints to this item.
*/
/*!
  \fn PageItem::childrenInserted
  This signal is emitted when child items were inserted to this item.
  \a parent ponints to this item.
*/
/*!
  \fn PageItem::childrenRemoved
  This signal is emitted when child items were removed from this item.
  \a parent ponints to this item.
*/
/*!
  \fn PageItem::dataChanged
  This signal is emitted when some of the properties of this item changes.
*/
/*!
  \fn PageItem::serviceMessage
  This signal is emitted when a \a message should be displayed in the UI
  related to this item.
*/
/*!
  \fn PageItem::aboutToBeRemovedFromParent
  This signal is emitted before the item is removed from it's
  current parent item.
*/

/*!
  Constructs a PageItem object with the given \a parent item and associated with
  the given \a service implementation.
*/
PageItem::PageItem(PageItemPtr parent, AbstractFeedAggreagatorService* service) :
    QObject(0),
    m_service(service),
    m_parentItem(parent),
    m_active(false),
    m_canFetchMore(false),
    m_enabled(false),
    m_busy(false),
    m_childrenBusy(false)
{
}

/*!
  Destroys the PageItem object.
*/
PageItem::~PageItem()
{
    if(isActive())
        setActive(false);
    m_childItems.clear();
    m_childItemIdHash.clear();
}

/*!
  Inserts the list of items in \a children as child items of this item, starting
  from the given \a row.
*/
void PageItem::insertChildren(int row, const QList<PageItemPtr> &children)
{
    if(!children.isEmpty())
    {
        emit aboutToInsertChildren(PageItemWeakRef(this), row, (row + children.count() - 1));
        int currentRow = row;
        for(int i = 0; i < children.count(); ++i, ++currentRow)
        {
            m_childItems.insert(currentRow, children.at(i));
            m_childItemIdHash.insert(children.at(i)->itemId(), children.at(i));
            children.at(i)->setParent(PageItemWeakRef(this));
        }
        emit childrenInserted(PageItemWeakRef(this));
        emit childCountChanged();
    }
}

/*!
  Appends the list of itmes in \a children to this item's children.
*/
void PageItem::appendChildren(const QList<PageItemPtr> &children)
{
    insertChildren(childCount(), children);
}

/*!
  Prepends the list of itmes in \a children to this item's children.
*/
void PageItem::prependChildren(const QList<PageItemPtr> &children)
{
    insertChildren(0, children);
}

/*!
  Inserts the \a child item into this item at the given \a row.
*/
void PageItem::insertChild(int row, PageItemPtr child)
{
    QList<PageItemPtr> children;
    children << child;
    insertChildren(row, children);
}

/*!
  Appends the given \a item to this item's children.
*/
void PageItem::appendChild(PageItemPtr item)
{
    insertChild(childCount(), item);
}

/*!
  Prepends the given \a item to this item's children.
*/
void PageItem::prependChild(PageItemPtr child)
{
    insertChild(0, child);
}

/*!
  Removes the given \a child item from this items children.
*/
void PageItem::removeChild(PageItemPtr child)
{
    if(m_childItems.contains(child))
    {
        child->onAboutToBeRemovedFromParent();
        int index = m_childItems.indexOf(child);
        emit aboutToRemoveChildren(PageItemWeakRef(this), index, index);
        m_childItems.removeAll(child);
        m_childItemIdHash.remove(child->itemId());
        emit childrenRemoved(PageItemWeakRef(this));
        emit childCountChanged();
    }
}

/*!
  Returns the list of item ids of this item's children.
*/
QStringList PageItem::childItemIds() const
{
    return m_childItemIdHash.keys();
}

/*!
  Returns the pointer of the child item at the given \a row.
*/
PageItem *PageItem::child(int row)
{
    return childItem(row).data();
}

/*!
  Returns the child item at the given \a row.
*/
PageItemPtr PageItem::childItem(int row) const
{
    if(row < m_childItems.count())
        return m_childItems.value(row);
    else
        return PageItemPtr();
}

/*!
  Returns the child item with given \a itemId.
*/
PageItemPtr PageItem::childItem(const QString& itemId) const
{
    if(contains(itemId))
        return children(itemId).first();
    else
        return PageItemPtr();
}

/*!
  Returns the list of child items which have the givenn \a itemId.
*/
QList<PageItemPtr> PageItem::children(const QString& itemId) const
{
    return m_childItemIdHash.values(itemId);
}

/*!
  Returns the list of child items.
*/
QList<PageItemPtr> PageItem::childItems() const
{
    return m_childItems;
}

/*!
  Replaces this item's current child items with the items in
  \a children.
*/
void PageItem::setChildItems(const QList<PageItemPtr> &children)
{
    clearChildItems();
    foreach(PageItemPtr childItem, children)
        appendChild(childItem);
}

/*!
  Removes the item's current child items.
*/
void PageItem::clearChildItems()
{
    if(m_childItems.count())
    {
        emit aboutToRemoveChildren(PageItemWeakRef(this), 0, m_childItems.count() - 1);
        foreach(PageItemPtr childItem, m_childItems)
            childItem->onAboutToBeRemovedFromParent();
        m_childItems.clear();
        m_childItemIdHash.clear();
        emit childrenRemoved(PageItemWeakRef(this));
        emit childCountChanged();
    }
}

/*!
  Returns true if this item has a child item with the given
  \a itemId, otherwise returns false.
*/
bool PageItem::contains(const QString &itemId) const
{
    return m_childItemIdHash.contains(itemId);
}

int PageItem::childCount() const
{
    return m_childItems.count();
}

/*!
  Returns this item's index in it's parent item.
*/
int PageItem::row() const
{
    PageItemPtr parentItem = m_parentItem.toStrongRef();
    PageItemWeakRef thisWeakPtr = PageItemWeakRef(const_cast<PageItem*>(this));
    PageItemPtr thisPtr = thisWeakPtr.toStrongRef();
    if(parentItem)
        return parentItem->m_childItems.indexOf(thisPtr);
    else
        return -1;
}

bool PageItem::isActive() const
{
    return m_active;
}

void PageItem::setActive(bool active)
{
    bool valueChanges = (active != m_active);
    if(valueChanges)
    {
        m_active = active;
        emit activityStatusChanged(PageItemWeakRef(this), m_active);
        emit activityChanged();
    }
}

void PageItem::setParent(PageItemPtr parent)
{
    m_parentItem = parent;
    emit parentChanged();
}

PageItem *PageItem::parent() const
{
    if(m_parentItem)
        return m_parentItem.data();
    else
        return 0;
}

PageItemWeakRef PageItem::parentItem() const
{
    return m_parentItem;
}

/*!
  Returns true if additional child items can
  be requested for this item, otherwise returns false.
*/
bool PageItem::canFetchMore() const
{
    return m_canFetchMore;
}

/*!
  \sa PageItem::canFetchMore
*/
void PageItem::setCanFetchMore(bool canFetchMore)
{
    m_canFetchMore = canFetchMore;
}

/*!
  Request to load more children for this item.
*/
void PageItem::loadMoreChildren()
{
    emit moreChildrenRequested(PageItemWeakRef(this));
}

QString PageItem::title() const
{
    return m_title;
}

void PageItem::setTitle(const QString &title)
{
    m_title = title;
    emit titleChanged();
    emit dataChanged(PageItemWeakRef(this));
}

QString PageItem::itemId() const
{
    return m_itemId;
}

void PageItem::setItemId(const QString &itemId)
{
    m_itemId = itemId;
}

bool PageItem::isEnabled() const
{
    return m_enabled;
}

void PageItem::setEnabled(bool enabled)
{
    m_enabled = enabled;
}

bool PageItem::isBusy() const
{
    return m_busy;
}

void PageItem::setBusy(bool busy)
{
    m_busy = busy;
    emit busyChanged();
}

QString PageItem::iconSource() const
{
    return m_iconSource;
}

void PageItem::setIconSource(const QString &source)
{
    m_iconSource = source;
    emit iconSourceChanged();
    emit dataChanged(PageItemWeakRef(this));
}

AbstractFeedAggreagatorService * PageItem::service() const
{
    return m_service;
}

void PageItem::setService(AbstractFeedAggreagatorService *service)
{
    m_service = service;
    emit serviceChanged();
}

bool PageItem::childrenBusy() const
{
    return m_childrenBusy;
}

void PageItem::setChildrenBusy(bool busy)
{
    m_childrenBusy = busy;
    emit childrenBusyChanged();
}

/*!
  Notifies the view that the given \a message should be displayed
  related to this item.
*/
void PageItem::showMessage(const QString &message)
{
    emit serviceMessage(message);
}

/*!
  Reimplemented from QObject::setParent.
*/
void PageItem::setParent(QObject *parent)
{    
    Q_UNUSED(parent)
}

/*!
  Notifies the item that it's going to be removed from it's parent item.
*/
void PageItem::onAboutToBeRemovedFromParent()
{
    emit aboutToBeRemovedFromParent();
}
