/* ====================================================================
 * 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 "WorkingCopyLvi.h"
#include "ListWidget.h"
#include "GuiLock.h"
#include "ScModel.h"
#include "WcModel.h"
#include "Project.h"
#include "UpdateDialog.h"
#include "CursorSupport.h"
#include "ActionStorage.h"
#include "ErrorSupport.h"
#include "PostCmdResult.h"
#include "DragDropMimeTypes.h"
#include "ExternProviderImpl.h"
#include "ColorId.h"
#include "ProjectLvi.h"
#include "ProjectFoldersWidget.h"
#include "ProjectItemDragObject.h"
#include "dialogs/CommitDialogCmd.h"
#include "events/LviOnItemEvent.h"
#include "events/MoveWcItemEvent.h"
#include "events/CopyWcItemEvent.h"
#include "events/EventSupport.h"
#include "commands/StatusParam.h"
#include "commands/UpdateParam.h"
#include "commands/CommitParam.h"
#include "commands/CheckoutParam.h"
#include "sublib/ColorStorage.h"
#include "sublib/PathOps.h"
#include "sublib/StringUtil.h"
#include "sublib/Utility.h"
#include "svn/Revision.h"
#include "svn/WcEntry.h"
#include "svn/Error.h"
#include "util/String.h"
#include "util/Guard.h"
#include "util/Compare.h"
#include "util/Id.h"

// qt
#include <QtGui/QPainter>
#include <QtGui/QCursor>
#include <QtCore/QEvent>
#include <qpopupmenu.h>
#include <QtGui/QAction>
#include <qdragobject.h>


enum Actions
{
  ActionUpdate,
  ActionUpdateRevision,
  ActionCommit,
  ActionLog,
  ItemActionCurrent,
  ItemActionEdit,
  ItemActionRemove
};

static ActionStorage _actions;
static QPopupMenu*   _menu = NULL;


WorkingCopyLvi::WorkingCopyLvi( QListViewItem* parent, WcModel* model, Project* prj,
  const Project::Item& wcItem )
  : super(parent,""), _wcModel(model), _prj(prj), _wcItem(wcItem)
{
  setText( 0, QString::fromUtf8(wcItem.getName()) );
  setPixmap( 0, QPixmap(getIconDir() + "BookmarkWorkingCopy.png") );
}

WorkingCopyLvi::~WorkingCopyLvi()
{
}

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

  action = new QAction( _q("&update"), _q("Ctrl+Shift+U"), parent );
  action->setStatusTip( _q("update the working copy path or file to head (R)") );
  action->setEnabled(false);
  parent->connect( action, SIGNAL(activated()), SLOT(update()) );
  _actions.addAction( ActionUpdate, action );

  action = new QAction( _q("update.."), _q("Ctrl+Shift+J"), parent );
  action->setStatusTip( _q("update the working copy path or file to a revision (R)") );
  action->setEnabled(false);
  parent->connect( action, SIGNAL(activated()), SLOT(updateRev()) );
  _actions.addAction( ActionUpdateRevision, action );
  
  action = new QAction( _q("c&ommit.."), _q("Ctrl+Shift+O"), parent );
  action->setStatusTip( _q("commit your changes in the working copy to the repository (R)") );
  action->setEnabled(false);
  parent->connect( action, SIGNAL(activated()), SLOT(commit()) );
  _actions.addAction( ActionCommit, action );

  action = new QAction( _q("&log.."), _q("Ctrl+Shift+L"), parent );
  action->setStatusTip( _q("view the log history of the selected path") );
  action->setEnabled(false);
  parent->connect( action, SIGNAL(activated()), SLOT(logWc()) );
  _actions.addAction( ActionLog, action );

  action = new QAction( _q("se&t current"), _q("Ctrl+T"), parent );
  action->setStatusTip( _q("set the current target working copy path") );
  action->setEnabled(false);
  parent->connect( action, SIGNAL(activated()), SLOT(setCurrentWc()) );
  _actions.addAction( ItemActionCurrent, action );

  action = new QAction( _q("&edit.."), _q("Ctrl+E"), parent );
  action->setStatusTip( _q("edit working copy configuration") );
  action->setEnabled(false);
  parent->connect( action, SIGNAL(activated()), SLOT(editWcPrjItem()) );
  _actions.addAction( ItemActionEdit, action );

  action = new QAction( _q("&delete"), _q("CTRL+D"), parent );
  action->setStatusTip( _q("delete working copy") );
  action->setEnabled(false);
  parent->connect( action, SIGNAL(activated()), SLOT(delWcPrjItem()) );
  _actions.addAction( ItemActionRemove, action );

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

    action = _actions.getAction( ActionUpdate );
    action->addTo(_menu);
    action = _actions.getAction( ActionUpdateRevision );
    action->addTo(_menu);

    _menu->insertSeparator();

    action = _actions.getAction( ActionCommit );
    action->addTo(_menu);
    action = _actions.getAction( ActionLog );
    action->addTo(_menu);

    _menu->insertSeparator();

    action = _actions.getAction( ItemActionCurrent ); 
    action->addTo(_menu);
    action = _actions.getAction( ItemActionEdit ); 
    action->addTo(_menu);
    action = _actions.getAction( ItemActionRemove ); 
    action->addTo(_menu);
  }
}

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

QString WorkingCopyLvi::text( int column ) const
{
  switch( column )
  {
  case 0:
    {
      return super::text(0);
    }
  case 1:
    {
      if( ! _svnversion.isEmpty() )
      {
        return "<@ " + _svnversion + ">";
      }
    }
  }
  return super::text(column);
}

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

  if( lvi )
  {
    return compare3( getSortPos(), lvi->getSortPos() );
  }
  else
  {
    return super::compare(i,col,ascending);
  }
}

bool WorkingCopyLvi::acceptDrop( const QMimeSource* mime ) const
{
  return mime->provides( ScMimeTypeRepository ) 
    ||   mime->provides( ScMimeTypeWorkingCopy );
}

void WorkingCopyLvi::paintFocus( QPainter * p, const QColorGroup & cg, const QRect & r )
{
  super::paintFocus(p,cg,r);

  //if( isRunning() )
  //{
    //p->setPen( QColor(255,255,0) );
    //p->drawRect( r );
  //}
}

void WorkingCopyLvi::paintBranches( QPainter* p, const QColorGroup& cg, int w, int y, int h )
{
  super::paintBranches(p,cg,w,y,h);

  //printf( "paintBranch w %d y %d h %d\n", w, y, h );
  //p->setPen( QColor(0,0,0) );
  //p->drawRect( 0, 0, 4, 4 );
}

void WorkingCopyLvi::paintCell( QPainter *p, const QColorGroup & cg, int column, int width, int alignment )
{
  QColorGroup g(cg);

  if( column == 0 )
  {
    // highlight if we are the current wc root
    if( isCurrentRoot() )
    {
      QFont f = p->font();
      f.setBold(true);
      p->setFont(f);
    }
  }
  else if( column == 1 ) // fade the revision
  {
    g.setColor( QColorGroup::Text,            ColorStorage::getColor(ColorFaded) );
    g.setColor( QColorGroup::HighlightedText, ColorStorage::getColor(ColorFaded) );
  }

  super::paintCell(p,g,column,width,alignment);
}

void WorkingCopyLvi::dropped( QDropEvent* e )
{
  ProjectItem prjItem;
  ProjectItemDragObject::decode(e,prjItem);

  if( ! (prjItem._prjId == _prj->getId()) || prjItem._itemId == _wcItem.getId() )
  {
    return;
  }

  _prj->moveItem( prjItem._itemId, _wcItem.getId() );
  ((ProjectLvi*)parent())->updateBookmarks();

  _wcModel->saveProject(_prj);
}

QDragObject* WorkingCopyLvi::dragObject()
{
  ProjectItem prjItem(_prj->getId(),_wcItem.getId());
  return new ProjectItemDragObject( ScMimeTypeWorkingCopy, listView(), prjItem );
}

Project* WorkingCopyLvi::getProject() const
{
  return _prj;
}

void WorkingCopyLvi::onItem()
{
  QString path = getWorkingCopyPath();
  postEvent( getTidObj(), new LviOnItemEvent(path) );
}

void WorkingCopyLvi::pressed( bool /*refresh*/ )
{
  // TODO replace with visitor

  ProjectFoldersWidget* pfw = dynamic_cast<ProjectFoldersWidget*>(listView());

  pfw->status( getId(), _wcItem.getSource(), _prj );
}

