/* ====================================================================
 * 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 "RevisionWidget.h"
#include "sublib/ExternButton.h"
#include "sublib/Utility.h"
#include "svn/Revision.h"

// qt
#include <QtGui/QStackedWidget>
#include <QtGui/QDateTimeEdit>
#include <QtGui/QToolButton>
#include <QtGui/QTabWidget>
#include <QtGui/QCheckBox>
#include <QtGui/QComboBox>
#include <QtGui/QGroupBox>
#include <QtGui/QSpinBox>
#include <QtGui/QLayout>
#include <QtGui/QLabel>
#include <QtGui/QMenu>

// apr
#include <apr_time.h>



/** symbolic revision strings. */
const QString workingRev     = _q("working");
const QString headRev        = _q("head");
const QString baseRev        = _q("base");
const QString committedRev   = _q("committed");
const QString previousRev    = _q("previous");
const QString unspecifiedRev = _q("unspecified");

/** the different revision type pages. */
const int noneId    = -1;  // in case the widget is disabled
const int symRevId  = 0;
const int numRevId  = 1;
const int dateRevId = 2;


RevisionWidget::RevisionWidget( bool allowNoRevision, const QString& types,
  const QString& symbols, RevisionProvider* prov, QWidget *parent )
  : super( parent, "RevisionWidget" ), _symRev(0), _numRev(0), _dateRev(0),
  _revpro(prov)
{
  QVBoxLayout* vl = new QVBoxLayout(this);
  vl->setContentsMargins(0,0,0,0);
  vl->setSpacing(0);
  {
    _title = new QLabel(this);
    _title->setText( _q("revision:") );
    vl->addWidget(_title);

    QHBoxLayout* hl = new QHBoxLayout();
    hl->setContentsMargins(0,0,0,0);
    vl->addLayout(hl);
    {
      _disable = new QCheckBox(this);
      hl->addWidget(_disable);
      
      _revBox = new QHBoxLayout(hl);
      _revBox->setContentsMargins(0,0,0,0);
      _revBox->setSpacing(5);
      {
        _menu = new QMenu(this);
        
        _menuButton = new QToolButton(this);
#ifdef Q_WS_MAC
        _menuButton->setStyleSheet( "border:none; padding-top: -5px;" );
#else // ! Q_WS_MAC
        _menuButton->setStyleSheet( "border:none;" );
#endif // Q_WS_MAC
        
        //_menuButton->setArrowType(Qt::UpArrow);
        _menuButton->setPopupMode(QToolButton::InstantPopup);
        _menuButton->setAutoRaise(true);
        _menuButton->setMenu(_menu);
        _revBox->addWidget(_menuButton);
        
        _stack = new QStackedWidget(this);
        for( int c = 0; c < types.length(); c++ )
        {
          addType( types.at(c) );
        }
        
        for( int c = 0; c < symbols.length(); c++ )
        {
          addSymbol( symbols.at(c) );
        }
        _revBox->addWidget(_stack);
        
        _extern = new ExternButton(this);
        _revBox->addWidget(_extern);
        
        if( ! _revpro )
        {
          _extern->setHidden(true);
        }
      }
    }
    _title->setBuddy(_stack);
  }
  
  connect( _menu, SIGNAL(activated(int)), _stack, SLOT(setCurrentIndex(int)));
  connect( _menu, SIGNAL(activated(int)), this,   SLOT(signalRev(int)) );


  if( ! allowNoRevision )
  {
    _disable->setChecked(true);
    _disable->hide();
  }
  else
  {
    toggled(false);
    connect( _disable, SIGNAL(toggled(bool)), SLOT(toggled(bool)) );
  }

  setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
}

RevisionWidget::~RevisionWidget()
{
}

void RevisionWidget::toggled( bool b )
{
  _menuButton->setEnabled(b);
  _stack->setEnabled(b);
  signalRev();
}

void RevisionWidget::addType( const QChar& c )
{
  switch( c.ascii() )
  {
  case 'S':
    {
      _symRev = new QComboBox(_stack);

      _stack->insertWidget(symRevId,_symRev);
      _menu->insertItem( _q("Symbolic Revision"), symRevId );

      connect( _symRev, SIGNAL(activated(int)), this, SLOT(signalRev(int)) );
      break;
    }
  case 'N':
    {
      // todo right align
      _numRev = new QSpinBox(_stack);
      _numRev->setMaxValue( 100000000 );
#ifdef Q_WS_MAC
      _numRev->setStyleSheet("padding: 1px 1px; padding-right: 18px");
#endif // Q_WS_MAC
      
      _stack->insertWidget(numRevId,_numRev);
      _menu->insertItem( _q("Numeric Revision"), numRevId );
      
      connect( _numRev, SIGNAL(valueChanged(int)), this, SLOT(signalRev(int)) );
      break;
    }
  case 'D':
    {
      _dateRev = new QDateTimeEdit( QDate::currentDate(), _stack );
      _dateRev->setDisplayFormat( getDateFormat() );
      _dateRev->setCalendarPopup(true);
      _dateRev->setAlignment(Qt::AlignVCenter);
#ifdef Q_WS_MAC
      _dateRev->setStyleSheet("padding: 1px 1px; padding-right: 18px");
#endif // Q_WS_MAC

      _stack->insertWidget(dateRevId,_dateRev);
      _menu->insertItem( _q("Date Revision"), dateRevId );

      connect( _dateRev, SIGNAL(dateTimeChanged(const QDateTime&)), this, SLOT(signalRev(const QDateTime&)) );
      break;
    }
  }
}

