//==============================================
//  copyright            : (C) 2003-2005 by Will Stokes
//==============================================
//  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.
//==============================================

//Systemwide includes
#include <qlayout.h>
#include <qlabel.h>
#include <qfont.h>
#include <qframe.h>
#include <qsize.h>
#include <qtoolbutton.h>
#include <qimage.h>
#include <qcursor.h>
#include <qapplication.h>
#include <math.h>
#include <qtooltip.h>
#include <qhgroupbox.h>
#include <qpushbutton.h>
#include <qcombobox.h>
#include <math.h>

//Projectwide includes
#include "../clickableLabel.h"
#include "editingInterface.h"
#include "selectionInterface.h"
#include "histogramEditor.h"
#include "grainEditor.h"
#include "manipulations/mosaicOptionsDialog.h"
#include "../statusWidget.h"
#include "../layoutWidget.h"
#include "../window.h"
#include "../titleWidget.h"
#include "../dialogs/questionDialog.h"
#include "../../config.h"
#include "../../backend/album.h"
#include "../../backend/subalbum.h"
#include "../../backend/photo.h"
#include "../../backend/tools/fileTools.h"
#include "../../backend/tools/imageTools.h"

#include "../../backend/enhancements/color.h"
#include "../../backend/enhancements/contrast.h"
#include "../../backend/enhancements/redEye.h"
#include "../../backend/enhancements/tilt.h"

#include "../../backend/manipulations/manipulationOptions.h"
#include "../../backend/manipulations/blackWhite.h"
#include "../../backend/manipulations/crop.h"
#include "../../backend/manipulations/emboss.h"
#include "../../backend/manipulations/invert.h"
#include "../../backend/manipulations/mosaic.h"
#include "../../backend/manipulations/painting.h"
#include "../../backend/manipulations/pointillism.h"
#include "../../backend/manipulations/sepia.h"

#include "../../configuration/configuration.h"

#define EFFECT_PREVIEW_WIDTH 107
#define EFFECT_PREVIEW_HEIGHT 80

#include <iostream>
using namespace std;