void WorkingCopyLvi::renamed( const QString& text )
{
  _wcItem.setName( sc::String(text.utf8()) );
  _prj->setItem(_wcItem);
}

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

long WorkingCopyLvi::getSortPos() const
{
  return _wcItem.getSortPos();
}

const Project::Item& WorkingCopyLvi::getProjectItem() const
{
  return _wcItem;
}

void WorkingCopyLvi::setProjectItem( const Project::Item& item )
{
  _wcItem = item;

  setText( 0, QString::fromUtf8(_wcItem.getName()) );
}

void WorkingCopyLvi::setSvnversion( const QString& version )
{
  _svnversion = version;
}

QString WorkingCopyLvi::getWorkingCopyPath() const
{
  return QString::fromUtf8(_wcItem.getSource());
}

void WorkingCopyLvi::checkout()
{
}

void WorkingCopyLvi::update()
{
  UpdateParam* param = new UpdateParam( sc::String(getWorkingCopyPath().utf8()),
    svn::RevisionPtr(new svn::Revision(svn::Revision_Unspecified)), true );
  PostCmdResult* pcres = new PostCmdResult(getTid());

  _wcModel->update( param, pcres );
}

void WorkingCopyLvi::updateRev()
{
  ExternProviderImpl externProvider(_wcModel);
  
  UpdateDialog* dlg =
    new UpdateDialog(&externProvider, _wcModel->isCmdRecursive(),listView());
  
  QString wcPath = getWorkingCopyPath();
  dlg->setWorkingCopyPath( wcPath );
  
  int result = dlg->exec();
  
  if( result != QDialog::Accepted )
  {
    return;
  }
  
  UpdateParam* param = new UpdateParam(
    sc::String(dlg->getWorkingCopyPath().utf8()),
    svn::RevisionPtr(dlg->getRevision()), dlg->isRecursive() );
  PostCmdResult* pcres = new PostCmdResult(getTid());
  
  _wcModel->update( param, pcres );
}

void WorkingCopyLvi::commit()
{
  svn::Paths paths;
  paths.push_back( sc::String(getWorkingCopyPath().utf8()) );
  
  CommitDialogCmd cmd( listView(), getTid(), _wcModel );
  
  cmd.run(paths);
}

void WorkingCopyLvi::log()
{
#if 0
  LogDialog* ldlg = new LogDialog( _wcModel, getProject(),
    sc::String(getWorkingCopyPath().utf8()), true );

  ldlg->show();
#endif
}

bool WorkingCopyLvi::isCurrentRoot()
{
  return _prj->isCurrentWorkingCopy(_wcItem);
}

QString WorkingCopyLvi::makePathRelative( QListViewItem* parent, QString path )
{
  WorkingCopyLvi* wc =  dynamic_cast<WorkingCopyLvi*>(parent);

  if( ! wc )
  {
    return path;
  }

  return stripPath( path, wc->getWorkingCopyPath() );
}

void WorkingCopyLvi::enableActions()
{
  _actions.enableActions();
  _actions.enableAction( ItemActionCurrent, !isCurrentRoot() );
}

void WorkingCopyLvi::disableActions()
{
  _actions.disableActions();
}