void RevisionWidget::addSymbol( const QChar& c )
{
  if( ! _symRev )
    return;

  switch( c.ascii() )
  {
  case 'H':
    {
      _symRev->insertItem( headRev );
      break;
    }
  case 'B':
    {
      _symRev->insertItem( baseRev );
      break;
    }
  case 'C':
    {
      _symRev->insertItem( committedRev );
      break;
    }
  case 'P':
    {
      _symRev->insertItem( previousRev );
      break;
    }
  case 'W':
    {
      _symRev->insertItem( workingRev );
      break;
    }
  case 'U':
    {
      _symRev->insertItem( unspecifiedRev );
      break;
    }
  }
}


/** Get the selected revision. It is the callers responsibility to delete the object. */
svn::Revision* RevisionWidget::getRevision() const
{
  int index = _stack->currentIndex();

  if( isEnabled() == false || ! _disable->isChecked() )
  {
    index = noneId; 
  }

  switch( index )
  {
  case symRevId:
    {
      QString text = _symRev->currentText();

      if( text == headRev )
      {
        return new svn::Revision(svn::Revision_Head);
      }
      else if( text == baseRev )
      {
        return new svn::Revision(svn::Revision_Base);
      }
      else if( text == previousRev )
      {
        return new svn::Revision(svn::Revision_Previous);
      }
      else if( text == committedRev )
      {
        return new svn::Revision(svn::Revision_Committed);
      }
      else if( text == workingRev )
      {
        return new svn::Revision(svn::Revision_Working);
      }
      else
      {
        return new svn::Revision(svn::Revision_Unspecified);
      }
      break;
    }
  case numRevId:
    {
      return new svn::RevisionNumber( _numRev->value() );
      break;
    }
  case dateRevId:
    {
      // surprisingly easy... :)
      // both times are relative to the 1 January, 1970 UTC, 00:00:00
      // the only difference is time resolution:
      // apr uses microseconds and qt uses seconds
      svn::Date svndate = apr_time_from_sec( _dateRev->dateTime().toTime_t() );

      return new svn::RevisionDate(svndate);
    }
  default:
    {
      return new svn::Revision(svn::Revision_Unspecified);
    }
  }
  return NULL;
}

void RevisionWidget::setRevision( const svn::Revision* rev )
{
  // _stack must be set first so it is properly set before the
  // revChanged signal is emitted in the specific set calls.

  switch( rev->getKind() )
  {
  case svn::Revision_Unspecified:
    {
      _stack->setCurrentIndex(symRevId);
      _symRev->setCurrentText(unspecifiedRev);
      break;
    }
  case svn::Revision_Date:
    {
      _stack->setCurrentIndex(dateRevId);
      svn::Date date = ((const svn::RevisionDate*)rev)->getDate();
      QDateTime qdate;
      qdate.setTime_t( apr_time_sec(date) );
      _dateRev->setDateTime(qdate);
      break;
    }
  case svn::Revision_Number:
    {
      _stack->setCurrentIndex(numRevId);
      svn::Revnumber revnum = ((const svn::RevisionNumber*)rev)->getNumber();
      _numRev->setValue(revnum);
      _disable->setChecked(true);
      break;
    }
  case svn::Revision_Working:
    {
      _stack->setCurrentIndex(symRevId);
      _symRev->setCurrentText(workingRev);
      break;
    }
  case svn::Revision_Head:
    {
      _stack->setCurrentIndex(symRevId);
      _symRev->setCurrentText(headRev);
      break;
    }
  case svn::Revision_Base:
    {
      _stack->setCurrentIndex(symRevId);
      _symRev->setCurrentText(baseRev);
      break;
    }
  case svn::Revision_Committed:
    {
      _stack->setCurrentIndex(symRevId);
      _symRev->setCurrentText(committedRev);
      break;
    }
  case svn::Revision_Previous:
    {
      _stack->setCurrentIndex(symRevId);
      _symRev->setCurrentText(previousRev);
      break;
    }
  }
}

void RevisionWidget::setRevision( svn::RevisionPtr rev )
{
  setRevision(rev.get());
}

void RevisionWidget::setTitle( const QString& title )
{
  _title->setText(title);
}

void RevisionWidget::signalRev()
{
  emit revChanged( svn::RevisionPtr(getRevision()) );
}

void RevisionWidget::signalRev(int)
{
  signalRev();
}

void RevisionWidget::signalRev(bool)
{
  signalRev();
}

void RevisionWidget::signalRev(const QDateTime&)
{
  signalRev();
}