//==============================================
EditingInterface::EditingInterface(QWidget *parent, const char* name ) 
                                 : QWidget(parent,name)
{
  //create a smaller font for drawing various labels and items
  QFont smallerFont = font();
  smallerFont.setPointSize( smallerFont.pointSize() - 1 );  

  setFocusPolicy(QWidget::StrongFocus);

  //set photo pointer to null by default
  photo = NULL;

  //store layout pointer
  layout = (LayoutWidget*)parent;

  //----------  
  //Construct photo frame that houses photo being edited and prev and next buttons
  QFrame* photoFrame = new QFrame(this, "photoFrame" );
  
  //Construct the frame that houses all the controls
  QFrame* controlsFrame = new QFrame(this, "controlsFrame");

  //Place photo fram and control widgets in a top level grid
  QGridLayout* mainGrid = new QGridLayout( this, 3, 3, 0 );
  mainGrid->addWidget( photoFrame, 0, 1 );
  mainGrid->setRowStretch(0, 1);
  mainGrid->addMultiCellWidget( controlsFrame, 1,1, 0,2 );
  mainGrid->setRowSpacing( 2, WIDGET_SPACING );
  //----------  
  //Previous photo button
  previousButton = new ClickableLabel( photoFrame, "previousButton" );
  previousButton->setPixmap( QPixmap(QString(IMAGE_PATH)+"buttonIcons/previous.png") ); 
  connect( previousButton, SIGNAL(clicked()), SLOT(showPrevPhoto()) );    

  //Create widget for displaying and selecting regions of the current photo
  selectionInterface = new SelectionInterface( photoFrame, "selectionInterface" );
  connect( selectionInterface, SIGNAL( selectionChanged() ), this, SLOT( handleSelectionChanged() ) );
  connect( selectionInterface, SIGNAL( aspectRatioChanged() ), this, SLOT( handleAspectRatioChanged() ) );
  connect( selectionInterface, SIGNAL( ctrlClick() ), this, SLOT( rotateSelection() ) );
  
  //Next photo button
  nextButton = new ClickableLabel( photoFrame, "nextButton" );
  nextButton->setPixmap( QPixmap(QString(IMAGE_PATH)+"buttonIcons/next.png") ); 
  connect( nextButton, SIGNAL(clicked()), SLOT(showNextPhoto()) );    

  //Place above widgets in grid, allow seletion interface to take up extra room
  QGridLayout* selectionGrid = new QGridLayout( photoFrame, 1, 5, 0 );
  selectionGrid->setColSpacing( 0, WIDGET_SPACING );
  selectionGrid->addWidget( previousButton,     0, 1, Qt::AlignCenter );
  selectionGrid->addWidget( selectionInterface, 0, 2 );
  selectionGrid->setColStretch( 2, 1 );
  selectionGrid->addWidget( nextButton,         0, 3, Qt::AlignCenter );
  selectionGrid->setColSpacing( 4, WIDGET_SPACING );
  selectionGrid->setSpacing( WIDGET_SPACING );
  //-----------
  //construct the frames each set of controls is placed in
  QHGroupBox* frameControls   = new QHGroupBox( tr("Frame"),   controlsFrame, "frameControls"   );
  frameControls->setAlignment( Qt::AlignHCenter );
  frameControls->setInsideMargin( WIDGET_SPACING );

  QHGroupBox* enhanceControls = new QHGroupBox( tr("Enhance"), controlsFrame, "enhanceControls" );
  enhanceControls->setAlignment( Qt::AlignHCenter ); 
  enhanceControls->setInsideMargin( WIDGET_SPACING );
  
  QHGroupBox* manipulateControls = new QHGroupBox( tr("Manipulate"), controlsFrame, "applyEffect" );
  manipulateControls->setAlignment( Qt::AlignHCenter );
  manipulateControls->setInsideMargin( WIDGET_SPACING );
      
  //layout groups of controls
  QGridLayout* controlsGrid = new QGridLayout( controlsFrame, 1, 5, 0 );
  controlsGrid->addWidget( frameControls,      0, 1 );  
  controlsGrid->addWidget( enhanceControls,    0, 2 );  
  controlsGrid->addWidget( manipulateControls, 0, 3 );  
  
  controlsGrid->setSpacing( WIDGET_SPACING );    
  controlsGrid->setColSpacing(0, WIDGET_SPACING );
  controlsGrid->setColStretch(0, 1);
  controlsGrid->setColSpacing(4, WIDGET_SPACING );
  controlsGrid->setColStretch(4, 1);
  
  //----------   
  //Frame Controls
  //----------    
  QFrame* frameControlsFrame = new QFrame( frameControls );
 
  //-----
  //rotate and flip buttons
  QFrame* rotateFlipFrame = new QFrame( frameControlsFrame );
  
  QToolButton* rotateRightButton = new QToolButton( rotateFlipFrame, "rotateRight" );
  rotateRightButton->setIconSet( QPixmap(QString(IMAGE_PATH)+"buttonIcons/rotate90.png") );
  rotateRightButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
  connect( rotateRightButton, SIGNAL(clicked()), SLOT(rotateRight()) );
  QToolTip::add( rotateRightButton, tr("Rotate clockwise") );  
  
  QToolButton* rotateLeftButton = new QToolButton( rotateFlipFrame, "rotateLeft" );
  rotateLeftButton->setIconSet( QPixmap(QString(IMAGE_PATH)+"buttonIcons/rotate270.png") ); 
  rotateLeftButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
  connect( rotateLeftButton, SIGNAL(clicked()), SLOT(rotateLeft()) );
  QToolTip::add( rotateLeftButton, tr("Rotate counterclockwise") );  

  QToolButton* flipHorizontalButton = new QToolButton( rotateFlipFrame, "flipHorizontal" );
  flipHorizontalButton->setIconSet( QPixmap(QString(IMAGE_PATH)+"buttonIcons/flipHorizontally.png") );
  flipHorizontalButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
  connect( flipHorizontalButton, SIGNAL(clicked()), SLOT(flipHorizontal()) );
  QToolTip::add( flipHorizontalButton, tr("Flip horizontally") );  
  
  QToolButton* flipVerticalButton = new QToolButton( rotateFlipFrame, "flipVertical" );
  flipVerticalButton->setIconSet( QPixmap(QString(IMAGE_PATH)+"buttonIcons/flipVertically.png") ); 
  flipVerticalButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
  connect( flipVerticalButton, SIGNAL(clicked()), SLOT(flipVertical()) );  
  QToolTip::add( flipVerticalButton, tr("Flip vertically") );  
  
  correctTiltButton = new QToolButton( rotateFlipFrame, "correctTilt" );
  correctTiltButton->setIconSet( QPixmap(QString(IMAGE_PATH)+"buttonIcons/correctTilt.png") ); 
  correctTiltButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
  
  connect( correctTiltButton, SIGNAL(clicked()), SLOT(startCorrectTilt()) );

  connect( selectionInterface, SIGNAL(lineSelected(QPoint, QPoint)),
           this, SLOT(finishCorrectTilt( QPoint, QPoint)) );

  QToolTip::add( correctTiltButton, tr("Correct tilt") );  

  //Place buttons in grid
  QGridLayout* rotateFlipGrid = new QGridLayout( rotateFlipFrame, 1, 5, 0 );
  rotateFlipGrid->setSpacing(TIGHT_WIDGET_SPACING);  
  rotateFlipGrid->addWidget( rotateRightButton,    0, 0 );
  rotateFlipGrid->addWidget( rotateLeftButton,     0, 1 );
  rotateFlipGrid->addWidget( flipHorizontalButton, 0, 2 );
  rotateFlipGrid->addWidget( flipVerticalButton,   0, 3 );
  rotateFlipGrid->addWidget( correctTiltButton,    0, 4 );
  //-----
  //aspect ratio selection and crop controls
  aspectRatios = new QComboBox( frameControlsFrame );
  aspectRatios->setFont( smallerFont );
  connect( aspectRatios, SIGNAL(activated(int)), this, SLOT(selectAspectRatio()) );  
  aspectRatioValues = new QSize[8];
  maxDimensions = new double[8];
  int curAspectRatio = 0;
  //--
  aspectRatios->insertItem( tr("Custom") );
  maxDimensions[curAspectRatio] = -1.0;
  aspectRatioValues[curAspectRatio++] = QSize( -1, -1 );
  //--  
  aspectRatios->insertItem( tr("Photo (3.5 x 5)") );
  maxDimensions[curAspectRatio] = 5.0;
  aspectRatioValues[curAspectRatio++] = QSize( 10, 7 );
  //--  
  aspectRatios->insertItem( tr("Photo (4 x 6)") );
  maxDimensions[curAspectRatio] = 6.0;
  aspectRatioValues[curAspectRatio++] = QSize( 6, 4 );
  //--  
  aspectRatios->insertItem( tr("Photo (5 x 7)") );
  maxDimensions[curAspectRatio] = 7.0;
  aspectRatioValues[curAspectRatio++] = QSize( 7, 5 );
  //--  
  aspectRatios->insertItem( tr("Photo (8 x 10)") );
  maxDimensions[curAspectRatio] = 10.0;
  aspectRatioValues[curAspectRatio++] = QSize( 10, 8 );
  //--  
  aspectRatios->insertItem( tr("Postcard") );
  maxDimensions[curAspectRatio] = 6.0;
  aspectRatioValues[curAspectRatio++] = QSize( 6, 4 );
  //--  
  aspectRatios->insertItem( tr("Wallet") );
  maxDimensions[curAspectRatio] = 3.0;
  aspectRatioValues[curAspectRatio++] = QSize( 3, 2 );
  //--  
  aspectRatios->insertItem( tr("Desktop") );
  displayResolutionIndex = curAspectRatio;
  maxDimensions[curAspectRatio] = -1.0;
  aspectRatioValues[curAspectRatio++] = qApp->desktop()->screenGeometry().size();
  //--  
  //connect signal emitted when screen resolution changes 
  //so as to update this stored value (and selction if necessary)
  connect( qApp->desktop(), SIGNAL( resized(int)), this, SLOT(screenResolutionChanged()) );

  QToolTip::add( aspectRatios, tr("Select region for cropping using a particular aspect ratio") );  

  QLabel* aspectRatioLabel = new QLabel( tr("Aspect Ratio"), frameControlsFrame );
  aspectRatioLabel->setFont( smallerFont );
  //--

  //Crop button
  cropButton = new QToolButton( frameControlsFrame );
  QIconSet cropIcon;
  cropIcon.setPixmap( QString(IMAGE_PATH)+"buttonIcons/crop.png",
                      QIconSet::Automatic,
                      QIconSet::Normal );
                       
  cropIcon.setPixmap( QString(IMAGE_PATH)+"buttonIcons/crop_disabled.png",
                      QIconSet::Automatic,
                      QIconSet::Disabled );
  cropButton->setIconSet( cropIcon );
  connect( cropButton, SIGNAL(clicked()), SLOT(crop()) );
  QToolTip::add( cropButton, tr("Crop photo to selected region") );  

  QLabel* cropLabel = new QLabel( tr("Crop"), frameControlsFrame );
  cropLabel->setFont( smallerFont );  
  
  //--  

  //Place frame controls in a grid
  QGridLayout* frameControlsGrid = new QGridLayout( frameControlsFrame, 3, 2, 0 );
  frameControlsGrid->setSpacing(TIGHT_WIDGET_SPACING);  
  frameControlsGrid->addMultiCellWidget( rotateFlipFrame, 0,0,  0,1 );
  frameControlsGrid->addWidget( aspectRatios,     1, 0, Qt::AlignHCenter );  
  frameControlsGrid->addWidget( cropButton,       1, 1, Qt::AlignHCenter );  
  frameControlsGrid->addWidget( aspectRatioLabel, 2, 0, Qt::AlignHCenter );  
  frameControlsGrid->addWidget( cropLabel,        2, 1, Qt::AlignHCenter );    

  //----------  
  //Enhance Controls
  //----------    
  QFrame* enhanceControlsFrame = new QFrame( enhanceControls );  
  
  //setup params for large buttons  
  int numLargeButtons = 3;
  int curButtonIndex=0;
  ClickableLabel** largeButtons = new ClickableLabel*[numLargeButtons];
  

  //--------------------
  //Frame for semi-automatic enhance controls
  QFrame* autoEnhanceControlsFrame = new QFrame( enhanceControlsFrame );  

  //Enhance Color
  ClickableLabel* enhanceColorButton = largeButtons[curButtonIndex] = 
    new ClickableLabel( autoEnhanceControlsFrame );
  largeButtons[curButtonIndex]->setPixmap( QPixmap(QString(IMAGE_PATH)+"buttonIcons/improveColorBalance.png") );
  connect( largeButtons[curButtonIndex], SIGNAL(clicked()), SLOT(colorBalance()) );
  QToolTip::add( largeButtons[curButtonIndex], tr("Enhance color balance") );  
  curButtonIndex++;

  QLabel* enhanceColorLabel = new QLabel( tr("Color"), autoEnhanceControlsFrame );
  enhanceColorLabel->setFont( smallerFont );  
  
  //Enhance Contrast
  ClickableLabel* enhanceContrastButton = largeButtons[curButtonIndex] = 
    new ClickableLabel( autoEnhanceControlsFrame );
  largeButtons[curButtonIndex]->setPixmap( QPixmap(QString(IMAGE_PATH)+"buttonIcons/enhanceContrast.png") );
  connect( largeButtons[curButtonIndex], SIGNAL(clicked()), SLOT(enhanceContrast()) );
  QToolTip::add( largeButtons[curButtonIndex], tr("Enhance contrast") );  
  curButtonIndex++;
  
  QLabel* enhanceContrastLabel = new QLabel( tr("Contrast"), autoEnhanceControlsFrame );
  enhanceContrastLabel->setFont( smallerFont );  

  //Remove Red-Eye  
  redEyeReductionButton = largeButtons[curButtonIndex] = new ClickableLabel( autoEnhanceControlsFrame );
  redEyeReductionButton->setEnabled( false );
  
  largeButtons[curButtonIndex]->setPixmap( QPixmap( QString(IMAGE_PATH)+"buttonIcons/redEyeReduction.png" ) );
  connect( largeButtons[curButtonIndex], SIGNAL(clicked()), SLOT(removeRedeye()) );
  QToolTip::add( largeButtons[curButtonIndex], tr("Remove red-eye") );  
  curButtonIndex++;

  QLabel* removeRedyEyeLabel = new QLabel( tr("Red Eye"), autoEnhanceControlsFrame );
  removeRedyEyeLabel->setFont( smallerFont );  

  //Place semi-automatic enhance controls in grid
  QGridLayout* autoEnhanceControlsGrid = new QGridLayout( autoEnhanceControlsFrame, 2, 3, 0 );
  autoEnhanceControlsGrid->setSpacing(TIGHT_WIDGET_SPACING);  
  autoEnhanceControlsGrid->addWidget( enhanceColorButton,    0, 0, Qt::AlignHCenter );
  autoEnhanceControlsGrid->addWidget( enhanceColorLabel,     1, 0, Qt::AlignHCenter );  

  autoEnhanceControlsGrid->addWidget( enhanceContrastButton, 0, 1, Qt::AlignHCenter );
  autoEnhanceControlsGrid->addWidget( enhanceContrastLabel,  1, 1, Qt::AlignHCenter );  

  autoEnhanceControlsGrid->addWidget( redEyeReductionButton, 0, 2, Qt::AlignHCenter );
  autoEnhanceControlsGrid->addWidget( removeRedyEyeLabel,    1, 2, Qt::AlignHCenter );  
  //--------------------
  //Frame for more labor intensive enhance controls
  QFrame* manualEnhanceControlsFrame = new QFrame( enhanceControlsFrame );  
  
  //Tune Levels Button
  ClickableLabel* tuneLevelsButton = new ClickableLabel( manualEnhanceControlsFrame );
  tuneLevelsButton->setPixmap( QPixmap(QString(IMAGE_PATH)+"buttonIcons/tuneLevels.png") );
  connect( tuneLevelsButton, SIGNAL(clicked()), SLOT(tuneLevels()) );
  QToolTip::add( tuneLevelsButton, tr("Fine tune brightness, contrast, and colors") );  
  
  QLabel* tuneLevelsLabel = new QLabel( tr("Levels..."), manualEnhanceControlsFrame );
  tuneLevelsLabel->setFont( smallerFont );  

  //Adjust Grain Button
  ClickableLabel* adjustGrainButton = new ClickableLabel( manualEnhanceControlsFrame );
  adjustGrainButton->setPixmap( QPixmap(QString(IMAGE_PATH)+"buttonIcons/adjustGrain.png") );
  connect( adjustGrainButton, SIGNAL(clicked()), SLOT(adjustGrain()) );
  QToolTip::add( adjustGrainButton, tr("Blur or sharpen image") );  

  QLabel* adjustGrainLabel = new QLabel( tr("Grain..."), manualEnhanceControlsFrame );
  adjustGrainLabel->setFont( smallerFont );  

  //Place manual enhance controls in grid
  QGridLayout* manualEnhanceControlsGrid = new QGridLayout( manualEnhanceControlsFrame, 2, 3, 0 );
  manualEnhanceControlsGrid->setSpacing(TIGHT_WIDGET_SPACING);  
  manualEnhanceControlsGrid->addWidget( tuneLevelsButton,    0, 0, Qt::AlignHCenter );
  manualEnhanceControlsGrid->addWidget( tuneLevelsLabel,     1, 0, Qt::AlignHCenter );
  manualEnhanceControlsGrid->setColSpacing( 1, WIDGET_SPACING );
  manualEnhanceControlsGrid->addWidget( adjustGrainButton, 0, 2, Qt::AlignHCenter );
  manualEnhanceControlsGrid->addWidget( adjustGrainLabel,  1, 2, Qt::AlignHCenter );
  //--------------------

  //Place enhance controls in a grid
  QGridLayout* enhanceControlsGrid = new QGridLayout( enhanceControlsFrame, 4, 2, 0 );
  enhanceControlsGrid->setSpacing(WIDGET_SPACING);  
  enhanceControlsGrid->addWidget( autoEnhanceControlsFrame,   0, 0, Qt::AlignHCenter );
  enhanceControlsGrid->addWidget( manualEnhanceControlsFrame, 1, 0, Qt::AlignHCenter );
  enhanceControlsGrid->setRowStretch( 0, 1 );
  enhanceControlsGrid->setRowStretch( 3, 1 );
  
  //----------  
  //Effects Controls
  //----------  
  QFrame* manipulateControlsFrame = new QFrame( manipulateControls, "manipulateControlsFrame" );
  //--  
  //Effects
  effectsList = new QComboBox( manipulateControlsFrame );
  effectsList->setFont( smallerFont );  
  connect( effectsList, SIGNAL(activated(int)), this, SLOT(selectEffect()) );

  int i;
  for(i=0; i<NUM_MANIPULATIONS; i++)
  {
    switch(i)
    {
      case BW_EFFECT:          effectsList->insertItem( tr("B + W") );       break;
      case SEPIA_EFFECT:       effectsList->insertItem( tr("Sepia") );       break;
      case INVERT_EFFECT:      effectsList->insertItem( tr("Invert") );      break;
      case EMBOSS_EFFECT:      effectsList->insertItem( tr("Emboss") );      break;
      case MOSAIC_EFFECT:      effectsList->insertItem( tr("Mosaic") );       break;
      case PAINTING_EFFECT:    effectsList->insertItem( tr("Painting") );    break;
      case POINTILLISM_EFFECT: effectsList->insertItem( tr("Pointillism") ); break;
    }
  }

  //Apply effect button
  applyEffectButton = new QPushButton( tr("Apply"), manipulateControlsFrame );
  applyEffectButton->setFont( smallerFont );  
  connect( applyEffectButton, SIGNAL(clicked()), SLOT(applyEffect()) );   
  
  //preview of seleted effect
  effectPreview = new QLabel( manipulateControlsFrame );
 
  //Place effects controls in a grid
  QGridLayout* manipulateControlsGrid = new QGridLayout( manipulateControlsFrame, 2, 2, 0 );
  manipulateControlsGrid->setSpacing(TIGHT_WIDGET_SPACING);  
  manipulateControlsGrid->addWidget( effectsList, 0, 0 );
  manipulateControlsGrid->addWidget( applyEffectButton, 1, 0, Qt::AlignHCenter );  
  manipulateControlsGrid->addMultiCellWidget( effectPreview, 0,1, 1,1, Qt::AlignHCenter );  

  //make sure preview image always requires EFFECT_PREVIEW_WIDTH width so contorls don't
  //move around when rotating photos
  manipulateControlsGrid->setColSpacing(1, 85 );
}
//==============================================
EditingInterface::~EditingInterface() { }
//==============================================
Photo* EditingInterface::getPhoto() { return photo; }
//==============================================
void EditingInterface::setPhoto(Subalbum* collection, Photo* photo)
{
  //store photo and collection object handles
  this->collection = collection;
  this->photo = photo;

  //update visibility of prev and next buttons
  previousButton->setInvisible( photo->getPrev() == NULL );
  nextButton->setInvisible( photo->getNext() == NULL );
  
  //reset combo menu's back to first entries
  aspectRatios->setCurrentItem(0);
  effectsList->setCurrentItem(0);

  //reset selectionRotated bool to false
  selectionRotated = false;
  
  //update view of photo
  selectionInterface->setPhoto( photo->getImageFilename() );  
 
  //created effect preview image  
  effectPreviewImageFilename = ((Window*)qApp->mainWidget())->getTitle()->getAlbum()->getTmpDir() + 
                               "/effectPreviewImage.jpg";
  scaleImage( photo->getImageFilename(), effectPreviewImageFilename, EFFECT_PREVIEW_WIDTH, EFFECT_PREVIEW_HEIGHT );
  selectEffect();
  
  //get full size photo dimensions
  getImageSize( photo->getImageFilename(), imageWidth, imageHeight );
  
  //get display size photo dimensions
  selectionInterface->getDisplaySize( displayWidth, displayHeight );

  //disable the crop and reset buttons
  cropButton->setEnabled( false );
  redEyeReductionButton->setEnabled( false );  
}
//==============================================
void EditingInterface::showPrevPhoto()
{ 
  Photo* prevPhoto = photo->getPrev();

  if( prevPhoto != NULL && 
      prevPhoto != photo )
  { showNextPrevFirstLastPhoto( prevPhoto ); }
}
//==============================================
void EditingInterface::showNextPhoto()
{ 
  Photo* nextPhoto = photo->getNext();

  if( nextPhoto != NULL &&
      nextPhoto != photo )
  { showNextPrevFirstLastPhoto( nextPhoto ); }
}  
//==============================================
void EditingInterface::showFirstPhoto()
{ 
  Photo* firstPhoto = collection->getFirst();

  if(firstPhoto != photo)
  { showNextPrevFirstLastPhoto( firstPhoto ); }
}
//==============================================
void EditingInterface::showLastPhoto()
{ 
  Photo* lastPhoto = collection->getLast();
  
  if(lastPhoto != photo)
  { showNextPrevFirstLastPhoto( lastPhoto ); }
}
//==============================================
void EditingInterface::showNextPrevFirstLastPhoto( Photo* newPhoto )
{  
  //set busy pointer
  qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
  qApp->processEvents();

  //reset photo object handle
  photo = newPhoto;

  //reset selectionRotated bool to false
  selectionRotated = false;
  
  //update visibility of prev and next buttons
  previousButton->setInvisible( photo->getPrev() == NULL );
  nextButton->setInvisible( photo->getNext() == NULL );
    
  //update view of photo
  selectionInterface->setPhoto( photo->getImageFilename() );  

  //created effect preview image  
  effectPreviewImageFilename = ((Window*)qApp->mainWidget())->getTitle()->getAlbum()->getTmpDir() +     "/effectPreviewImage.jpg";
  scaleImage( photo->getImageFilename(), effectPreviewImageFilename, EFFECT_PREVIEW_WIDTH, EFFECT_PREVIEW_HEIGHT );
  selectEffect();

  //get full size photo dimensions
  getImageSize( photo->getImageFilename(), imageWidth, imageHeight );

  //get display size photo dimensions
  selectionInterface->getDisplaySize( displayWidth, displayHeight );

  //auto select region if custom not selected, else select nothing
  if(aspectRatios->currentItem() != 0)
  { selectAspectRatio(); }
  else  
  { selectionInterface->setSelection( QPoint(-1,-1), QPoint(-1, -1) ); }
  
  //emit signal that photo state possibly has changed
  emit photoModified();
  
  //remove busy cursor
  qApp->restoreOverrideCursor();
  qApp->processEvents();
}
//==============================================
void EditingInterface::rotateRight()
{
  rotateFlip( ROTATE_90 );
}
//==============================================
void EditingInterface::rotateLeft()
{
  rotateFlip( ROTATE_270 );
}
//==============================================
void EditingInterface::flipHorizontal()
{
  rotateFlip( FLIP_H );
}
//==============================================
void EditingInterface::flipVertical()
{
  rotateFlip( FLIP_V );
}
//==============================================
void EditingInterface::rotateFlip( TRANSFORM_CODE rotationFlipType )
{
  //set busy pointer
  qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
  qApp->processEvents();
  
  //disable user input
  layout->getWindow()->getStatus()->grabInput();
  
  //rotate image, bail if rotation fails
  QString editedImagePath = ((Window*)qApp->mainWidget())->getTitle()->getAlbum()->getTmpDir() + "/editedImage.jpg";
  transformImage( photo->getImageFilename(), editedImagePath, rotationFlipType );

  //apply changes to photo
  photo->setImage( editedImagePath );
  
  //Reload photo view
  bool aspectRatioChanged = ( rotationFlipType == ROTATE_90 || rotationFlipType == ROTATE_270 );  
  selectionInterface->setPhoto( editedImagePath, aspectRatioChanged );  
  
  //update image dimension variables
  getImageSize( photo->getImageFilename(), imageWidth, imageHeight );  
  
  //get display size photo dimensions
  selectionInterface->getDisplaySize( displayWidth, displayHeight );
  
  //reapply selected aspect ratio selection if aspect ratio changed
  if( aspectRatioChanged )
  {
    //reset selectionRotated bool to false
    selectionRotated = false;
    selectAspectRatio();      
  }
  
  //update effect preview
  scaleImage( photo->getImageFilename(), effectPreviewImageFilename, EFFECT_PREVIEW_WIDTH, EFFECT_PREVIEW_HEIGHT );
  selectEffect();
  
  //emit modified signal
  emit photoModified();
  
  //enable user input
  layout->getWindow()->getStatus()->releaseInput();

  //remove busy cursor
  qApp->restoreOverrideCursor();
  qApp->processEvents();
}
//==============================================
void EditingInterface::screenResolutionChanged()
{
  //reset display resolution
  aspectRatioValues[displayResolutionIndex] = qApp->desktop()->screenGeometry().size();

  //if user is currently constraining the current selection then reset to fit new display resolution
  if(aspectRatios->currentItem() == displayResolutionIndex )
  { selectAspectRatio(); }
}
//==============================================
void EditingInterface::crop()
{
  //find selection, if empty bail!
  QPoint topLeft, bottomRight;
  if (!findSelection(topLeft, bottomRight) )
    return;

  //set busy cursor
  qApp->setOverrideCursor( QCursor(Qt::WaitCursor));

  //disable user input
  layout->getWindow()->getStatus()->grabInput();

  //crop image
  applyImageUpdate( cropImage( photo->getImageFilename(), topLeft, bottomRight ),
                    true );

  //enable user input
  layout->getWindow()->getStatus()->releaseInput();

  //remove busy cursor
  qApp->restoreOverrideCursor();
}
//==============================================
void EditingInterface::enhanceContrast()
{
  //set busy cursor
  qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
  qApp->processEvents();

  //disable user input
  layout->getWindow()->getStatus()->grabInput();

  //enhance image
  applyImageUpdate( enhanceImageContrast( photo->getImageFilename(),
                                          layout->getWindow()->getStatus() ),
                    false );

  //enable user input
  layout->getWindow()->getStatus()->releaseInput();

  //remove busy cursor
  qApp->restoreOverrideCursor();
  qApp->processEvents();
}
//==============================================
void EditingInterface::colorBalance()
{
  //set busy cursor
  qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
  qApp->processEvents();

  //disable user input
  layout->getWindow()->getStatus()->grabInput();

  //improve color balance
  applyImageUpdate( improveColorBalance( photo->getImageFilename(),
                                         layout->getWindow()->getStatus() ),
                    false );

  //enable user input
  layout->getWindow()->getStatus()->releaseInput();

  //remove busy cursor
  qApp->restoreOverrideCursor();
  qApp->processEvents();
}
//==============================================
void EditingInterface::removeRedeye()
{
  //find selection, if empty bail!
  QPoint topLeft, bottomRight;
  if (!findSelection(topLeft, bottomRight) )
    return;

  //set busy cursor
  qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
  qApp->processEvents();
  
  //disable user input
  layout->getWindow()->getStatus()->grabInput();

  //remove redeye image
  applyImageUpdate( removeRedeyeRegions( photo->getImageFilename(), 
                                         topLeft, bottomRight,
                                         layout->getWindow()->getStatus() ),
                    true );

  //enable user input
  layout->getWindow()->getStatus()->releaseInput();

  //remove busy cursor
  qApp->restoreOverrideCursor();
  qApp->processEvents();
}
//==============================================
void EditingInterface::tuneLevels()
{
  //load photo in histogram editor
  //if changes took place update image
  HistogramEditor editor( photo->getImageFilename(), this);
  if( editor.exec() ) 
  { 
    //set busy cursor
    qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
    qApp->processEvents();

    //disable user input
    layout->getWindow()->getStatus()->grabInput();

    //update image    
    applyImageUpdate( editor.getModifiedImage(), false ); 

    //enable user input
    layout->getWindow()->getStatus()->releaseInput();

    //remove busy cursor
    qApp->restoreOverrideCursor();
    qApp->processEvents();
  }
}
//==============================================
void EditingInterface::adjustGrain()
{
  //load photo in grain editor
  //if changes took place update image
  GrainEditor editor( photo->getImageFilename(), this);
  if( editor.exec() ) 
  { 
    //set busy cursor
    qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
    qApp->processEvents();
    
    //disable user input
    layout->getWindow()->getStatus()->grabInput();

    //update image    
    applyImageUpdate( editor.getModifiedImage(), false ); 
    
    //enable user input
    layout->getWindow()->getStatus()->releaseInput();

    //remove busy cursor
    qApp->restoreOverrideCursor();
    qApp->processEvents();
  }
}
//==============================================
void EditingInterface::selectEffect()
{  
  //apply effect on preview image
  QImage* editedImage = applyEffect( effectPreviewImageFilename );
  
  //bail if generated preview image failed
  if( editedImage == NULL ) return;
  
  //refresh displayed preview image
  effectPreview->setPixmap( QPixmap(*editedImage) );
  delete editedImage;
  editedImage = NULL;  
}
//==============================================
void EditingInterface::applyEffect()
{  
  //--------------------------------
  //Get any manipulation options if needed
  ManipulationOptions* options = NULL;
  if( effectsList->currentItem() == MOSAIC_EFFECT )   
  {
    MosaicOptionsDialog optionsDialog(this);
    //user accepted so get selected options
    if( optionsDialog.exec() ) 
    { 
      //constructing the tiles list can unfortunately be quite slow so show a preparing tiles message
      //and busy icon while getting the options chosen
      layout->getWindow()->getStatus()->showProgressBar( QString(tr("Preparing Tiles")), 100 );
      qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
      qApp->processEvents();       
      options = optionsDialog.getOptions(); 
      qApp->restoreOverrideCursor();
    }
    //user hit cancel so bail
    else 
    { return; }
  }
  else
  { options = new ManipulationOptions( layout->getWindow()->getStatus() ); }
  //--------------------------------
  //Disable user input
  layout->getWindow()->getStatus()->grabInput();
  applyEffectButton->setEnabled(false);
  qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
  qApp->processEvents();
  //--------------------------------
  //Apply effect
  QImage* editedImage = applyEffect( photo->getImageFilename(), options );
  applyImageUpdate( editedImage, false );
  delete options; options = NULL;
  //--------------------------------
  //Remove status bar if present and reenable user input
  layout->getWindow()->getStatus()->setStatus( "" );
  layout->getWindow()->getStatus()->releaseInput();
  applyEffectButton->setEnabled(true);
  qApp->restoreOverrideCursor();
  qApp->processEvents();
  //--------------------------------
} 
//==============================================
QImage* EditingInterface::applyEffect(QString filename, ManipulationOptions* options)
{
  //apply effect
  QImage* effectedImage = NULL;
  switch( effectsList->currentItem() )
  {
    case BW_EFFECT:          effectedImage = blackWhiteEffect(  filename, options ); break;
    case SEPIA_EFFECT:       effectedImage = sepiaEffect(       filename, options ); break;
    case INVERT_EFFECT:      effectedImage = invertEffect(      filename, options ); break;
    case EMBOSS_EFFECT:      effectedImage = embossEffect(      filename, options ); break;
    case PAINTING_EFFECT:    effectedImage = oilPaintingEffect( filename, options ); break;
    case POINTILLISM_EFFECT: effectedImage = pointillismEffect( filename, options ); break;
    case MOSAIC_EFFECT:      effectedImage = mosaicEffect(      filename, (MosaicOptions*) options ); break;
  }

  //return effected image
  return effectedImage;
}
//==============================================
void EditingInterface::applyImageUpdate(QImage* editedImage, bool resetSelection)
{  
  //skip apply step if pointer is null. this usually means
  //no modifications were made (for example: no red eyes were detected)
  if(editedImage == NULL) 
  { 
    //sometimes the user instructs the program to modify an image in a way
    //where no real changes are actually necessary. case in point, red eye reduction
    //on a region where there is no red stuff at all! in order to be consistant, if
    //the user is expecting the selection to be reset then always reset it! this
    //normally occurs below when resetting the photo but we'll do it here 
    //since resetting the photo will not take place
    if(resetSelection) 
    { 
      selectionInterface->selectNone(); 
    }
    
    return; 
  }
  
  //construct edited image path
  QString editedImagePath = ((Window*)qApp->mainWidget())->getTitle()->getAlbum()->getTmpDir() + "/editedImage.jpg";

  //save edited image to temporary location
  //TODO: EXIF information is lost at this point, Qt does not support
  //storing exif information, but perhaps a 2nd pass can be made on the file
  //where exif info is added using libjpeg functions?
  editedImage->save( editedImagePath, "JPEG", 95 );
  delete editedImage;
  editedImage = NULL;
    
  //apply changes to photo
  photo->setImage( editedImagePath );
  
  //Reload photo view
  selectionInterface->setPhoto( editedImagePath, resetSelection );  

  //If we're resetting the selection (due to cropping), reset the 
  //selection rotated bit as well
  if( resetSelection ) { selectionRotated = false; }
  
  //update image dimension variables
  getImageSize( photo->getImageFilename(), imageWidth, imageHeight );  

  //get display size photo dimensions
  selectionInterface->getDisplaySize( displayWidth, displayHeight );

  //update effect preview
  scaleImage( photo->getImageFilename(), effectPreviewImageFilename, EFFECT_PREVIEW_WIDTH, EFFECT_PREVIEW_HEIGHT );
  selectEffect();
  
  //emit modified signal
  emit photoModified();
}
//==============================================
void EditingInterface::returnAction()
{
  //exit edit mode
  layout->organize();
}
//==============================================
bool EditingInterface::findSelection(QPoint& topLeft, QPoint& bottomRight)
{
  //get raw selection in display coordinates
  selectionInterface->getSelection(topLeft, bottomRight);

  //if range is empty then retrun false
  if(topLeft.x() >= bottomRight.x() ||
     topLeft.y() >= bottomRight.y())
    return false;
  
  //return success
  return true;
}
//==============================================
void EditingInterface::handleSelectionChanged()
{
  //crop button is enabled only when a portion of the image is selected
  QPoint topLeft, bottomRight;
  bool selectionPresent = findSelection(topLeft,bottomRight);     

  cropButton->setEnabled( selectionPresent );
  redEyeReductionButton->setEnabled( selectionPresent );  
}
//==============================================
void EditingInterface::handleAspectRatioChanged()
{
  //change aspect ratio combo box to custom
  aspectRatios->setCurrentItem(0);  
}
//==============================================
void EditingInterface::selectAll(QPoint& topLeft, QPoint& bottomRight)
{
  topLeft.setX(0); 
  topLeft.setY(0);
  bottomRight.setX(imageWidth - 1); 
  bottomRight.setY(imageHeight - 1);     
}
//==============================================
void EditingInterface::keyPressEvent( QKeyEvent *e )
{
  //next handle additional keys
  switch( e->key() )
  {
    //apply changes and exit
    case Qt::Key_Escape:
      returnAction();
      break;
    case Qt::Key_Prior:
      showPrevPhoto();
      break;
    case Qt::Key_Next:
      showNextPhoto();
      break;
    case Qt::Key_Home:
      showFirstPhoto();
      break;
    case Qt::Key_End:
      showLastPhoto();
      break;
    case Qt::Key_R:
      if(e->state() & Qt::ControlButton)
        rotateRight();
      break;
    case Qt::Key_L:
      if(e->state() & Qt::ControlButton)
        rotateLeft();
      break;
    case Qt::Key_F:
      if(e->state() & Qt::ControlButton)
      {
        if( e->state() & Qt::AltButton )
          flipVertical();
        else
          flipHorizontal();
      }
      break;
    default:
      e->ignore();
  }
}
//==============================================
bool EditingInterface::currentPhotoRevertable()
{
  if(photo == NULL) 
    return false;
  else
    return photo->revertPossible();
}
//==============================================
void EditingInterface::revertCurrentPhoto()
{
  //if current photo not revertable immediately bail
  if( ! currentPhotoRevertable() ) return;
  
  //set busy cursor
  qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
  
  //disable user input
  layout->getWindow()->getStatus()->grabInput();

  //get current and reverted image sizes and compare to see if size has changed.
  //if so reset selected region
  int origWidth, origHeight;
  getImageSize( photo->originalImageFilename(), origWidth, origHeight );  
  bool resetSelection = (origWidth != imageWidth) || (origHeight != imageHeight);
  
  ///Revert photo
  photo->revertPhoto();

  //Reload photo view
  selectionInterface->setPhoto( photo->getImageFilename(), resetSelection );  
  
  //update image dimension variables
  getImageSize( photo->getImageFilename(), imageWidth, imageHeight );  
  
  //get display size photo dimensions
  selectionInterface->getDisplaySize( displayWidth, displayHeight );
  
  //update effect preview
  scaleImage( photo->getImageFilename(), effectPreviewImageFilename, EFFECT_PREVIEW_WIDTH, EFFECT_PREVIEW_HEIGHT );
  selectEffect();  
  
  //emit modified signal
  emit photoModified();

  //enable user input
  layout->getWindow()->getStatus()->releaseInput();

  //remove busy cursor
  qApp->restoreOverrideCursor();
}
//==============================================
void EditingInterface::setFocus()
{
  //always pass off focus even to selection interface so it can get key strokes
  selectionInterface->setFocus();
}
//==============================================
void EditingInterface::rotateSelection()
{
  //invert rotate bool
  selectionRotated = !selectionRotated;
  
  //rotate custom selection in place, scale/shift if necessary to keep on image
  if(aspectRatios->currentItem() == 0)
  {
    //get cur selection
    QPoint curTopLeft, curBottomRight;
    selectionInterface->getSelection(curTopLeft, curBottomRight); 

    //find center
    QPoint selectionCenter = QPoint( ( curTopLeft.x() + curBottomRight.x() ) / 2,
                                     ( curTopLeft.y() + curBottomRight.y() ) / 2 );    

    //scale rotated width/height to fit image
    int newWidth = curBottomRight.y() - curTopLeft.y() + 1;
    int newHeight =curBottomRight.x() - curTopLeft.x() + 1;
    calcScaledImageDimensions( newWidth, newHeight,
                               imageWidth, imageHeight,
                               newWidth, newHeight );

    //center new selection over old selection
    QPoint topLeft = QPoint( selectionCenter.x() - newWidth/2,
                             selectionCenter.y() - newHeight/2 );
    QPoint bottomRight = QPoint( topLeft.x() + newWidth - 1,
                                 topLeft.y() + newHeight - 1 );

    //shift selection area to not go outside image boundary
    if(topLeft.x() < 0)
    {
      bottomRight.setX( bottomRight.x() - topLeft.x() );
      topLeft.setX( 0 );
    }

    if(topLeft.y() < 0)
    {
      bottomRight.setY( bottomRight.y() - topLeft.y() );
      topLeft.setY( 0 );
    }
    
    if(bottomRight.x() >= imageWidth )
    {
      topLeft.setX( topLeft.x() - ( bottomRight.x() - imageWidth + 1 ) );
      bottomRight.setX( imageWidth - 1 );
    }

    if(bottomRight.y() >= imageHeight )
    {
      topLeft.setY( topLeft.y() - ( bottomRight.y() - imageHeight + 1 ) );
      bottomRight.setY( imageHeight - 1 );
    }
    
    //select new region
    selectionInterface->setSelection(topLeft, bottomRight);    
  }
  //else call selectAspectRatio passing true that we're
  //using the rotated version of the current aspect ratio
  else
  { 
    selectAspectRatio(); 
  }
}
//==============================================
void EditingInterface::selectAspectRatio()
{
  //if user selected "custom" don't modify current selection
  if( aspectRatios->currentItem() == 0 ) return;
  
  //get default aspect ratio
  QSize aspectRatio = aspectRatioValues[ aspectRatios->currentItem() ];  

  //automatically rotate default if current photo is taller than wide
  if( imageHeight > imageWidth )
  { aspectRatio = QSize( aspectRatio.height(), aspectRatio.width() ); }
  
  //exchange aspect ratio dimensions if we're in rotated selection mode
  if( selectionRotated )
  { aspectRatio = QSize( aspectRatio.height(), aspectRatio.width() ); }
    
  //determine selected width and height;
  int selectedWidth = 0; 
  int selectedHeight = 0;
  
  //display resolution, match exactly or scale down
  if(aspectRatios->currentItem() == displayResolutionIndex)
  {    
    //select region less than or equal to display resolution while maintaining aspect ratio
    selectedWidth = aspectRatio.width();
    selectedHeight = aspectRatio.height();
    calcScaledImageDimensions( selectedWidth, selectedHeight,
                               imageWidth, imageHeight,
                               selectedWidth, selectedHeight );
  }
  //else use aspect ratio directly as a ratio
  else
  {  
    //select region using same aspect ratio
    selectedWidth = imageWidth;
    selectedHeight = (int) (((double) (imageWidth * aspectRatio.height()) ) / aspectRatio.width() );
    calcScaledImageDimensions( selectedWidth, selectedHeight,
                               imageWidth, imageHeight,
                               selectedWidth, selectedHeight );
    
  }
  
  //get current selection boundary
  QPoint curTopLeft, curBottomRight;
  selectionInterface->getSelection( curTopLeft, curBottomRight );
  
  //get current selection center
  QPoint curCenter;
  curCenter.setX( (curTopLeft.x() + curBottomRight.x()) / 2 );
  curCenter.setY( (curTopLeft.y() + curBottomRight.y()) / 2 );

  //if center is off image (no previous selection) then
  //fix center to center of image
  if( curCenter.x() < 0 || curCenter.y() < 0 )
  {
    curCenter.setX( imageWidth/2 );
    curCenter.setY( imageHeight/2 );
  }
  
  //attempt to center new selection overold selection, only
  //offset if necessary
  QPoint newTopLeft, newBottomRight;

  newTopLeft.setX( curCenter.x() - selectedWidth/2 );
  newTopLeft.setY( curCenter.y() - selectedHeight/2 );

  //push right/down if necessary
  if( newTopLeft.x() < 0 ) newTopLeft.setX( 0 );
  if( newTopLeft.y() < 0 ) newTopLeft.setY( 0 );
  
  //push left/up if necessary
  newBottomRight.setX( newTopLeft.x() + selectedWidth - 1 );
  if( newBottomRight.x() >= imageWidth )
  {
    newBottomRight.setX( imageWidth-1 );
    newTopLeft.setX( newBottomRight.x() - selectedWidth + 1 );
  }
  
  newBottomRight.setY( newTopLeft.y() + selectedHeight - 1 );
  if( newBottomRight.y() >= imageHeight )
  {
    newBottomRight.setY( imageHeight-1 );
    newTopLeft.setY( newBottomRight.y() - selectedHeight + 1 );
  }

  //select region
  selectionInterface->setSelection(newTopLeft, newBottomRight,
                                   maxDimensions[aspectRatios->currentItem()] );    
} 
//==============================================
void EditingInterface::startCorrectTilt()
{
  //instruct user to select a horizontal or vertical line in the image by
  //beginning draw line mode in the selection interface
  correctTiltButton->setEnabled( false );
  selectionInterface->enterDrawLineMode();
}
//==============================================
void EditingInterface::finishCorrectTilt( QPoint p1, QPoint p2 )
{
  //if either point is invalid ignore action
  if( p1.x() == -1 || p2.x() == -1 )
  {
    //reenable tilt button
    correctTiltButton->setEnabled( true );
    return;
  }
  
  //set busy cursor
  qApp->setOverrideCursor( QCursor(Qt::WaitCursor));
  
  //disable user input
  layout->getWindow()->getStatus()->grabInput();
  
  //rotate image by determining correct angle from two points
  QImage* rotatedImage = correctImageTilt( photo->getImageFilename(), p1, p2,
                                           layout->getWindow()->getStatus() );
  applyImageUpdate( rotatedImage, true );
  
  //reenable tilt button
  correctTiltButton->setEnabled( true );

  //enable user input
  layout->getWindow()->getStatus()->releaseInput();

  //remove busy cursor
  qApp->restoreOverrideCursor();
}
//==============================================


