/* ====================================================================
 * 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 "StatusCmd2.h"
#include "StatusParam.h"
#include "../StatusResult.h"
#include "ScCmdData.h"
#include "CmdResultCallback.h"
#include "CmdProgressCallback.h"
#include "svn/Client.h"
#include "svn/WcStatus.h"
#include "svn/StatusBaton.h"
#include "svn/ClientContext.h"
#include "svn/CancelBaton.h"
#include "svn/Error.h"
#include "util/Error.h"

// sys
#include <queue>

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

class StatusBaton2 : public svn::StatusBaton
{
public:
  StatusBaton2( svn::WcStatuss& statuss, bool repos )
    : svn::StatusBaton(), _repos(repos), _statuss(statuss)
  {
  }

  void status( const char* path, svn::WcStatusPtr status )
  {
    status->calcLockStatus(_repos);
    _statuss.push_back(status);
  }

  bool            _repos;
  svn::WcStatuss& _statuss;
};


class LessWcStatus
{
public:
  bool operator()( svn::WcStatusPtr a, svn::WcStatusPtr b )
  {
    return a->getName() < b->getName();
  }
};

class StatusBaton3 : public svn::StatusBaton
{
public:
  StatusBaton3( bool repos )
    : svn::StatusBaton(), _repos(repos)
  {
  }

  void status( const char* path, svn::WcStatusPtr status )
  {
    status->calcLockStatus(_repos);
    _statuss.push_back(status);
  }

  // fix random order
  void sort()
  {
    std::sort( _statuss.begin(), _statuss.end(), LessWcStatus() );
  }

  bool           _repos;
  svn::WcStatuss _statuss;
};
//
///////////////////////////////////////////////////////////////////////////////

StatusCmd2::StatusCmd2( StatusParam* param, StatusResult* sr, CmdResultCallback* cb )
: ScCmd(param->getId()), _param(param), _sr(sr), _cb(cb)
{
}

StatusCmd2::~StatusCmd2()
{
  delete _cb;
}


typedef std::queue<sc::String> StatusPaths;

void StatusCmd2::run()
{
  sc::Error* err = sc::Success;
  _prgscb->started( _param, getCancel() );

  //int i = 0;
  //int j = 0;
  //int k = i/j;

  StatusPaths paths;
  paths.push( _param->getPath() );
  
  while( ! paths.empty() )
  {
    //printf( "%s\n", (const char*)paths.front() );

    if( _client->getContext()->getCancelBaton()->isCanceled() )
    {
      err = svn::wrapError(svn_error_create(SVN_ERR_CANCELLED, NULL, _s("status canceled")));
      goto done;
    }

    StatusBaton3   baton(_param->getUpdate());
    svn::Revnumber revnum;

    // if refresh or not cached..
    if( _param->getRefresh() || (! _sr->getWcStatus( paths.front(), baton._statuss )) )
    {
      err = _client->status( &revnum, paths.front(), *_param->getRevision(), &baton,
        false, _param->getAll(), _param->getUpdate(), _param->getIgnore() );

      if( err != sc::Success )
      {
        goto done;
      }

      baton.sort();  // fix order
      _sr->setWcStatus( paths.front(), baton._statuss );
    }

    StatusParam* param = new StatusParam( paths.front()/*_param->getPath()*/, 0/*_param->getRevision()*/,
      _param->getRecurse(), _param->getAll(), _param->getUpdate(), _param->getIgnore(),
      _param->getRefresh(), true );
    param->getStatuss() = baton._statuss;
    _cb->handleResult( param, sc::Success/*err*/ );

    if( _param->getRecurse() )
    {
      for( svn::WcStatuss::iterator it = baton._statuss.begin(); it != baton._statuss.end(); it++ )
      {
        svn::WcStatusPtr status = *it;
  
        if( status->isDir() && !(paths.front() == status->getName()) )
        {
          paths.push( status->getName() );
        }
      }
    }

    paths.pop();
  }

done:
  _param->setError( err );
  _prgscb->finished( _param );

  _cb->handleResult( _param, err );
}
