/***************************************************************************
    qgsundowidget.cpp
    ---------------------
    begin                : June 2009
    copyright            : (C) 2009 by Martin Dobias
    email                : wonder dot sk at gmail dot com
 ***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include "qgsundowidget.h"

#include "qgsmaplayer.h"
#include "qgsmapcanvas.h"
#include "qgslegend.h"

#include "qgisapp.h"
#include "qgsapplication.h"

QgsUndoWidget::QgsUndoWidget( QWidget * parent, QgsMapCanvas * mapCanvas )
    : QDockWidget( parent )
{
  setupUi( this );
  setWidget( dockWidgetContents );

  connect( undoButton, SIGNAL( clicked() ), this, SLOT( undo( ) ) );
  connect( redoButton, SIGNAL( clicked() ), this, SLOT( redo( ) ) );
  connect( QgisApp::instance()->legend(), SIGNAL( currentLayerChanged( QgsMapLayer* ) ),
           this, SLOT( layerChanged( QgsMapLayer* ) ) );

  undoButton->setDisabled( true );
  redoButton->setDisabled( true );
  mMapCanvas = mapCanvas;
  mUndoView = new QUndoView( dockWidgetContents );
  gridLayout->addWidget( mUndoView, 0, 0, 1, 2 );
  mUndoStack = NULL;
  mPreviousIndex = 0;
  mPreviousCount = 0;
}


void QgsUndoWidget::layerChanged( QgsMapLayer * layer )
{
  if ( layer != NULL )
  {
    setUndoStack( layer->undoStack() );
  }
  else
  {
    destroyStack();
  }
  emit undoStackChanged();
}


void QgsUndoWidget::destroyStack()
{
  if ( mUndoStack != NULL )
  {
    // do not clear undo stack here, just null pointer
    mUndoStack = NULL;
  }
  if ( mUndoView != NULL )
  {
    mUndoView->close();
    delete mUndoView;
    mUndoView = new QUndoView( dockWidgetContents );
    gridLayout->addWidget( mUndoView, 0, 0, 1, 2 );
  }
}

void QgsUndoWidget::undoChanged( bool value )
{
  undoButton->setDisabled( !value );
  emit undoStackChanged();
}

void QgsUndoWidget::redoChanged( bool value )
{
  redoButton->setDisabled( !value );
  emit undoStackChanged();
}

void QgsUndoWidget::indexChanged( int curIndx )
{
  // this is called twice when a non-current command is clicked in QUndoView
  //   first call has offset, second call will have offset of 0
  int curCount = 0;
  bool canRedo = true;
  if ( mUndoStack )
  {
    canRedo = mUndoStack->canRedo();
    curCount = mUndoStack->count();
  }
  int offset = qAbs( mPreviousIndex - curIndx );

  // when individually redoing, differentiate between last redo and a new command added to stack
  bool lastRedo = ( mPreviousIndex == ( mPreviousCount - 1 ) && mPreviousCount == curCount && !canRedo );

  if ( offset != 0 )
  {
    QgsDebugMsg( QString( "curIndx : %1" ).arg( curIndx ) );
    QgsDebugMsg( QString( "offset  : %1" ).arg( offset ) );
    QgsDebugMsg( QString( "curCount: %1" ).arg( curCount ) );
    if ( lastRedo )
    {
      QgsDebugMsg( QString( "lastRedo: true" ) );
    }
  }

  // avoid canvas redraws when only new command was added to stack (i.e. no user undo/redo action)
  // or when user has clicked back in QUndoView history then added a new command to the stack
  if ( offset > 1 || ( offset == 1 && ( canRedo || lastRedo ) ) )
  {
    if ( mMapCanvas )
    {
      QgsDebugMsg( QString( "trigger redraw" ) );
      mMapCanvas->refresh();
    }
  }

  mPreviousIndex = curIndx;
  mPreviousCount = curCount;
}

void QgsUndoWidget::undo( )
{
  if ( mUndoStack )
    mUndoStack->undo();
}

void QgsUndoWidget::redo()
{
  if ( mUndoStack )
    mUndoStack->redo();
}

void QgsUndoWidget::setUndoStack( QUndoStack* undoStack )
{
  if ( mUndoView != NULL )
  {
    mUndoView->close();
    delete mUndoView;
    mUndoView = NULL;
  }

  mUndoStack = undoStack;
  mPreviousIndex = mUndoStack->index();
  mPreviousCount = mUndoStack->count();

  mUndoView = new QUndoView( dockWidgetContents );
  mUndoView->setStack( undoStack );
  mUndoView->setObjectName( "undoView" );
  gridLayout->addWidget( mUndoView, 0, 0, 1, 2 );
  setWidget( dockWidgetContents );
  connect( mUndoStack, SIGNAL( canUndoChanged( bool ) ), this, SLOT( undoChanged( bool ) ) );
  connect( mUndoStack, SIGNAL( canRedoChanged( bool ) ), this, SLOT( redoChanged( bool ) ) );

  // gets triggered also when a new command is added to stack, and twice when clicking a command in QUndoView
  connect( mUndoStack, SIGNAL( indexChanged( int ) ), this, SLOT( indexChanged( int ) ) );

  undoButton->setDisabled( !mUndoStack->canUndo() );
  redoButton->setDisabled( !mUndoStack->canRedo() );
}



void QgsUndoWidget::setupUi( QDockWidget *UndoWidget )
{
  if ( UndoWidget->objectName().isEmpty() )
    UndoWidget->setObjectName( QString::fromUtf8( "UndoWidget" ) );
  UndoWidget->resize( 200, 223 );
  UndoWidget->setMinimumSize( QSize( 200, 220 ) );
  dockWidgetContents = new QWidget( UndoWidget );
  dockWidgetContents->setObjectName( QString::fromUtf8( "dockWidgetContents" ) );
  gridLayout = new QGridLayout( dockWidgetContents );
  gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) );
  gridLayout->setContentsMargins( 0, 0, 0, 0 );
  spacerItem = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );

  gridLayout->addItem( spacerItem, 0, 0, 1, 1 );

  undoButton = new QPushButton( dockWidgetContents );
  undoButton->setObjectName( QString::fromUtf8( "undoButton" ) );
  undoButton->setIcon( QgsApplication::getThemeIcon( "mActionUndo.png" ) );

  gridLayout->addWidget( undoButton, 1, 0, 1, 1 );

  redoButton = new QPushButton( dockWidgetContents );
  redoButton->setObjectName( QString::fromUtf8( "redoButton" ) );
  redoButton->setIcon( QgsApplication::getThemeIcon( "mActionRedo.png" ) );

  gridLayout->addWidget( redoButton, 1, 1, 1, 1 );

  spacerItem1 = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );

  gridLayout->addItem( spacerItem1, 0, 1, 1, 1 );

  UndoWidget->setWidget( dockWidgetContents );

  retranslateUi( UndoWidget );

  QMetaObject::connectSlotsByName( UndoWidget );
} // setupUi

void QgsUndoWidget::retranslateUi( QDockWidget *UndoWidget )
{
  UndoWidget->setWindowTitle( QApplication::translate( "UndoWidget", "Undo/Redo", 0, QApplication::UnicodeUTF8 ) );
  undoButton->setText( QApplication::translate( "UndoWidget", "Undo", 0, QApplication::UnicodeUTF8 ) );
  redoButton->setText( QApplication::translate( "UndoWidget", "Redo", 0, QApplication::UnicodeUTF8 ) );
  Q_UNUSED( UndoWidget );
}