/* ====================================================================
 * Copyright 2003-2008  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 "WcViewCmdProgress.h"
#include "Cancel.h"
#include "LogData.h"
#include "events/LoggingEvent.h"
#include "events/EventSupport.h"
#include "commands/ScParamVisitor.h"
#include "commands/AddParam.h"
#include "commands/BlameParam.h"
#include "commands/CheckoutParam.h"
#include "commands/CommitParam.h"
#include "commands/CopyParam.h"
#include "commands/DeleteParam.h"
#include "commands/DiffParam.h"
#include "commands/EditConflictParam.h"
#include "commands/ResolvedParam.h"
#include "commands/MergeParam.h"
#include "commands/MkdirParam.h"
#include "commands/RevertParam.h"
#include "commands/StatusParam.h"
#include "commands/SwitchParam.h"
#include "commands/UpdateParam.h"
#include "commands/LogParam.h"
#include "commands/LogGraphParam.h"
#include "commands/ListParam.h"
#include "commands/MoveParam.h"
#include "commands/PropListParam.h"
#include "commands/PropSetParam.h"
#include "commands/PropSetRevParam.h"
#include "commands/ImportParam.h"
#include "commands/ExportParam.h"
#include "commands/CleanupParam.h"
#include "commands/LockParam.h"
#include "commands/UnlockParam.h"
#include "commands/IgnoreParam.h"
#include "sublib/PathSimplifier.h"
#include "svn/WcStatus.h"

// qt
#include <QtCore/QString>

// todo rename source class
typedef LoggingEvent CmdProgressEvent;

///////////////////////////////////////////////////////////////////////////////

/** a helper class that prepares log information for each command. */
class CmdProgressStartedVisitor : 
public ParamVisitor<AddParam>,
public ParamVisitor<BlameParam>,
public ParamVisitor<CheckoutParam>,
public ParamVisitor<CommitParam>,
public ParamVisitor<CopyParam>,
public ParamVisitor<DeleteParam>,
public ParamVisitor<DiffParam>,
public ParamVisitor<EditConflictParam>,
public ParamVisitor<ListParam>,
public ParamVisitor<LogParam>,
public ParamVisitor<LogGraphParam>,
public ParamVisitor<MergeParam>,
public ParamVisitor<MkdirParam>,
public ParamVisitor<RevertParam>,
public ParamVisitor<ResolvedParam>,
public ParamVisitor<StatusParam>,
public ParamVisitor<SwitchParam>,
public ParamVisitor<UpdateParam>,
public ParamVisitor<MoveParam>,
public ParamVisitor<PropListParam>,
public ParamVisitor<PropSetParam>,
public ParamVisitor<PropSetRevParam>,
public ParamVisitor<ImportParam>,
public ParamVisitor<ExportParam>,
public ParamVisitor<CleanupParam>,
public ParamVisitor<LockParam>,
public ParamVisitor<UnlockParam>,
public ParamVisitor<IgnoreParam>
{
public:
  CmdProgressStartedVisitor( QObject* o, long id, Cancel* cb )
    : _o(o), _id(id), _cb(cb)
  {
  }

  /** post progress log information. */
  void post( const QString& cmd, const QString& msg, bool cancelable )
  {
    LogData* data = new LogData( _id,
      Log::Started | (cancelable ? Log::Cancelable : 0), 
      sc::String(cmd.utf8()), sc::NullString, sc::String(msg.utf8()), _cb );

    postEvent( _o, new CmdProgressEvent(data) );
  }

  void visit( ListParam* p )
  {
    QString cmd = _q("list");
    QString msg = _q("running list on %1").arg( QString::fromUtf8(p->getPathOrUrl()) );
    post( cmd, msg, _cb );
  }

  void visit( StatusParam* p )
  {
    QString act = _q("status");
    QString msg = _q("running status on %1").arg( QString::fromUtf8(p->getPath()) );
    post( act, msg, true );
  }

  void visit( UpdateParam* p )
  {
    QString act = _q("update");
    QString msg = _q("updating working copy %1").arg( QString::fromUtf8(p->getPath()) );
    post( act, msg, true );
  }

  void visit( CommitParam* p )
  {
    QString act = _q("commit");
    QString msg = (p->getPaths().size() == 1)
      ? _q("committing %1").arg( QString::fromUtf8( p->getPaths().front() ))
      : _q("committing selection");
    post( act, msg, true );
  }

  void visit( MkdirParam* p )
  {
    QString act = _q("mkdir");
    QString msg = _q("creating folder %1").arg( QString::fromUtf8(p->getPathsOrUrls().front()) );
    post( act, msg, true );
  }

  void visit( AddParam* p )
  {
    QString act = _q("add");
    QString msg = (p->getPaths().size() == 1)
      ? _q("adding %1").arg( QString::fromUtf8( p->getPaths().front() ) )
      : _q("adding selection");
    post( act, msg, true );
  }

  void visit( RevertParam* p )
  {
    QString act = _q("revert");
    QString msg =(p->getPaths().size() == 1)
      ? _q("reverting %1").arg( QString::fromUtf8( p->getPaths().front() ) )
      : _q("reverting selection");
    post( act, msg, true );
  }

  void visit( DiffParam* p )
  {
    QString act = _q("diff");
    QString msg;

    if(p->isPeg())
    {
      msg =
        _q("running diff on %1@%2 between %3 and %4")
        .arg( QString::fromUtf8(p->getPathOrUrl1()) )
        .arg( QString::fromUtf8(p->getPegRevision()->toString()) )
        .arg( QString::fromUtf8(p->getRevision1()->toString()) )
        .arg( QString::fromUtf8(p->getRevision2()->toString()) );
    }
    else
    {
      PathSimplifier s(p->getPathOrUrl1(),p->getPathOrUrl2());

      msg =
        _q("running diff between %2@%3 and %4@%5 (%1)")
        .arg( QString::fromUtf8(s.getPrefix()) )
        .arg( QString::fromUtf8(s.getPath1()) )
        .arg( QString::fromUtf8(p->getRevision1()->toString()) )
        .arg( QString::fromUtf8(s.getPath2()) )
        .arg( QString::fromUtf8(p->getRevision2()->toString()) );
    }
    post( act, msg, false );
  }

  void visit( EditConflictParam* p )
  {
    QString act = _q("edit");
    QString msg = _q("running conflict editor with %1")
      .arg( QString::fromUtf8(p->getStatus()->getName()) );
    post( act, msg, false );
  }

  void visit( ResolvedParam* p )
  {
    QString act = _q("resolved");
    QString msg = _q("setting conflict(s) to resolved for %1")
      .arg( QString::fromUtf8(p->getPath()) );
    post( act, msg, true );
  }

  void visit( LogParam* p )
  {
    QString act = _q("log");
    QString msg = (p->getPathsOrUrls().size() == 1)
      ? _q("running log on %1").arg( QString::fromUtf8( p->getPathsOrUrls().front() ) )
      : _q("running log on the selection");
    post( act, msg, true );
  }

  void visit( LogGraphParam* p )
  {
    QString act = _q("log graph");
    QString msg = _q("retrieving graph information for %1").arg(QString::fromUtf8(p->getSource()));
    post( act, msg, true );
  }

  void visit( CheckoutParam* p )
  {
    QString act = _q("checkout");
    QString msg = _q("checking out %1@%2 at %3 to %4")
      .arg( QString::fromUtf8(p->getUrl()) )
      .arg( QString::fromUtf8(p->getPegRevision()->toString()) )
      .arg( QString::fromUtf8(p->getRevision()->toString()) )
      .arg( QString::fromUtf8(p->getPath()) );
    post( act, msg, true );
  }

  void visit( MergeParam* p )
  {
    QString act = _q("merge");
    QString msg = p->isPeg()
      ? _q("merging %1@%2 from %3 to %4 into %5")
        .arg( QString::fromUtf8(p->getPathOrUrl1()) )
        .arg( QString::fromUtf8(p->getPegRevision()->toString()) )
        .arg( QString::fromUtf8(p->getRevision1()->toString()) )
        .arg( QString::fromUtf8(p->getRevision2()->toString()) )
        .arg( QString::fromUtf8(p->getTargetPath()) )
      : _q("merging from %1@%2 to %3@%4 into %5")
        .arg( QString::fromUtf8(p->getPathOrUrl1()) )
        .arg( QString::fromUtf8(p->getRevision1()->toString()) )
        .arg( QString::fromUtf8(p->getPathOrUrl2()) )
        .arg( QString::fromUtf8(p->getRevision2()->toString()) )
        .arg( QString::fromUtf8(p->getTargetPath()) );
    post( act, msg, true );
  }

  void visit( SwitchParam* p )
  {
    QString act = _q("switch");
    QString msg = _q("switching %1 to %2")
      .arg( QString::fromUtf8(p->getPath()) )
      .arg( QString::fromUtf8(p->getUrl()) );
    post( act, msg, true );
  }

  void visit( DeleteParam* p )
  {
    QString act = _q("delete");
    QString msg =(p->getPaths().size() == 1)
      ? _q("deleting %1").arg( QString::fromUtf8( p->getPaths().front() ) )
      : _q("deleting selection");
    post( act, msg, true );
  }

  void visit( CopyParam* p )
  {
    QString act = _q("copy");
    QString msg = (p->getSrcPathsOrUrls().size() == 1)
      ? _q("copying %1 to %2")
        .arg( QString::fromUtf8( p->getSrcPathsOrUrls().front() ) )
        .arg( QString::fromUtf8(p->getDstPathOrUrl()) )
      : _q("copying selection to %2")
        .arg( QString::fromUtf8(p->getDstPathOrUrl()) );
    post( act, msg, true );
  }

  void visit( MoveParam* p )
  {
    QString act = _q("move");
    QString msg =(p->getSrcPathsOrUrls().size() == 1)
      ? _q("moving %1 to %2")
        .arg( QString::fromUtf8( p->getSrcPathsOrUrls().front() ) )
        .arg( QString::fromUtf8( p->getDstPathsOrUrls().front() ) )
      : _q("moving selection");
    post( act, msg, true );
  }

  void visit( PropListParam* p )
  {
    QString act = _q("proplist");
    QString msg = _q("listing properties of %1")
      .arg( QString::fromUtf8(p->getPathOrUrl()) );
    post( act, msg, true );
  }

  void visit( PropSetParam* p )
  {
    QString act = _q("propset");
    QString msg = _q("setting property %1 on %2")
      .arg( QString::fromUtf8(p->getPropName()) )
      .arg( QString::fromUtf8(p->getPath()) );
    post( act, msg, true );
  }

  void visit( PropSetRevParam* p )
  {
    QString act = _q("propsetrev");
    QString msg = _q("setting revision property %1 on revision %2")
      .arg( QString::fromUtf8(p->getPropName()) )
      .arg( QString::fromUtf8(p->getRevision()->toString()) );
    post( act, msg, true );
  }

  void visit( BlameParam* p )
  {
    QString act = _q("blame");
    QString msg = _q("running blame on %1")
      .arg( QString::fromUtf8( p->getPathOrUrl() ) );
    post( act, msg, true );
  }

  void visit( ImportParam* p )
  {
    QString act = _q("import");
    QString msg = _q("importing %1 to %2")
      .arg( QString::fromUtf8( p->getPath() ) )
      .arg( QString::fromUtf8( p->getUrl() ) );
    post( act, msg, true );
  }

  void visit( ExportParam* p )
  {
    QString act = _q("export");
    QString msg = _q("exporting %1 to %2")
      .arg( QString::fromUtf8( p->getSrcPathOrUrl() ) )
      .arg( QString::fromUtf8( p->getPath() ) );
    post( act, msg, true );
  }

  void visit( CleanupParam* p )
  {
    QString act = _q("cleanup");
    QString msg = _q("cleaning up %1")
      .arg( QString::fromUtf8( p->getPath() ) );
    post( act, msg, true );
  }

  void visit( LockParam* p )
  {
    QString act = _q("lock");
    QString msg = (p->getPathsOrUrls().size() == 1)
      ? _q("locking %1")
        .arg( QString::fromUtf8( p->getPathsOrUrls().front() ) )
      : _q("locking selection");
    post( act, msg, true );
  }

  void visit( UnlockParam* p )
  {
    QString act = _q("unlock");
    QString msg = (p->getPathsOrUrls().size() == 1)
      ? _q("unlocking %1")
        .arg( QString::fromUtf8( p->getPathsOrUrls().front() ) )
      : _q("unlocking selection");
    post( act, msg, true );
  }

  void visit( IgnoreParam* p )
  {
    QString act = _q("ignore");
    QString msg = (p->getPaths().size() == 1)
      ? _q("ignoring %1")
        .arg( QString::fromUtf8( p->getPaths().front() ) )
      : _q("ignoring selection");
    post( act, msg, true );
  }

private:
  QObject* _o;

  long     _id;
  Cancel*  _cb;
};

