/* ====================================================================
 * Copyright (c) 2003-2006, 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 "Project.h"
#include "WcModel.h"
#include "ProjectLvi.h"
#include "Repository.h"
#include "RepositoryLvi.h"
#include "RepositoryTagsLvi.h"
#include "RepositoryBranchesLvi.h"
#include "WorkingCopyLvi.h"
#include "DragDropMimeTypes.h"
#include "ActionStorage.h"
#include "ProjectFoldersWidget.h"
#include "events/EventSupport.h"
#include "events/LviOnItemEvent.h"
#include "events/ReOrderProjectsEvent.h"
#include "sublib/DragDrop.h"
#include "sublib/PathOps.h"
#include "sublib/Utility.h"
#include "util/Compare.h"

// qt
#include <qpopupmenu.h>
#include <QtGui/QAction>
#include <qdragobject.h>
#include <QtGui/QApplication>
#include <QtGui/QCursor>


enum Actions
{
  ActionRemove,
  ActionNew,
  ActionRename,
  ActionSettings,
  ActionAddRepository,
  ActionAddWorkingCopy,
  ActionEditTrunk,
  ActionEditBranches,
  ActionEditTags
};

enum DropType
{
  DropTrunk,
  DropBranches,
  DropTags,
  DropWorkingCopy,
  DropRepository
};

static ActionStorage _actions;
static QPopupMenu*   _menu = NULL;
static QPopupMenu*   _drop = NULL;


ProjectLvi::ProjectLvi( QListView* parent, RepositoryModel* rmodel,
  WcModel* wmodel, Project* project )
: super(parent,QString::fromUtf8(project->getName().getStr())),
  _project(project), _rpModel(rmodel), _wcModel(wmodel)
{
  setPixmap( 0, QPixmap(getIconDir() + "BookmarkProjectClosed.png") );
  init();
}

void ProjectLvi::init()
{
  Project::Items items;
  _project->getItems(items);

  for( Project::Items::const_iterator it = items.begin(); it != items.end(); it++ )
  {
    const Project::Item& item = *it;
    addBookmark(item);
  }
}

void ProjectLvi::addBookmark( const Project::Item& item )
{
  if( item.isTrunk() && ! item.getSource().isEmpty() )
  {
    /*ScLvi* lvi =*/ new RepositoryLvi( this, _rpModel, _project, item );
  }
  if( item.isBranches() && ! item.getSource().isEmpty() )
  {
    /*ScLvi* lvi =*/ new RepositoryBranchesLvi( this, _rpModel, _project, item );
  }
  if( item.isTags() && ! item.getSource().isEmpty() )
  {
    /*ScLvi* lvi =*/ new RepositoryTagsLvi( this, _rpModel, _project, item );
  }
  else if( item.isWorkingCopy() && item.getName().getCharCnt() != 0 )
  {
    /*ScLvi* lvi =*/ new WorkingCopyLvi( this, _wcModel, _project, item );
  }
  else if( item.isRepository() && item.getName().getCharCnt() != 0 )
  {
    /*ScLvi* lvi =*/ new RepositoryLvi( this, _rpModel, _project, item );
  }
}

QListViewItem* ProjectLvi::findBookmark( const Project::Item& item )
{
  QListViewItem* lvi = firstChild();
  while(lvi)
  {
    if( item.isWorkingCopy() && WcLvi_cast(lvi) )
    {
      WorkingCopyLvi* wc = WcLvi_cast(lvi);
      const Project::Item& wcItem = wc->getProjectItem();

      if( item.getId() == wcItem.getId() )
      {
        return lvi;
      }
    }
    else if( ! item.isWorkingCopy() && RpLvi_cast(lvi) )
    {
      RepositoryLvi* rp = RpLvi_cast(lvi);
      const Project::Item& rpItem = rp->getProjectItem();

      if( item.getId() == rpItem.getId() )
      {
        return lvi;
      }
    }

    lvi = lvi->nextSibling();
  }

  return NULL;
}


