/* ====================================================================
 * Copyright (c) 2009       Martin Hauner
 *                          http://subcommander.tigris.org
 *
 * Subcommander is licensed as described in the file doc/COPYING, which
 * you should have received as part of this distribution.
 * ====================================================================
 */

// sc
#include "config.h"
#include "ItemModel.h"
#include "ItemFolder.h"
#include "ItemFinder.h"
#include "BadItemException.h"
//#include "DragDropMimeTypes.h"
//#include "ColorId.h"
//#include "sublib/QSupportAlgorithms.h"
//#include "sublib/Utility.h"
//#include "svn/WcStatus.h"
//#include "svn/WcEntry.h"
#include "svn/Path.h"

// qt
#include <QtCore/QtAlgorithms>
#include <QtCore/QMimeData>
#include <QtCore/QList>
#include <QtGui/QPixmap>

namespace wcview {

ItemModel::ItemModel(
  const sc::String& root, const WcViewItemData* data )
  : _rootPath(root), _root(NULL), _data(data)
{
}
  
ItemModel::~ItemModel()
{
  //breaks test code using stack objects!
  //delete _root;
}

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

  if (isInvisibleRoot (parent))
    return createIndex (row,column,_root);

  Item* parentItem = getItem (parent);
  Item* childItem = parentItem->getChild (row);
  return createIndex (row,column,childItem);
}

QModelIndex ItemModel::parent (const QModelIndex& index) const
{
  if (!hasParent (index))
    return QModelIndex ();

  Item* item = getItem (index);
  Item* parentItem = item->getParent ();
  Index parentRow = parentItem->getParentIndex ();
  return createIndex (parentRow, 0, parentItem);
}

bool ItemModel::hasParent (const QModelIndex& idx) const
{
  if (!idx.isValid ())
    return false;

  return getItem (idx)->getParent () != NULL;
}

int ItemModel::columnCount (const QModelIndex& parent) const
{
  return _data->columns();
}

int ItemModel::rowCount (const QModelIndex& parent) const
{
  if( !_root )
    return 0;

  if( !parent.isValid() )
    return 1;

  if( parent.column() != 0 )
    return 0;

  Item* item = getItem (parent);
  return item->getChildCount ();
}

QVariant ItemModel::
headerData( int section, Qt::Orientation orientation, int role ) const
{
  return _data->header(section);
#if 0
  if( orientation == Qt::Horizontal && role == Qt::DisplayRole )
    return _data->header(section);
  
  else if( role == Qt::TextAlignmentRole )
    return (uint)_data->alignment(section);

  return QVariant();
#endif
}

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

  const WcViewItem* item = _m->getItem(index);

  if( role == Qt::DisplayRole || role == Qt::EditRole )
  {
    if( item->isDir() && _data->deep(index.column()) )
    {
      WcViewTreeItemFolder* folder = _m->getItemFolder(item->path());

      if( folder && folder->deepStatus() )
        return "*";
      else
        return "";
    }
    return _data->data(index.column(),item);
  }
  else if( role == Qt::BackgroundRole )
    return _data->color(index.column(),item);

  else if( role == Qt::TextAlignmentRole )
    return (uint)_data->alignment(index.column());

  else if( role == WcViewItemRole )
    return QVariant::fromValue(item);

  else if( role == DeepRole )
  {
    if( ! item->isDir() )
      return QVariant(false);
    
    WcViewTreeItemFolder* folder = _m->getItemFolder(item->path());
    if(folder)
      return QVariant(folder->deepStatus());
    else
      return QVariant(false);
  }
  else if( role == NameRole )
    return QVariant::fromValue(item->path());

  else if( role == DragRole )
    return _data->data(0,item);

  else if( role == DirRole )
    return QVariant::fromValue(item->isDir());

  else if( role == ChangedRole )
    return QVariant::fromValue(item->isChanged());

  else if( role == TextStatusRole )
    return QVariant::fromValue(item->getTextStatus());

  else if( role == IgnoredRole )
    return QVariant(item->isIgnored());

  else if( role == SwitchedRole )
    return QVariant(item->isSwitched());

  else if( role == OutOfDateRole )
    return QVariant(item->isOutOfDate());

  else if( role == SortRole )
    return _data->dataSort(0,item);

  return QVariant();

#if 0
  else if( role == Qt::DecorationRole )
    return item->standardDecoration();

  else if( role == ExpandedDecorationRole )
    return item->expandedDecoration();

  else if( role == Qt::ToolTipRole )
    return item->tooltip();

  else if( role == OnItemRole )
    return item->tooltip();

  else if( role == CurrentWcRole )
    return item->bookmark()->isCurrent();
#endif
#endif
}

Qt::ItemFlags ItemModel::flags( const QModelIndex &index ) const
{
  return Qt::NoItemFlags;
#if 0
  Qt::ItemFlags flags;

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

  flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;

  const WcViewItem* item = _m->getItem(index);

  if( index.column() == 0 /*WcStatusDisplayData::ColName*/ )
  {
    flags |= Qt::ItemIsEditable;
  }

  if( item->isDir() )
  {
    flags |= Qt::ItemIsDropEnabled;
  }

  return flags;
#endif
}