///////////////////////////////////////////////////////////////////////////////

class CmdProgressFinishedVisitor : 
public ParamVisitor<AddParam>,
public ParamVisitor<BlameParam>,
public ParamVisitor<CheckoutParam>,
public ParamVisitor<CommitParam>,
public ParamVisitor<CopyParam>,
public ParamVisitor<DeleteParam>,
public ParamVisitor<DiffParam>,
public ParamVisitor<EditConflictParam>,
public ParamVisitor<ListParam>,
public ParamVisitor<LogParam>,
public ParamVisitor<LogGraphParam>,
public ParamVisitor<MergeParam>,
public ParamVisitor<MkdirParam>,
public ParamVisitor<RevertParam>,
public ParamVisitor<ResolvedParam>,
public ParamVisitor<StatusParam>,
public ParamVisitor<SwitchParam>,
public ParamVisitor<UpdateParam>,
public ParamVisitor<MoveParam>,
public ParamVisitor<PropListParam>,
public ParamVisitor<PropSetParam>,
public ParamVisitor<PropSetRevParam>,
public ParamVisitor<ImportParam>,
public ParamVisitor<ExportParam>,
public ParamVisitor<CleanupParam>,
public ParamVisitor<LockParam>,
public ParamVisitor<UnlockParam>,
public ParamVisitor<IgnoreParam>
{
public:
  CmdProgressFinishedVisitor( QObject* o, long id, double ms )
    : _o(o), _id(id), _ms(ms)
  {
  }

  void post( const sc::Error* err, const sc::String& msg = sc::NullString )
  {
    if( err == sc::Success )
    {
      LogData* data = new LogData( _id, Log::Finished, 
        _s("result"), sc::NullString, msg.isEmpty() ? _s("done") : msg );

      postEvent( _o, new LoggingEvent(data) );
    }
    else if( err->getCode() == SVN_ERR_CANCELLED )
    {
      LogData* data = new LogData( _id, Log::Finished,
        sc::NullString, sc::NullString, _s("canceled.") );

      postEvent( _o, new LoggingEvent(data) );
    }
    else
    {
      const sc::Error* terr = err;
      while( terr )
      {
        LogData* error = new LogData( _id, Log::Error,
         _s("error"), sc::NullString, terr->getMessage() );
        
        postEvent( _o, new LoggingEvent(error) );
        
        terr = terr->getNested();
      }
      
      LogData* data = new LogData( _id, Log::Finished, 
        _s("result"), sc::NullString, _s("failed") );
      
      postEvent( _o, new LoggingEvent(data) );
    }
  }

  void visit( ListParam* p )
  {
    post( p->getError() );
  }

  void visit( StatusParam* p )
  {
    post( p->getError() );
  }

  void visit( UpdateParam* p )
  {
    post( p->getError() );
  }

  void visit( CommitParam* p )
  {
    QString qmsg = (p->getInfo().getRevnumber() >= 0)
      ? _q("done, comitted as r%1.").arg( p->getInfo().getRevnumber() )
      : _q("done, not comitted, there were no changes.");

    post( p->getError(), sc::String(qmsg.utf8()) );
  }

  void visit( MkdirParam* p )
  {
    post( p->getError() );
  }

  void visit( AddParam* p )
  {
    post( p->getError() );
  }

  void visit( RevertParam* p )
  {
    post( p->getError() );
  }

  void visit( DiffParam* p )
  {
    post( p->getError() );
  }

  void visit( EditConflictParam* p )
  {
    post( p->getError() );
  }

  void visit( ResolvedParam* p )
  {
    post( p->getError() );
  }

  void visit( LogParam* p )
  {
    post( p->getError() );
  }

  void visit( LogGraphParam* p )
  {
    post( p->getError() );
  }

  void visit( CheckoutParam* p )
  {
    post( p->getError() );
  }

  void visit( MergeParam* p )
  {
    post( p->getError() );
  }

  void visit( SwitchParam* p )
  {
    post( p->getError() );
  }

  void visit( DeleteParam* p )
  {
    post( p->getError() );
  }

  void visit( CopyParam* p )
  {
    post( p->getError() );
  }

  void visit( MoveParam* p )
  {
    post( p->getError() );
  }

  void visit( PropListParam* p )
  {
    post( p->getError() );
  }

  void visit( PropSetParam* p )
  {
    post( p->getError() );
  }

  void visit( PropSetRevParam* p )
  {
    post( p->getError() );
  }

  void visit( BlameParam* p )
  {
    post( p->getError() );
  }

  void visit( ImportParam* p )
  {
    post( p->getError() );
  }

  void visit( ExportParam* p )
  {
    post( p->getError() );
  }

  void visit( CleanupParam* p )
  {
    post( p->getError() );
  }

  void visit( LockParam* p )
  {
    post( p->getError() );
  }

  void visit( UnlockParam* p )
  {
    post( p->getError() );
  }

  void visit( IgnoreParam* p )
  {
    post( p->getError() );
  }

private:
  QObject* _o;

  long     _id;
  double   _ms;
};

///////////////////////////////////////////////////////////////////////////////

WcViewCmdProgress::WcViewCmdProgress( QObject* target )
: _target(target)
{
}

void WcViewCmdProgress::started( ScParam* p, Cancel* c )
{
  CmdProgressStartedVisitor v(_target,p->getId(),c);
  p->accept(&v);
}

void WcViewCmdProgress::finished( ScParam* p, double time )
{
  CmdProgressFinishedVisitor v(_target,p->getId(),time);
  p->accept(&v);
}