void ProjectLvi::setupActions( ProjectFoldersWidget* parent )
{
  QAction* action;

  action = new QAction( _q("rename project"), QString(""), parent );
  action->setStatusTip( _q("rename the selected project") );
  action->setEnabled(false);
  parent->connect( action, SIGNAL(activated()), SLOT(renamePrj()) );
  _actions.addAction( ActionRename, action );

  action = new QAction( _q("&remove project"), _q("Ctrl+R"), parent );
  action->setStatusTip( _q("remove the selected project") );
  action->setEnabled(false);
  parent->connect( action, SIGNAL(activated()), SLOT(removePrj()) );
  _actions.addAction( ActionRemove, action );

  action = new QAction( _q("&new project.."), _q("Ctrl+N"), parent );
  action->setStatusTip( _q("add a new project") );
  action->setEnabled(true);
  parent->connect( action, SIGNAL(activated()), SLOT(addPrj()) );
  _actions.addAction( ActionNew, action );

  action = new QAction( _q("project settings.."), QString(""), parent );
  action->setStatusTip( _q("open project settings") );
  action->setEnabled(true);
  parent->connect( action, SIGNAL(activated()), SLOT(prjSettings()) );
  _actions.addAction( ActionSettings, action );

  action = new QAction( _q("new repository.."), QString(""), parent );
  action->setStatusTip( _q("add a new repository bookmark") );
  action->setEnabled(true);
  parent->connect( action, SIGNAL(activated()), SLOT(addPrjRepository()) );
  _actions.addAction( ActionAddRepository, action );

  action = new QAction( _q("new working copy.."), QString(""), parent );
  action->setStatusTip( _q("add a new working copy bookmark") );
  action->setEnabled(true);
  parent->connect( action, SIGNAL(activated()), SLOT(addPrjWorkingCopy()) );
  _actions.addAction( ActionAddWorkingCopy, action );

  action = new QAction( _q("edit trunk.."), QString(""), parent );
  action->setStatusTip( _q("edit the trunk bookmark") );
  action->setEnabled(true);
  parent->connect( action, SIGNAL(activated()), SLOT(editTrunkBookmark()) );
  _actions.addAction( ActionEditTrunk, action );

  action = new QAction( _q("edit branches.."), QString(""), parent );
  action->setStatusTip( _q("edit the branches bookmark") );
  action->setEnabled(true);
  parent->connect( action, SIGNAL(activated()), SLOT(editBranchesBookmark()) );
  _actions.addAction( ActionEditBranches, action );

  action = new QAction( _q("edit tags.."), QString(""), parent );
  action->setStatusTip( _q("edit the tags bookmark") );
  action->setEnabled(true);
  parent->connect( action, SIGNAL(activated()), SLOT(editTagsBookmark()) );
  _actions.addAction( ActionEditTags, action );

  {
    _menu = new QPopupMenu(parent);
    QAction* action;

    action = _actions.getAction( ActionNew );
    action->addTo(_menu);
    action = _actions.getAction( ActionRemove );
    action->addTo(_menu);
    action = _actions.getAction( ActionRename );
    action->addTo(_menu);
    action = _actions.getAction( ActionSettings );
    action->addTo(_menu);

    _menu->insertSeparator();

    action = _actions.getAction( ActionAddRepository );
    action->addTo(_menu);
    action = _actions.getAction( ActionAddWorkingCopy );
    action->addTo(_menu);
    action = _actions.getAction( ActionEditTrunk );
    action->addTo(_menu);
    action = _actions.getAction( ActionEditBranches );
    action->addTo(_menu);
    action = _actions.getAction( ActionEditTags );
    action->addTo(_menu);
  }
  {
    _drop = new QPopupMenu(parent);
    _drop->insertItem( _q("insert as url: trunk"), DropTrunk );
    _drop->insertItem( _q("insert as url: branches"), DropBranches );
    _drop->insertItem( _q("insert as url: tags"), DropTags );
    _drop->insertItem( _q("insert as url: repository"), DropRepository );
    _drop->insertItem( _q("insert as working copy"), DropWorkingCopy );
  }
}

QPopupMenu* ProjectLvi::getMenu()
{
  return _menu;
}

QString ProjectLvi::text( int column ) const
{
  if( column != 0 )
  {
    return "";
  }

  if( isRename() )
  {
    return super::text(column);
  }

  return QString::fromUtf8( _project->getName().getStr() );
}

int ProjectLvi::compare( QListViewItem* i, int col, bool ascending ) const
{
  ProjectLvi* lvi = dynamic_cast<ProjectLvi*>(i);

  return compare3( getSortPos(), lvi->getSortPos() );
}

bool ProjectLvi::acceptDrop( const QMimeSource* mime ) const
{
  return mime->provides( ScMimeTypeProject ) 
    ||   mime->provides( "text/plain" )
    ||   mime->provides( "text/uri-list" );
}

