/* ====================================================================
 * Copyright (c) 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 "SingleProjectReader.h"
#include "Project.h"
#include "sublib/config/ConfigData.h"
#include "util/uuid.h"

// qt
#include <QtCore/QRegExp>


const long MaxItems    = 128;
const long MaxSubItems = 128;


SingleProjectReader::SingleProjectReader( long prjIdx,
  const ConfigValues& values )
: _prjIdx(prjIdx), _values(values), _project(NULL)
{
}

SingleProjectReader::~SingleProjectReader()
{
}

void SingleProjectReader::read()
{
  readRoot();
  readTrunkItem();
  readBranchesItem();
  readTagsItem();
  readWorkingCopyItems();
  readRepositoryItems();
}

void SingleProjectReader::readRoot()
{
  QRegExp reGuid( "project\\.\\d+\\.guid$" );
  QRegExp reName( "project\\.\\d+\\.name$" );
  QRegExp reSort( "project\\.\\d+\\.sortpos$" );
  QRegExp reCurWc( "project\\.\\d+\\.wc\\.current$" );

  sc::String name;
  Uuid uuid = Uuid::createEmpty();
  long sortPos = 0;
  long wcCurId = 0;

  for( ConfigValues::iterator it = _values.begin(); it != _values.end();
    it++ )
  {
    const ConfigValue& cval = *it;

    QString    key = QString::fromUtf8(cval.getKey());
    sc::String val = cval.getStringValue();

    if( reGuid.exactMatch(key) )
      uuid = Uuid(val);

    else if( reName.exactMatch(key) )
      name = val;

    else if( reSort.exactMatch(key) )
      sortPos = cval.getNumericValue();

    else if( reCurWc.exactMatch(key) )
      wcCurId = cval.getNumericValue();
  }

  _project = new Project( uuid, name );
  _project->setSortPos(sortPos);
  _project->setCurWorkingCopy(wcCurId);
  _project->setIndex(_prjIdx);
}

void SingleProjectReader::readTrunkItem()
{
  ConfigValues values;
  filterConfigValues( sc::String("project.\\d+.trunk."), values );

  Project::Item item;
  readRepositoryItem( values, item );
  _project->setTrunk(item);
}

void SingleProjectReader::readBranchesItem()
{
  ConfigValues values;
  filterConfigValues( sc::String("project.\\d+.branches."), values );

  Project::Item item;
  readRepositoryItem( values, item );
  _project->setBranches(item);
}

void SingleProjectReader::readTagsItem()
{
  ConfigValues values;
  filterConfigValues( sc::String("project.\\d+.tags."), values );

  Project::Item item;
  readRepositoryItem( values, item );
  _project->setTags(item);
}

void SingleProjectReader::readWorkingCopyItems()
{
  // note: the wc numbers are not used as the ProjectItem ids.
  for( int cnt = 0; cnt < MaxItems; cnt++ )
  {
    QString qpat = QString("project.\\d+.wc.%1.").arg(cnt);
    sc::String pat(qpat.utf8());

    ConfigValues values;
    filterConfigValues( pat, values );

    if( values.size() == 0 )
      return;

    Project::Item item  = _project->createWorkingCopyItem();
    readWorkingCopyItem( values, item );

    // map config current id to real current id as returned from
    // createWorkingCopyItem() above.
    if( cnt == _project->getCurWorkingCopyId() )
      _project->setCurWorkingCopy(item.getId());

    readWorkingCopySubItems( values, item );
    _project->setItem(item);
  }
}

void SingleProjectReader::readRepositoryItems()
{
  // note: the rp numbers are not used as the ProjectItem ids.
  for( int cnt = 0; cnt < MaxItems; cnt++ )
  {
    QString qpat = QString("project.\\d+.rp.%1.").arg(cnt);
    sc::String pat(qpat.utf8());

    ConfigValues values;
    filterConfigValues( pat, values );

    if( values.size() == 0 )
      return;

    Project::Item item  = _project->createRepositoryItem();
    readRepositoryItem( values, item );
    _project->setItem(item);
  }
}

void SingleProjectReader::readWorkingCopySubItems( const ConfigValues&
  valuesIn, Project::Item& item )
{
  // note: the sub item numbers are not used as the ProjectSubItem ids.
  for( int cnt = 0; cnt < MaxSubItems; cnt++ )
  {
    QString qpat = QString("project.\\d+.wc.\\d+.subitem.%1.").arg(cnt);
    sc::String pat(qpat.utf8());

    ConfigValues values;
    filterConfigValues( pat, valuesIn, values );

    if( values.size() == 0 )
    {
      if( ! item.hasSubItems() )
      {
        Project::SubItem subItem = item.createSubItem();
        subItem.setPath(item.getSource());
        item.setSubItem(subItem);
      }
      return;
    }

    Project::SubItem subItem = item.createSubItem();
    readWorkingCopySubItem( values, subItem );
    item.setSubItem(subItem);
  }
}

void SingleProjectReader::readRepositoryItem( const ConfigValues&
  values, Project::Item& item )
{
  QRegExp reName      ( "\\S+\\.name$" );
  QRegExp reUrl       ( "\\S+\\.url$" );
  QRegExp reSort      ( "\\S+\\.sortpos$" );
  QRegExp reRecursive ( "\\S+\\.recursive$" );
  QRegExp reRevision  ( "\\S+\\.revision$" );
  QRegExp reViewHeader( "\\S+\\.viewheader$" );

  for( ConfigValues::const_iterator it = values.begin(); it != 
    values.end(); it++ )
  {
    const ConfigValue& cval = *it;

    QString ckey = QString::fromUtf8(cval.getKey());

    if( reName.exactMatch(ckey) )
      item.setName(cval.getStringValue());

    else if( reUrl.exactMatch(ckey) )
      item.setSource(cval.getStringValue());

    else if( reSort.exactMatch(ckey) )
      item.setSortPos(cval.getNumericValue());

    else if( reRecursive.exactMatch(ckey) )
      item.setRecursive(cval.getBoolValue());

    else if( reRevision.exactMatch(ckey) )
    {
      //\todo parse revision
      //item.setRevision(cval.getStringValue());
      //trunk.setRevision(
      //  svn::RevisionPtr(new svn::Revision(svn::RevHead)) );
    }
    else if( reViewHeader.exactMatch(ckey) )
      item.setViewHeader(cval.getStringValue());
  }
}

void SingleProjectReader::readWorkingCopyItem( const ConfigValues&
  values, Project::Item& item )
{
  QRegExp reName        ( "\\S+\\.wc\\.\\d+\\.name$" );
  QRegExp rePath        ( "\\S+\\.wc\\.\\d+\\.path$" );
  QRegExp reSort        ( "\\S+\\.wc\\.\\d+\\.sortpos$" );
  QRegExp reRecursive   ( "\\S+\\.wc\\.\\d+\\.recursive$" );
  QRegExp reUpdate      ( "\\S+\\.wc\\.\\d+\\.update$" );
  QRegExp reAutoUpdate  ( "\\S+\\.wc\\.\\d+\\.autoupdate$" );
  QRegExp reAutoRefresh ( "\\S+\\.wc\\.\\d+\\.autorefresh$" );
  QRegExp reViewHeader  ( "\\S+\\.wc\\.\\d+\\.viewheader$" );

  for( ConfigValues::const_iterator it = values.begin(); it != 
    values.end(); it++ )
  {
    const ConfigValue& cval = *it;
    QString ckey = QString::fromUtf8(cval.getKey());

    if( reName.exactMatch(ckey) )
      item.setName(cval.getStringValue());

    else if( rePath.exactMatch(ckey) )
      item.setSource(cval.getStringValue());

    else if( reSort.exactMatch(ckey) )
      item.setSortPos(cval.getNumericValue());

    else if( reRecursive.exactMatch(ckey) )
      item.setRecursive(cval.getBoolValue());

    else if( reUpdate.exactMatch(ckey) )
      item.setUpdate(cval.getBoolValue());

    else if( reAutoUpdate.exactMatch(ckey) )
      item.setAutoUpdate(cval.getBoolValue());

    else if( reAutoRefresh.exactMatch(ckey) )
      item.setAutoRefresh(cval.getBoolValue());

    else if( reViewHeader.exactMatch(ckey) )
      item.setViewHeader(cval.getStringValue());
  }
}

void SingleProjectReader::readWorkingCopySubItem( const ConfigValues&
  values, Project::SubItem& item )
{
  QRegExp rePath( "\\S+\\.subitem\\.\\d+\\.path$" );
  QRegExp reType( "\\S+\\.subitem\\.\\d+\\.type$" );

  for( ConfigValues::const_iterator it = values.begin(); it != 
    values.end(); it++ )
  {
    const ConfigValue& cval = *it;
    QString ckey = QString::fromUtf8(cval.getKey());

    if( rePath.exactMatch(ckey) )
    {
      item.setPath(cval.getStringValue());
    }
    else if( reType.exactMatch(ckey) )
    {
      if( cval.getStringValue() == "flat" )
        item.setType(Project::SubItem_Flat);
      else
        item.setType(Project::SubItem_Tree);
    }
  }
}

Project* SingleProjectReader::getProject()
{
  return _project;
}

void SingleProjectReader::filterConfigValues( const sc::String& pattern,
  ConfigValues& out )
{
  ConfigData::getValues( pattern, _values, out, true );
}

void SingleProjectReader::filterConfigValues( const sc::String& pattern,
  const ConfigValues& in, ConfigValues& out )
{
  ConfigData::getValues( pattern, in, out, true );
}
