/* ====================================================================
 * Copyright (c) 2003-2009  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 "ScModel.h"
#include "Repository.h"
#include "Project.h"
#include "AuthPromptProvider.h"
#include "Cancel.h"
#include "Notify.h"
#include "Commit.h"
#include "ConfigManager.h"
#include "WcStatusCache.h"
#include "RepositoryCache.h"
#include "ProjectSorter.h"
#include "WcViewCmdProgress.h"
#include "NullCmdProgress.h"
#include "commands/ScCmd.h"
#include "commands/CmdProgressCallback.h"
#include "events/EventSupport.h"
#include "sublib/Utility.h"
#include "util/Exception.h"
#include "util/apr.h"
#include "util/Guard.h"
#include "svn/Client.h"
#include "svn/ClientContext.h"

// svn
#include <svn_utf.h>

// apr
#include <apr_thread_proc.h>


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

class ThreadParam
{
public:
  ThreadParam( ScCmd* cmd, svn::Client* client, CmdProgressCallback* cb, ScModel* model )
    : _cmd(cmd), _client(client), _prgscb(cb), _model(model)
  {
  }

  ~ThreadParam()
  {
    delete _client;
    delete _cmd;
  }

  ScCmd*               _cmd;
  svn::Client*         _client;
  CmdProgressCallback* _prgscb;

private:
  ScModel*             _model;
};


// apr_thread_start_t
void* APR_THREAD_FUNC ThreadFunc( apr_thread_t* t, void* d )
{
  initStackThread();
  try
  {
    ThreadParam* p = static_cast<ThreadParam*>(d);

    p->_cmd->run( p->_client, p->_prgscb );

    delete p;
  }
  catch( sc::Exception& e )
  {
    postException(e);
  }

  stopStackThread();
  return 0;
}

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


ScModel::ScModel( ConfigManager* conf )
  : _conf(conf), _progress(new NullCmdProgress())
{
  _pool = apr::createPool();

  // speeds up iconv stuff
  svn_utf_initialize(_pool);

  _listCache = new RepositoryCache();
  _statusCache = new WcStatusCache();

  _conf->getProjects( _projects );
}

ScModel::~ScModel()
{
  delete _progress;

  Projects::iterator it = _projects.begin();
  for( ; it != _projects.end(); it++ )
  {
    delete *it;
  }

  delete _statusCache;
  delete _listCache;

  apr::destroyPool(_pool);
}

Project* ScModel::createProject()
{
  Project* prj = new Project( Uuid::createUuid(), _s("new project ...") );

  prj->setIndex(   (long)_projects.size() );
  prj->setSortPos( (long)_projects.size() );
  prj->preset();

  return prj;
}

void ScModel::addProject( Project* project )
{
  _projects.push_back(project);
  _conf->setProject(project);
  _conf->save();
}

void ScModel::removeProject( Project* project )
{
  _conf->removeProject(project);

  for( Projects::iterator it = _projects.begin() ; it != _projects.end(); it++ )
  {
    if( (*it)->getId() == project->getId() )
    {
      _projects.erase(it);
      break;
    }
  }
  delete project;

  // reorder project config index
  long index = 0;
  for( Projects::iterator it = _projects.begin() ; it != _projects.end(); it++ )
  {
    if( (*it)->getIndex() != index )
    {
      _conf->removeProject(*it);
      (*it)->setIndex(index);
      _conf->setProject(*it);
    }
    index++;
  }
  _conf->save();
}

const Projects& ScModel::getProjects()
{
  return _projects;
}

void ScModel::reorderProjects( Project* src, Project* dst )
{
  ProjectSorter sort(_projects);
  sort.move( src->getSortPos(), dst->getSortPos() );
}

void ScModel::saveProject( Project* project )
{
  _conf->setProject(project);
  _conf->save();
}

void ScModel::saveProjects()
{
  for( Projects::iterator it = _projects.begin(); it != _projects.end(); it++ )
  {
    _conf->setProject(*it);
  }
  _conf->save();
}

void ScModel::runThreaded( ScCmd* cmd )
{
  runAsync(cmd,_progress);
}

void ScModel::runBlocked( ScCmd* cmd )
{
  runSync(cmd,_progress);
}

void ScModel::runAsync( ScCmd* cmd, CmdProgressCallback* pcb )
{
  apr_status_t  status;

  apr_threadattr_t* attr   = 0;
  apr_thread_t*     handle = 0;

  svn::Client* client = createSvnClient( cmd->getId(), pcb );
  ThreadParam* param  = new ThreadParam( cmd, client, pcb, this );

  status = apr_threadattr_create( &attr, _pool );
  status = apr_thread_create( &handle, attr, &ThreadFunc, param, _pool );
}

void ScModel::runSync( ScCmd* cmd, CmdProgressCallback* pcb )
{
  svn::Client* client = createSvnClient( cmd->getId(), pcb );

  cmd->run( client, pcb );
}

void ScModel::setProgressCallback( CmdProgressCallback* progress )
{
  _progress = progress;
}

bool ScModel::isWorkingCopy( const sc::String& path )
{
  return svn::Client::isWorkingCopy(path);
}

bool ScModel::getOptionCommandForce()
{
  return _conf->getOptCommandForce();
}

void ScModel::setOptionCommandForce(bool b)
{
  _conf->setOptCommandForce(b);
}

bool ScModel::getOptionCommandRecurse()
{
  return _conf->getOptCommandRecursive();
}

void ScModel::setOptionCommandRecurse(bool b)
{
  _conf->setOptCommandRecursive(b);
}

svn::Client* ScModel::createSvnClient( unsigned long id ) const
{
  return createSvnClient( id, _progress );
}

QObject* getTarget( CmdProgressCallback* cb )
{
  // hack..
  WcViewCmdProgress* prg = dynamic_cast<WcViewCmdProgress*>(cb);
  if(!prg)
    return NULL;

  return prg->getTarget();
}

svn::Client* ScModel::createSvnClient( unsigned long id,
  CmdProgressCallback* cb ) const
{
  assert(cb);

  svn::ClientContext* context = new svn::ClientContext(
    new AuthPromptProvider(), new Cancel(), new Notify(id,getTarget(cb)
    ), new Commit(), _conf->getDiffCmd(), _conf->getMergeCmd() );

  return new svn::Client(context);
}

void ScModel::saveSettings()
{
  _conf->save();
}

FontSettings* ScModel::getFontSettings()
{
  return _conf;
}

LayoutSettings* ScModel::getLayoutSettings()
{
  return _conf;
}

CommitLogHistory* ScModel::getCommitLogHistory()
{
  return _conf;
}

ListCache* ScModel::getListCache()
{
  return _listCache;
}

WcStatusCache* ScModel::getStatusCache()
{
  return _statusCache;
}

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

//#if _WIN32
//  BOOL b   = SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL );
//  DWORD le = GetLastError();
//#endif

#if 0
// qt based thread implementation

// qt
#define QT_THREAD_SUPPORT
#include <qthread.h>

class ScThread : public QThread
{
public:
  ScThread( ScCmd* cmd, svn::Client* svn )
    : _cmd(cmd), _svn(svn)
  {
  }

  void run()
  {
    _cmd->run( _svn );
  }

private:
  ScCmd*       _cmd;
  svn::Client* _svn;
};

ScThread* t = new ScThread( cmd, _svn );
t->start(QThread::IdlePriority);
#endif