void ProjectLvi::dropped( QDropEvent* e )
{
  QString text;
  if( QTextDrag::decode(e,text) )
  {
    QStringList list = decodeDropText( text, e->format() );

    if( list.size() == 0 )
    {
      // show error?
      return;
    }

    int item = _drop->exec( QCursor::pos() );

    switch( item )
    {
    case DropTrunk:
      {
        _project->setRepositoryUrl( sc::String(((*list.begin()).utf8())) );
        break;
      }
    case DropBranches:
      {
        _project->setBranchesUrl( sc::String(((*list.begin()).utf8())) );
        break;
      }
    case DropTags:
      {
        _project->setTagsUrl( sc::String(((*list.begin()).utf8())) );
        break;
      }
    case DropRepository:
      {
        for( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
        {
          QString url = *it;

          Project::Item item = _project->createRepositoryItem();
          QString name = url.section( "/", -1 );
          if( name.isEmpty() )
          {
            name = _q("new repository");
          }
          item.setName  ( sc::String(name.utf8()) );
          item.setSource( sc::String(url.utf8()) );
          _project->setItem(item);
        }
        break;
      }
    case DropWorkingCopy:
      {
        for( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
        {
          QString path = *it;
          path.remove("file://");

          Project::Item item = _project->createWorkingCopyItem();
          QString name = path.section( "/", -1 );
          if( name.isEmpty() )
          {
            name = _q("new wc");
          }
          item.setName  ( sc::String(name.utf8()) );
          item.setSource( sc::String(path.utf8()) );
          _project->setItem(item);
        }
        break;
      }
    default:
      {
        return;
      }
    }

    _wcModel->saveProject(_project);
    refresh();
  }
  else // project
  {
    ProjectLvi* lvi = dynamic_cast<ProjectLvi*>(listView()->selectedItem());

    // the project may not be the selected item if one holds the drag a while
    // on a non project item and then drops it on a project item before it gets
    // selected. Ignore the drop then. Also ignore the drop if we drop the item
    // on itself.
    if( ! lvi || lvi == this )
    {
      return;
    }

    postEvent( getTidObj(), new ReOrderProjectsEvent( lvi->getProject(), getProject() ) );
    e->accept();
  }
}

void ProjectLvi::setOpen(bool open)
{
  super::setOpen(open);

  if(open)
  {
    setPixmap( 0, QPixmap(getIconDir() + "BookmarkProjectOpened.png") );
  }
  else
  {
    setPixmap( 0, QPixmap(getIconDir() + "BookmarkProjectClosed.png") );
  }
}

QDragObject* ProjectLvi::dragObject()
{
  return new QStoredDrag( ScMimeTypeProject, listView() );
}

void ProjectLvi::onItem()
{
  postEvent( getTidObj(), new LviOnItemEvent(text(0)) );
}

void ProjectLvi::contextMenuRequest( const QPoint& pos, int col )
{
  _menu->exec(pos);
}

long ProjectLvi::getSortPos() const
{
  return _project->getSortPos();
}

void ProjectLvi::renamed( const QString& text )
{
  _project->setName( sc::String(text.utf8()) );
  _wcModel->saveProject(_project);
}

void ProjectLvi::enableActions()
{
  _actions.enableActions();
}

void ProjectLvi::disableActions()
{
  _actions.disableActions();
  _actions.enableAction( ActionNew, true );
}

Project* ProjectLvi::getProject()
{
  return _project;
}

void ProjectLvi::refresh( Project* prj )
{
  if( prj && !(prj->getId() == _project->getId()) )
  {
    return;
  }

  QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );

  removeBookmarks();
  updateBookmarks();
  insertBookmarks();

  QApplication::restoreOverrideCursor();
}

void ProjectLvi::removeBookmarks()
{
  QListViewItem* lvi = firstChild();
  while( lvi )
  {
    QListViewItem* next = lvi->nextSibling();

    if( WcLvi_cast(lvi) )
    {
      WorkingCopyLvi* wcLvi = WcLvi_cast(lvi);
      if( _project->getItem(wcLvi->getProjectItem().getId()).isNull() )
      {
        takeItem(lvi);
        delete lvi;
      }
    }
    else if( RpLvi_cast(lvi) )
    {
      RepositoryLvi* rpLvi = RpLvi_cast(lvi);
      long id = rpLvi->getProjectItem().getId();
      if(  _project->getItem(id).isNull()
        // don't show trunk/branches/tags without source
        || _project->getItem(id).getSource().getCharCnt() == 0
      )
      {
        takeItem(lvi);
        delete lvi;
      }
    }

    lvi = next;
  }

  sort();
}

void ProjectLvi::updateBookmarks()
{
  QListViewItem* lvi = firstChild();
  while( lvi )
  {
    if( WcLvi_cast(lvi) )
    {
      WorkingCopyLvi* wcLvi = WcLvi_cast(lvi);
      wcLvi->setProjectItem( _project->getItem(wcLvi->getProjectItem().getId()) );
    }
    else if( RpLvi_cast(lvi) )
    {
      RepositoryLvi* rpLvi = RpLvi_cast(lvi);
      rpLvi->setProjectItem( _project->getItem(rpLvi->getProjectItem().getId()) );
    }

    lvi = lvi->nextSibling();
  }

  sort();
}

void ProjectLvi::insertBookmarks()
{
  Project::Items items;
  _project->getItems(items);

  for( Project::Items::const_iterator it = items.begin(); it != items.end(); it++ )
  {
    const Project::Item& item = *it;

    QListViewItem* lvi = findBookmark(item);
    if( ! lvi )
    {
      addBookmark(item);
    }
  }

  sort();
}

bool ProjectLvi::isSelectable( QListViewItem* prev ) const
{
  return true;
}
