//==============================================
//  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 <qpixmap.h>
#include <qpainter.h>
#include <qpopupmenu.h>
#include <qiconset.h>
#include <qapplication.h>
#include <qstringlist.h>
#include <qcursor.h>
#include <qdragobject.h>
#include <qstringlist.h>

//Projectwide includes
#include "photosIconView.h"
#include "layoutWidget.h"
#include "window.h"
#include "titleWidget.h"
#include "photoPreviewWidget.h"
#include "photoDescEdit.h"
#include "../config.h"
#include "../backend/photo.h"
#include "../backend/album.h"
#include "../configuration/configuration.h"

//==============================================
class PhotoDrag : public QUriDrag 
{
public:
  PhotoDrag( QWidget* dragSource=0, const char* name=0)
  { QUriDrag(dragSource, name); }
  
  ///force all drags to COPY (not move/delete) original files!!!
  virtual bool drag(DragMode mode)  
  { return QDragObject::drag( QDragObject::DragCopy ); }
};
//==============================================
PhotosIconView::PhotosIconView( QWidget *parent ) : QIconView( parent, "iconView", WNoAutoErase )
{
  viewport()->setBackgroundMode( Qt::NoBackground );

  currentPseudoSelection = NULL;
  handCursorShown = false;
  
  curPhotoDescEdit = NULL;

  //by default no photo has been right clicked on
  rightClickedPhoto = NULL;

  //load background image
  backgroundImage = new QPixmap( QString(IMAGE_PATH)+"miscImages/backgroundImage.png" );

  //load drag icon
  dragIcon = new QPixmap( QString(IMAGE_PATH)+"miscImages/moveImage.png" );

  //connect mouse over events to paint pseudo selection in ligher blue
  connect( this, SIGNAL(onItem(QIconViewItem*)),
                this, SLOT(repaintGroup(QIconViewItem*)) );

  //clear any pseudo selection when mouse moves off icons
  connect( this, SIGNAL(onViewport()),
                this, SLOT(clearPseudoSelection()) );

  connect( this, SIGNAL(pressed( QIconViewItem*, const QPoint& )),
                 this, SLOT(captureClick(QIconViewItem*, const QPoint&)) );
}
//==============================================
QDragObject* PhotosIconView::dragObject()
{
  //no item selected?
  if( !currentItem() )
    return 0;
  
  //create drag object
//  PhotoDrag *drag = new PhotoDrag( viewport() );
  QIconDrag *drag = new QIconDrag( viewport() );
  
  //use small icon to represent drag, does not cover up too much of the screen
  drag->setPixmap( *dragIcon );
  
  //!!!!!!!!!!!!!!!!!!!!
  //TODO find out how we can add filenames and PREVENT them from being MOVED to new location
  //like whendropping photoson the desktop!!!
  //create stringlist of all selected photo filenames
  //QStringList filenames;
  //QIconViewItem* current = firstItem();
  //while(current != NULL)
  //{
  //  if(current->isSelected())
  //  { 
  //    filenames.append( ((PhotoPreviewWidget*)current)->getPhoto()->getImageFilename() ); 
  //  }
  //  
  //  current = current->nextItem();
  //}
  //  drag->setFileNames( filenames );
  //!!!!!!!!!!!!!!!!!!!!

  return drag;
}
//==============================================
bool PhotosIconView::findNearestUnselectedPhoto( const QPoint &pos,
                                                 QIconViewItem** nearestItem,
                                                 bool &posIsleftOfItem )
{
  //if there are no items we can't find one now can we?
  if ( firstItem() == NULL )
    return false;
  
  //------------------------------------------------
  //first see if there is an unselected photo here
  QIconViewItem* item = firstItem();
  while(item != NULL)
  {
    if( !item->isSelected() && item->contains(pos) )
    {
      (*nearestItem) = item;
      posIsleftOfItem = pos.x() < item->x() + (item->width()/2);
      return true;
    }
    
    item = item->nextItem();
  }
  //------------------------------------------------
  //see if drop occurred below the last unselected photo
  
  //find last unselected photo
  item = lastItem();
  while( item != NULL && item->isSelected() )
  { item = item->prevItem(); }
    
  //is point below last unselected photo?
  if( item != NULL && pos.y() > (item->y() + item->height()) )
  {
    (*nearestItem) = item;
    posIsleftOfItem = false;
    return true;
  }
  //------------------------------------------------
  //see if drop occurred above the first unselected photo
  
  //find first unselected photo
  item = firstItem();
  while( item != NULL && item->isSelected() )
  { item = item->nextItem(); }
  
  //is point below last unselected photo?
  if( item != NULL && pos.y() < item->y() )
  {
    (*nearestItem) = item;
    posIsleftOfItem = true;
    return true;
  }
  //------------------------------------------------
  //next try checking within this row, walk left first
  int x;
  for(x = pos.x()-1; x>=0; x--)
  {
    item = findItem( QPoint(x, pos.y()) );
    if( item == NULL || item->isSelected() ) continue;
    else
    {
      (*nearestItem) = item;
      posIsleftOfItem = false;
      return true;
    }
  }
  //walking left failed, try walking right
  for(x = pos.x()+1; x<width(); x++)
  {
    item = findItem( QPoint(x, pos.y()) );
    if( item == NULL || item->isSelected() ) continue;
    else
    {
      (*nearestItem) = item;
      posIsleftOfItem = true;
      return true;
    }
  }
  //------------------------------------------------ 
  //ok, no unselected item is at this point, to the left, to the right, and the point
  //is not above the first unselected photo or below the last unselected photo. the
  //only way this is possible is if the point is between two rows of photos in the margin.

  //while it's certain there are photos both above and below, it is possible 
  //either of the adjacent rows of photos only contain selected photos so searching 
  //a single row will not necessarily find the nearest photo.
  
  //however, we are guaranteed to find an unselected photo both above and below 
  //this point, somewhere, so only searching up or down is necessary. we'll search 
  //up and the first unselected photo we find we'll use as the nearst. All selects 
  //photos must come after (aka to the right of) this photo.
  int itemWidth  = firstItem()->width();
  int itemHeight = firstItem()->height();
  int y;
  for(y = pos.y()-(itemHeight/2); y >= 0; y-=(itemHeight/2) )
  {
    for(x = width(); x >= 0; x-=(itemWidth/2))
    {
      item = findItem( QPoint(x, y) );
      if( item == NULL || item->isSelected() ) { continue; }
      else
      {
        (*nearestItem) = item;
        posIsleftOfItem = false;
        return true;
      }
    }
  }
  //------------------------------------------------ 
  //unable to find nearest unselected item
  return false;
}
//==============================================
void PhotosIconView::contentsDropEvent( QDropEvent *e )
{
  QIconView::contentsDropEvent( e );
  
  //if item is from the viewport emit item moved signal
  if(e->source() == viewport() )
  { 
    //find nearest unselected item
    QIconViewItem* item; bool leftOf;
    if( !findNearestUnselectedPhoto( e->pos(), &item, leftOf ) )
    {
      //unable to find nearest item. this should be impossible
    //  cout << "ERROR! Failed to find nearest item!\n";
      emit itemHasMoved();
    }  
    
    //selected photos dropped on LEFT of nearest item
    if( leftOf )
    {
      //count number of items being moved
      int num=0;
      QIconViewItem* current = firstItem();
      while(current != NULL)
      {
        if(current->isSelected()) { num++; }
        current = current->nextItem();
      }
    
      //move items to their new locations
      int xpos = item->x() - num;
      current = firstItem();
      while(current != NULL)
      {
        if(current->isSelected())
        {
          current->move(xpos, item->y()); 
          xpos++;
        }
        current = current->nextItem();
      }
    }
    //selected phtos dropped on RIGHT of nearest item
    else
    {
      //move items to their new locations
      int xpos = item->x() + (item->width()/2) + 1;
      QIconViewItem* current = firstItem();
      while(current != NULL)
      {
        if(current->isSelected())
        {
          current->move(xpos, item->y());
          xpos++;
        }
        current = current->nextItem();
      }
    }

    //items have moved!
    emit itemHasMoved();
  }
  //else it's from off the viewport, if we can get filename then try adding photos being added to subalbum
  else
  {
    QStringList fileNames;
    if( QUriDrag::decodeLocalFiles( e, fileNames ) )
    {
      //for some reason the order in which we receive file names is entire illogical.
      //it does not appear to be based on filename, data, modified data, size, anything!
      //thus, before adding files, first sort by ascending filename
      fileNames.sort();
      emit addPhotos(fileNames);
    }
  }
  
}
//==============================================
int PhotosIconView::numSelected()
{
  int num = 0;
  QIconViewItem* current = firstItem();
  while(current != NULL)
  {
    if(current->isSelected())
      num++;
    current = current->nextItem();
  }
  return num;
}
//==============================================
void PhotosIconView::contextMenuEvent ( QContextMenuEvent * e )
{
  rightClickedPhoto = (PhotoPreviewWidget*) findItem( QPoint(e->x(), e->y()+contentsY()) );
  if(rightClickedPhoto == NULL)
    return;

  QPopupMenu contextMenu( this );

  contextMenu.insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/setAlbumImage.png") ),
                          tr("Set album image"), this, SLOT(setAlbumImage()) );

  contextMenu.insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/setSubalbumImage.png") ),
                          tr("Set collection image"), this, SLOT(setSubalbumImage()) );

  contextMenu.exec( QPoint(e->globalX(), e->globalY()) );
}
//==============================================
void PhotosIconView::setAlbumImage( )
{
  ((Window*)qApp->mainWidget())->getTitle()->setAlbumImage( rightClickedPhoto->getPhoto() );
  rightClickedPhoto = NULL;
}
//==============================================
void PhotosIconView::setSubalbumImage( )
{
  ((Window*)qApp->mainWidget())->getTitle()->setSubalbumImage( rightClickedPhoto->getPhoto() );
  rightClickedPhoto = NULL;
}
//==============================================
void PhotosIconView::drawBackground( QPainter* p, const QRect& r)
{
  QBrush brush;
  brush.setPixmap( *backgroundImage );
  p->fillRect( r, brush );
}
//==============================================
 void PhotosIconView::drawContents ( QPainter * p, int clipx, int clipy, int clipw, int cliph )
{
    if( bufferPixmap.size() != size())
    {  bufferPixmap.resize( size() ); }
    QPainter bufferPainter( &bufferPixmap, viewport() );
    int xOffset = clipx - contentsX();
    int yOffset = clipy - contentsY();

    bufferPainter.translate( -contentsX(), -contentsY() );
    QIconView::drawContents( &bufferPainter, clipx, clipy, clipw, cliph );
    bitBlt(p->device(), xOffset, yOffset, &bufferPixmap, xOffset, yOffset, clipw, cliph );
}
//==============================================
void PhotosIconView::repaintGroup( QIconViewItem* pseudoSelection)
{
  //if old pseudo selection unselect it
  clearPseudoSelection();

  //paint new selection
  if(pseudoSelection != NULL)
  {
    currentPseudoSelection = (PhotoPreviewWidget*)pseudoSelection;
    currentPseudoSelection->setMousedOver(true);
    repaintItem(currentPseudoSelection);
  }
}
//==============================================
void PhotosIconView::clearPseudoSelection()
{
  //check to make sure mouse is still off item. when a user expands an image to edit
  //it's description, the selection is cleard (since hte mouse is over a new window). however,
  //when that window disappears repaintGroup never got recalled (if the mouse was still on the original window),
  //so just ignore the original clear (it was invalid anyways right?)
 if( findItem( viewport()->mapFromGlobal( QCursor::pos() )+=QPoint( contentsX(), contentsY() )  ) == currentPseudoSelection )
   return;

  //if old pseudo selection unselect it
  if(currentPseudoSelection != NULL)
  {
    currentPseudoSelection->setMousedOver(false);
    repaintItem(currentPseudoSelection);
    currentPseudoSelection = NULL;
  }
}
//==============================================
void PhotosIconView::captureClick(QIconViewItem *item, const QPoint& point)
{
  //if no item has been clicked then ignore
  if(item == NULL)
    return;

  //get info button rect
  QRect infoButtonRec = ((PhotoPreviewWidget*)item)->getPhotoInfoRect();

  //remove scroll offset
  infoButtonRec.moveBy( -contentsX(), -contentsY() );

  //convert to screen coordinates by shifting by offset of topleft of widget
  QPoint topLeft = mapToGlobal( QPoint(0,0) );
  infoButtonRec.moveBy( topLeft.x(), topLeft.y() );

  //if screen coordinates of mouse click are in rectangle then button pressed
  if( infoButtonRec.contains( point.x(), point.y() ) )
  {
    //make sure all events have been processed first. if were just
    //editing a differnt photo description it is first necessary to 
    //repaint before we start editing the next
    qApp->processEvents();

    if( curPhotoDescEdit != NULL ) { delete curPhotoDescEdit; }

    PhotoPreviewWidget* ppw = (PhotoPreviewWidget*)item;
    curPhotoDescEdit = new PhotoDescEdit( ppw,
        ((Window*)qApp->mainWidget())->getConfig()->getBool( "layout", "animation" ) );
  }
}
//==============================================
void PhotosIconView::contentsMousePressEvent ( QMouseEvent * e )
{
  dragStartPos = e->pos();
  QIconView::contentsMousePressEvent( e );
}
//==============================================
void PhotosIconView::contentsMouseMoveEvent( QMouseEvent *e)
{
  QIconView::contentsMouseMoveEvent( e );

  //if no item is under mouse then return
  if(currentPseudoSelection == NULL)
  {
    if(handCursorShown)
    {
      setCursor( QCursor( Qt::ArrowCursor ) );
      handCursorShown = false;
    }
    return;
  }

  QRect photoInfoRect = currentPseudoSelection->getPhotoInfoRect();

  //if hand not shown but over hover over image then turn hand cursor on
  if( !handCursorShown && photoInfoRect.contains( e->x(), e->y() ) )
  {
    setCursor( QCursor( Qt::PointingHandCursor ) );
    handCursorShown = true;
    return;
  }

  //if hand cursor shown but nolonger over hover over image set cursor back to normal
  if( handCursorShown && !photoInfoRect.contains( e->x(), e->y() ) )
  {
    setCursor( QCursor( Qt::ArrowCursor ) );
    handCursorShown = false;
    return;
  }
}
//==============================================
void PhotosIconView::keyPressEvent( QKeyEvent *e )
{
  //next handle additional keys
  switch( e->key() )
  {
    //edit selected photo
    case Qt::Key_Enter:
    case Qt::Key_Return:
      //only edit photo if one is selected
      if(numSelected() == 1)
      {
        if( curPhotoDescEdit != NULL ) { delete curPhotoDescEdit; }

        PhotoPreviewWidget* ppw = (PhotoPreviewWidget*)currentItem();
        curPhotoDescEdit = new PhotoDescEdit( ppw, ((Window*)qApp->mainWidget())->getConfig()->getBool
                                              ( "layout", "animation" ) );
      }
      break;
    //delete - remove selected photos
    case Qt::Key_Delete:
      if(numSelected() > 0) emit removeSelectedPhotos();
      break;
    //ctrl + r - rotate photo right
    case Qt::Key_R:
      if( e->state() & Qt::ControlButton && numSelected() > 0)
        emit rotate90SelectedPhotos();
      break;
    //ctrl + l - rotate photo left
    case Qt::Key_L:
      if(e->state() & Qt::ControlButton && numSelected() > 0) 
        emit rotate270SelectedPhotos();
      break;
    //e - edit photo using editing interface
    case Qt::Key_E:
      if(e->state() & Qt::ControlButton && numSelected() >= 1) emit editSelectedPhoto();
      break;
    //allow base class to handle key event
    default:
      QIconView::keyPressEvent(e);
      break;
  }
}
//==============================================
