/* ====================================================================
 * Copyright (c) 2003-2007, 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 "DiffDialog.h"
#include "RevisionWidget.h"
#include "ExternProvider.h"
#include "DiffViewModel.h"
#include "DiffSummarizeLvi.h"
#include "TextWindow.h"
#include "Settings.h"
#include "events/EventSupport.h"
#include "events/ScParamEvent.h"
#include "sublib/Gui.h"
#include "sublib/ExternButton.h"
#include "sublib/settings/LayoutSettings.h"
#include "svn/DiffSummarize.h"

// qt
#include <QtGui/QLayout>
#include <QtGui/QPushButton>
#include <QtGui/QLabel>
#include <QtGui/QLineEdit>
#include <QtGui/QComboBox>
#include <QtGui/QFileDialog>
#include <QtGui/QToolTip>
#include <QtGui/QCheckBox>
#include <QtGui/QRadioButton>
#include <Qt3Support/Q3ListView>
#include <Qt3Support/Q3GroupBox>
#include <Qt3Support/Q3HBox>

// sys
#include <assert.h>


DiffDialog::DiffDialog( DiffViewModel* model, ExternProvider* p, QWidget *parent )
: super(parent,0), TargetId(this), _model(model), _p(p)
{
  setWindowFlags( windowFlags() | Qt::WDestructiveClose );
  setName( "DiffDialog" );
  setCaption( _q("subcommander:diff") );

  QVBoxLayout *vbl = new QVBoxLayout(this,5,8);
  vbl->setSpacing(10);
  {
    Q3GroupBox* gb = new Q3GroupBox(1,Qt::Vertical,this);
    gb->setTitle( _q("diff options: ") );
    gb->setInsideMargin(0);
    gb->setFlat(true);
    vbl->addWidget(gb);

    QGridLayout* gl = new QGridLayout(vbl,4,4);
    gl->setMargin(0);
    gl->setColStretch( 0, 0 );
    gl->setColStretch( 1, 1 );
    gl->setColStretch( 2, 3 );
    gl->setColStretch( 3, 0 );
    {
      {
        _rep1L = new QLabel(this);
        _rep1  = new QComboBox(this);
        _rep1B = new ExternButton(this);

        _rep1L->setBuddy(_rep1);
        _rep1L->setText( _q("&target:") );
        _rep1->setEditable(true);
        _rep1->setAutoCompletion(true);

        gl->addWidget( _rep1L, 1, 1 );
        gl->addWidget( _rep1,  1, 2 ); 
        gl->addWidget( _rep1B, 1, 3 ); 

        connect( _rep1, SIGNAL(activated(const QString&)),
          SLOT(checkButtons(const QString&)) );
        connect( _rep1, SIGNAL(highlighted(const QString&)),
          SLOT(checkButtons(const QString&)) );
        connect( _rep1, SIGNAL(textChanged(const QString&)),
          SLOT(checkButtons(const QString&)) );
        connect( _rep1, SIGNAL(textChanged(const QString&)),
          SLOT(textChanged1(const QString&)) );

        connect( _rep1B, SIGNAL(clicked()), SLOT(selectRep1Url()) );

        QToolTip::add( _rep1, _q("target for a diff between two revisions") );
      }
      {
        _type  = new QCheckBox(this);
        _rep2L = new QLabel(this);
        _rep2  = new QComboBox(this);
        _rep2B = new ExternButton(this);

        _rep2L->setBuddy(_rep2);
        _rep2L->setText( _q("&new target:") );
        _rep2->setEditable(true);
        _rep2->setAutoCompletion(true);

        gl->addWidget( _type,  2, 0 );
        gl->addWidget( _rep2L, 2, 1 );
        gl->addWidget( _rep2,  2, 2 ); 
        gl->addWidget( _rep2B, 2, 3 ); 

        connect( _type, SIGNAL(toggled(bool)), SLOT(changedDiffType(bool)) );
        connect( _rep2, SIGNAL(activated(const QString&)),
          SLOT(checkButtons(const QString&)) );
        connect( _rep2, SIGNAL(highlighted(const QString&)),
          SLOT(checkButtons(const QString&)) );
        connect( _rep2, SIGNAL(textChanged(const QString&)),
          SLOT(checkButtons(const QString&)) );
        connect( _rep2, SIGNAL(textChanged(const QString&)),
          SLOT(textChanged2(const QString&)) );

        connect( _rep2B, SIGNAL(clicked()), SLOT(selectRep2Url()) );

        QToolTip::add( _rep2, _q("new target for a diff between two paths") );

        _rep2L->setEnabled(false);
        _rep2->setEnabled(false);
        _rep2B->setEnabled(false);
      }

      QHBoxLayout* h0 = new QHBoxLayout;
      gl->addMultiCellLayout( h0, 3, 3, 0, 3 );
      {
        _rwPeg = new RevisionWidget(true,"NDS","HBCP",0,this);
        _rwPeg->setTitle( _q("target peg revision:") );
        h0->addWidget(_rwPeg);

        QToolTip::add( _rwPeg, _q("the targets name was target in this revision") );

        connect( 
          _rwPeg, SIGNAL(revChanged(svn::RevisionPtr)),
          _model, SLOT(setRevisionPeg(svn::RevisionPtr)));
      }

      QHBoxLayout* h1 = new QHBoxLayout;
      gl->addMultiCellLayout( h1, 4, 4, 0, 3 );
      {
        _rw1 = new RevisionWidget(false,"SDN","HBWCP",0,this);
        _rw1->setTitle( _q("target from revision:") );
        h1->addWidget(_rw1);

        _rw2 = new RevisionWidget(false,"SDN","HBWCP",0,this);
        _rw2->setTitle( _q("target to revision:") );
        h1->addWidget(_rw2);

        connect( 
          _rw1, SIGNAL(revChanged(svn::RevisionPtr)),
          _model, SLOT(setRevision1(svn::RevisionPtr)));
        connect( 
          _rw2, SIGNAL(revChanged(svn::RevisionPtr)),
          _model, SLOT(setRevision2(svn::RevisionPtr)));
      }
    }

    QHBoxLayout* h1 = new QHBoxLayout;
    vbl->addLayout(h1);
    {
      _recurse = new QCheckBox(_q("&recursive"),this);
      _recurse->setChecked(_model->getRecursive());
      h1->addWidget(_recurse);

      _ancestry = new QCheckBox(_q("&ancestry"),this);
      _ancestry->setChecked(true);
      h1->addWidget(_ancestry);

      _deleted = new QCheckBox(_q("&show deleted"),this);
      _deleted->setChecked(true);
      h1->addWidget(_deleted);

      connect( _recurse, SIGNAL(toggled(bool)), _model, SLOT(setRecursive(bool)) );
      connect( _ancestry, SIGNAL(toggled(bool)), _model, SLOT(setCopies(bool)) );
      connect( _deleted, SIGNAL(toggled(bool)), _model, SLOT(setDeletes(bool)) );
    }

    _summarize = new  Q3ListView(this);
    _summarize->setItemMargin( 2 );
    _summarize->setShowToolTips( true );
    _summarize->setAllColumnsShowFocus(true);
    _summarize->addColumn( _q("text") );
    _summarize->addColumn( _q("prop") );
    _summarize->addColumn( _q("type") );
    _summarize->addColumn( _q("file/folder") );
    _summarize->setColumnAlignment( 0, Qt::AlignCenter );
    _summarize->setColumnAlignment( 1, Qt::AlignCenter );
    _summarize->setColumnAlignment( 2, Qt::AlignCenter );
    _summarize->setColumnAlignment( 3, Qt::AlignLeft );
    _summarize->setSortColumn( 3 );
    _summarize->setResizeMode(Q3ListView::LastColumn);
    _summarize->setSelectionMode(Q3ListView::Extended);
    _summarize->setHidden(!_model->getDir());
    vbl->addWidget(_summarize);

    connect( _summarize, SIGNAL(selectionChanged()), SLOT(enableButtons()) );
    connect( _summarize, SIGNAL(selectionChanged()), SLOT(updateSelection()) );
    connect( _summarize, SIGNAL(doubleClicked(Q3ListViewItem*,const QPoint&, int)),
      SLOT(doubleClicked(Q3ListViewItem*,const QPoint&, int)) );

    QHBoxLayout* hu = new QHBoxLayout;
    vbl->addLayout(hu);
    {
      // eats extra space, so the buttons keep their size
      hu->addStretch(1); 

      _diff = new QPushButton(this);
      _diff->setText( _q("&Diff") );
      hu->addWidget(_diff);

      _patch = new QPushButton(this);
      _patch->setText( _q("P&atch") );
      _patch->setDefault(true);
      hu->addWidget(_patch);

      _sum = new QPushButton(this);
      _sum->setText( _q("&Summarize") );
      _sum->setHidden(!_model->getDir());
      hu->addWidget(_sum);

      _close = new QPushButton(this);
      _close->setText( _q("&Close") );
      hu->addWidget(_close);

      hu->addSpacing(getSizeGripSpacing());

      connect( _diff, SIGNAL(clicked()), SLOT(diff()) );
      connect( _patch, SIGNAL(clicked()), SLOT(patch()));
      connect( _sum, SIGNAL(clicked()), SLOT(sum()));
      connect( _close, SIGNAL(clicked()), SLOT(close()) );
    }
  }

  _rep1->insertItem( QString::fromUtf8(_model->getPathOrUrl1()), 0 );
  _rep2->insertItem( QString::fromUtf8(_model->getPathOrUrl2()), 0 );
  _rw1->setRevision( _model->getRevision1() );
  _rw2->setRevision( _model->getRevision2() );
  enablePathOrUrl1( _model->getChangePathOrUrl1() );
  enablePathOrUrl2( _model->getChangePathOrUrl2() );
  enableTypeChange( _model->getChangeType() );

  connect( 
    _model, SIGNAL(addSumEntry(svn::DiffSummarizePtr)),
    this, SLOT(addSumEntry(svn::DiffSummarizePtr)) );

  Settings s;
  resize( s.layout().getSize( name(), QSize(400,400) ) );

  if( !_model->getDir() )
  {
    // don't resize vertically.
    resize( width(), sizeHint().height() );
    setMaximumHeight( sizeHint().height() );
  }
}

DiffDialog::~DiffDialog()
{
  Settings s;
  s.layout().setSize( name(), geometry().size() );

  delete _p;
  delete _model;
}

void DiffDialog::closeEvent( QCloseEvent *e )
{
  if( _model->runs() )
  {
    return;
  }

  super::closeEvent(e);
}

void DiffDialog::setPathOrUrl1( const QString& pathOrUrl )
{
  _rep1->insertItem( pathOrUrl, 0 );
  enableButtons();
}

void DiffDialog::setPathOrUrl2( const QString& pathOrUrl )
{
  _rep2->insertItem( pathOrUrl, 0 );
  enableButtons();
}

void DiffDialog::enablePathOrUrl1( bool enable )
{
  _rep1->setEnabled(enable);
  _rep1B->setEnabled(enable);
}

void DiffDialog::enablePathOrUrl2( bool enable )
{
  _rep2L->setEnabled(enable);
  _rep2->setEnabled(enable);
  _rep2B->setEnabled(enable);
  _type->setChecked(enable);
}

void DiffDialog::enableTypeChange( bool enable )
{
  _type->setEnabled(enable);
}

void DiffDialog::setRevision1( const svn::Revision* rev )
{
  _rw1->setRevision(rev);
}

void DiffDialog::setRevision2( const svn::Revision* rev )
{
  _rw2->setRevision(rev);
}

void DiffDialog::updateSelection()
{
  svn::DiffSummarizes sums;
  getSelection(sums);
  _model->setSelection(sums);
}

void DiffDialog::enableButtons()
{
  if( _model->getDir() )
  {
    Q3ListViewItemIterator it( _summarize, Q3ListViewItemIterator::Selected );
    _diff->setEnabled(it.current());
    _patch->setEnabled(true);
    _sum->setEnabled(true);
  }
  else
  {
    _diff->setEnabled(true);
    _patch->setEnabled(true);
    _sum->setEnabled(false);
  }
}

void DiffDialog::changedDiffType( bool )
{
  if( ! _type->isChecked() )
  {
    _rep2L->setEnabled(false);
    _rep2->setEnabled(false);
    _rep2B->setEnabled(false);

    _rwPeg->setEnabled(true);
    _model->setPegDiff(true);

    _rep1L->setText( _q("&target:") );
    QToolTip::add( _rep1, _q("target for a diff between two revisions") );
    _rw1->setTitle( _q("target from revision:") );
    _rw2->setTitle( _q("target to revision:") );
  }
  else if( _type->isChecked() )
  {
    _rep2L->setEnabled(true);
    _rep2->setEnabled(true);
    _rep2B->setEnabled(true);

    _rwPeg->setEnabled(false);
    _model->setPegDiff(false);

    _rep1L->setText( _q("&old target:") );
    QToolTip::add( _rep1, _q("old target for a diff between two paths") );
    _rw1->setTitle( _q("old target revision:") );
    _rw2->setTitle( _q("new target revision:") );
  }
}

void DiffDialog::diff()
{
  // selection diff thread running?
  if( ! _model->runs() )
  {
    _model->diff();

    // started selection diff thread?
    if( _model->runs() )
    {
      _diff->setText( _q("&Stop") );

      _sum->setDisabled(true);
      _patch->setDisabled(true);
      _close->setDisabled(true);
      _summarize->setDisabled(true);
    }
  }
  else
  {
    // stop selection diff thrad
    _diff->setDisabled(true);
    _model->stop();
    _diff->setText( _q("&Diff") );

    _sum->setEnabled(true);
    _diff->setEnabled(true);
    _patch->setEnabled(true);
    _close->setEnabled(true);
    _summarize->setEnabled(true);
  }
}

void DiffDialog::patch()
{
  _model->patch();
}

void DiffDialog::sum()
{
  _summarize->clear();
  _model->summarize();
}

void DiffDialog::close()
{
  super::close();
}

void DiffDialog::doubleClicked( Q3ListViewItem* item, const QPoint& p, int c )
{
  diff();
}

void DiffDialog::textChanged1(const QString& text)
{
  _model->setPathOrUrl1( sc::String(text.utf8()) );
}

void DiffDialog::textChanged2(const QString& text)
{
  _model->setPathOrUrl2( sc::String(text.utf8()) );
}

void DiffDialog::selectRep1Url()
{
  sc::String res;

  long flags = 0;
  flags |= _model->getDir() ?  ExternProvider::Dir : ExternProvider::File;

  if( _p->selectUrl( this, sc::String(_rep1->currentText().utf8()), res, flags) )
  {
    _rep1->insertItem( QString::fromUtf8(res), 0 );
  }
}

void DiffDialog::selectRep2Url()
{
  sc::String res;

  long flags = 0;
  flags |= _model->getDir() ?  ExternProvider::Dir : ExternProvider::File;

  if( _p->selectUrl( this, sc::String(_rep2->currentText().utf8()), res, flags) )
  {
    _rep2->insertItem( QString::fromUtf8(res), 0 );
  }
}

void DiffDialog::checkButtons( const QString& /*ignored*/ )
{
  QString rep1 = _rep1->currentText();
  QString rep2 = _rep2->currentText();

  bool ok;

  if( _type->isChecked() ) 
  {
    // normal
    ok = ! rep1.isEmpty() && ! rep2.isEmpty();
  }
  else
  {
    // peg
    ok = ! rep1.isEmpty();
  }

  if( ok )
  {
    enableButtons();
  }
  else
  {
    _diff->setEnabled(false);
    _patch->setEnabled(false);
    _sum->setEnabled(false);
  }
}

void DiffDialog::addSumEntry( svn::DiffSummarizePtr sum )
{
  new DiffSummarizeLvi( _summarize, sum );
}

void DiffDialog::getSelection( svn::DiffSummarizes& entries )
{
  Q3ListViewItemIterator it( _summarize, Q3ListViewItemIterator::Selected );
  while( it.current() )
  {
    entries.push_back( ((DiffSummarizeLvi*)it.current())->getDiffSummarize() );
    ++it;
  }
}
