/* ====================================================================
 * Copyright (c) 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 "PostProgressCallback.h"
#include "LogData.h"
#include "Cancel.h"
#include "events/LoggingEvent.h"
#include "events/CancelStartEvent.h"
#include "events/CancelStopEvent.h"
#include "events/EventSupport.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 "svn/WcStatus.h"

// qt
#include <QtCore/QString>

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

class StartedVisitor : 
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:
  StartedVisitor( QObject* o, long id, Cancel* cb )
    : _o(o), _id(id), _cb(cb)
  {
  }

  /**
   * post log info.
   */
  void post( const QString& action, const QString& msg )
  {
    LogData* data = new LogData( _id, Log::Started, 
      sc::String(action.utf8()), sc::NullString, sc::String(msg.utf8()) );

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

  /**
   * post log info and cancel start.
   */
  void postC( const QString& action, const QString& msg )
  {
    post(action,msg);

    postEvent( _o, new CancelStartEvent(_id,_cb) );
  }

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

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

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

  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");
    postC( act, msg );
  }

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

  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");
    postC( act, msg );
  }

  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");
    postC( act, msg );
  }

  void visit( DiffParam* p )
  {
    QString act = _q("diff:");
    QString msg = p->isPeg()
      ? _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()) )
      : _q("running diff between %1@%2 and %3@%4")
        .arg( QString::fromUtf8(p->getPathOrUrl1()) )
        .arg( QString::fromUtf8(p->getRevision1()->toString()) )
        .arg( QString::fromUtf8(p->getPathOrUrl2()) )
        .arg( QString::fromUtf8(p->getRevision2()->toString()) );
    post( act, msg );
  }

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

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

  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");
    postC( act, msg );
  }

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

  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()) );
    postC( act, msg );
  }

  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()) );
    postC( act, msg );
  }

  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()) );
    postC( act, msg );
  }

  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");
    postC( act, msg );
  }

  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()) );
    postC( act, msg );
  }

  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");
    postC( act, msg );
  }

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

  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()) );
    postC( act, msg );
  }

  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()) );
    postC( act, msg );
  }

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

  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() ) );
    postC( act, msg );
  }

  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() ) );
    postC( act, msg );
  }

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

  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");
    postC( act, msg );
  }

  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");
    postC( act, msg );
  }

  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");
    postC( act, msg );
  }

private:
  QObject* _o;

  long     _id;
  Cancel*  _cb;
};

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

class FinishedVisitor : 
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:
  FinishedVisitor( QObject* o, long id ) : _o(o), _id(id)
  {
  }

  void post( const sc::Error* err, bool stopCancel )
  {
    post( err, stopCancel, _s("done.") );
  }

  void post( const sc::Error* err, bool stopCancel, const sc::String& successMsg )
  {
    if( stopCancel )
    {
      postEvent( _o, new CancelStopEvent(_id) );
    }

    if( err == sc::Success )
    {
      LogData* data = new LogData( _id, Log::Stopped, 
        sc::NullString, sc::NullString, successMsg );

      postEvent( _o, new LoggingEvent(data) );
    }
    else if( err->getCode() == SVN_ERR_CANCELLED )
    {
      LogData* data = new LogData( _id, Log::Stopped | Log::Canceled,
        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::Running | Log::Error,
          _s("error:"), sc::NullString, terr->getMessage() );

        postEvent( _o, new LoggingEvent(error) );

        terr = terr->getNested();
      }

      LogData* data = new LogData( _id, Log::Stopped, 
        sc::NullString, sc::NullString, _s("failed.") );

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

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

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

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

  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.");

    sc::String smsg(qmsg.utf8());

    post( p->getError(), true, smsg );
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

private:
  QObject* _o;

  long     _id;
};

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

PostProgressCallback::PostProgressCallback( QObject* target )
: _target(target), _time(true)
{
}

void PostProgressCallback::started( long id, ScParam* p, Cancel* cb )
{
  StartedVisitor v(_target,id,cb);
  p->accept(&v);
}

void PostProgressCallback::finished( long id, ScParam* p, double ms )
{
  FinishedVisitor v(_target,id);
  p->accept(&v);
}

void PostProgressCallback::setTime( bool b )
{
  _time = b;
}

void PostProgressCallback::time( long id, double time )
{
  if( _time )
  {
    QString msg = QString( "[%1 ms]" ).arg(time,0,'f',2);

    LogData* data = new LogData( id, Log::Time, 
      sc::NullString, sc::NullString, sc::String(msg.utf8()) );

    postEvent( _target, new LoggingEvent(data) );
  }
}