void ItemModel::insert( const WcViewItemPtr& item )
{
  if( !_root )
  {
    if( item->path() != _rootPath )
      throw BadItemException (item->path ());

    beginInsertRows( QModelIndex(), 0, 0 );
    _root = new ItemFolder( item, NULL );
    endInsertRows();
  }
  else
  {
    sc::String parentPath = svn::Path::getDirName (item->path());

    Item* parent;
    if (_root->getName() == parentPath)
      parent = _root;
    else
    {
      try {
        parent = ItemFinder (_root).find (parentPath);
      } catch (const BadNameException& e) {
        throw BadItemException (e.getBadName ());
      }
    }

    Index parentRow = parent->getParentIndex ();
    QModelIndex index = createIndex (parentRow, 0, parent);

    int newRow = parent->getChildCount ();

    beginInsertRows (index, newRow, newRow);
    parent->insertChild (new ItemFolder (item,parent));
    endInsertRows ();
  }
}

void ItemModel::remove( const WcViewItemPtr& item )
{
}

void ItemModel::update( const WcViewItemPtr& item )
{
}

bool ItemModel::isInvisibleRoot (const QModelIndex& index) const
{
  return !index.isValid ();
}

Item* ItemModel::getItem (const QModelIndex& index) const
{
  assert(index.isValid());
  Item* item = static_cast<Item*>(index.internalPointer());
  assert(item);
  return item;
}




#if 0
QModelIndex WcViewTreeItemModel::index( const sc::String& path ) const
{
  if( path == _m->invisible()->path() )
    return QModelIndex();

  WcViewTreeItemFolder* parentFolder = _m->getParentFolder(path);
  if(!parentFolder)
  {
    assert(parentFolder);
    return QModelIndex();
  }

  int row = parentFolder->row(path);
  if( row == -1 )
    return QModelIndex();

  WcViewItemPtr child = parentFolder->child(row);

  return createIndex( row, 0, child.get() );
}
#endif

#if 0
void WcViewTreeItemModel::updateDeepStatus( const sc::String& path )
{
  WcViewTreeItemFolder* pathFolder = _m->getItemFolder(path);
  if(!pathFolder)
    return;

  bool deepStatus = false;
  for( int i = 0; i < pathFolder->childCount(); i++ )
  {
    WcViewItemPtr item = pathFolder->child(i);
    
    deepStatus |= item->isChanged();

    if( deepStatus )
        break;

    if( item->isDir() )
    {
      WcViewTreeItemFolder* me = _m->getItemFolder(item->path());
      if(!me)
        continue;

      deepStatus |= me->deepStatus();
    }
  }

  // no change...?
  if( pathFolder->deepStatus() == deepStatus )
    return;
  
  pathFolder->setDeepStatus(deepStatus);

  // notify deep status change
  QModelIndex idx = index(path);
  dataChanged(idx,idx);

  // recurse up...
  WcViewTreeItemFolder* parent = _m->getParentFolder(path);
  if( !parent )
    return;

  updateDeepStatus(parent->path());

  emit deepStatusChanged(path);
}
#endif

#if 0
void WcViewTreeItemModel::insert( const sc::String& path, const WcViewItems& items )
{
  if( items.size() < 1 )
    return;

  WcViewTreeItemFolder* parent = _m->getParentFolder(path);
  if( !parent )
  {
    assert(parent);
    return;
  }

//  emit layoutAboutToBeChanged();

  QModelIndex pidx = index(parent->path());
  int row = parent->insertRow(path);

  beginInsertRows( pidx, row, row );

  parent->insert( row, *(items.begin()) );

  WcViewTreeItemFolder* item = new WcViewTreeItemFolder(path,items,false);
  _m->index().insert( path, item );

  endInsertRows();

  updateDeepStatus(parent->path());

  // children?
  if( items.size() >= 2 )
  {
    // 0 based and skip first (ie. the "." folder)
    beginInsertRows( index(path), 0, items.size()-2 );
    endInsertRows();
    
    if( item->deepStatus() )
      emit deepStatusChanged(path);
  }

  QModelIndex idx = index(path);
  dataChanged(idx,idx);
  //emit layoutChanged();
}

void WcViewTreeItemModel::remove( const sc::String& path )
{
  WcViewTreeItemFolder* parent = _m->getParentFolder(path);
  if(!parent)
    return;

  // it is ok if we don't find a row (initial load)
  int row = parent->row(path);
  if( row == -1 )
    return;

  beginRemoveRows( index(path).parent(), row, row );

  // it is ok if we don't find a folder (intial load)
  WcViewTreeItemFolder* folder = _m->getItemFolder(path);
  if(folder)       
    remove(folder);

  parent->remove(row);

  endRemoveRows();
}

void WcViewTreeItemModel::remove( WcViewTreeItemFolder* folder )
{
  if(!folder)
    return;

  for( int i = 0; i < folder->childCount(); i++ )
  {
    WcViewItemPtr item = folder->child(i);
    
    if( item->isDir() )
    {
      WcViewTreeItemFolder* me = _m->getItemFolder(item->path());
      remove(me);
    }
  }
  delete _m->index().take(folder->path());
}
#endif

} // namespace
