//==============================================
//  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 <qapplication.h>
#include <qpainter.h>
#include <qcursor.h>

//Projectwide includes
#include "selectionPlacementInterface.h"
#include "../../backend/tools/imageTools.h"
#include "../cursors.h"

//==============================================
SelectionPlacementInterface::SelectionPlacementInterface( QString imageFilename, 
                                                          QWidget *parent, const char* name ) 
                                                        : QWidget (parent, name )
{                  
  //store original image dimensions
  getImageSize( imageFilename, origImageSize );
  
  //construct scaled image
  scaleImage( imageFilename, scaledImage, 200, 200 );
  
  //construct an unselected scaled image
  unselectedScaledImage = scaledImage.copy();  
  int x, y;
  QRgb* rgb;
  uchar* scanLine;
  for( y=0; y<unselectedScaledImage.height(); y++)
  {   
    //iterate over each selected pixel in scanline
    scanLine = unselectedScaledImage.scanLine(y);
    for( x=0; x<unselectedScaledImage.width(); x++)
    {
      //compress dynamic range to 25% of original
      rgb = ((QRgb*)scanLine+x);
      
      double r = ((double)qRed(*rgb)   )/255.0;
      double g = ((double)qGreen(*rgb) )/255.0;
      double b = ((double)qBlue(*rgb)  )/255.0;
      
      //convert to hsv
      double h,s,v;
      RGBtoHSV(r,g,b,&h,&s,&v);
      
      //scale and clamp v
      v*=0.25;
      
      //convert adjusted color back to rgb colorspace and clamp
      HSVtoRGB( &r,&g,&b, h,s,v);         
      int rp = (int) QMIN( QMAX((r*255), 0), 255 );
      int gp = (int) QMIN( QMAX((g*255), 0), 255 );
      int bp = (int) QMIN( QMAX((b*255), 0), 255 );
      
      //set adjusted color value
      *rgb = qRgb(rp,gp,bp);          
    }
  }  
  
  //watch mouse movements in order to drag selection
  //watch mouse movements in order to move split point between adjusted and original image
  setMouseTracking(true);
  
  //by default no in dragging mode
  currentlyDragging = false;
  currentMouseShapeIsDrag = false;
  
  //accept focus when clicked on
  setFocusPolicy( QWidget::ClickFocus );

  //init selection area
  selection.setTopLeft( QPoint( -1, -1 ) );
  selection.setBottomRight( QPoint( -1, -1 ) );
}
//==============================================
SelectionPlacementInterface::~SelectionPlacementInterface() { }
//==============================================
void SelectionPlacementInterface::paintEvent(QPaintEvent *e)
{ 
  //if no scaled image just return
  if(scaledImage.isNull()) { return; }
  
  //create buffer to draw in
  QPixmap buffer( size() );
  
  //create a painter pointing to the buffer
  QPainter bufferPainter( &buffer );
  
  //turn off clipping to make painting operations faster
  bufferPainter.setClipping(false);

  //initialize buffer with background brush
  bufferPainter.fillRect( buffer.rect(), backgroundBrush() );

  int xOffset = (width() - scaledImage.width()) / 2;
  int yOffset = (height() - scaledImage.height()) / 2;

  //selection not set yet, simply paint the scaled image normally
  if(selection.width() == 0 || selection.height() == 0 )
  {
    bufferPainter.drawImage( QPoint(xOffset, yOffset), scaledImage );
  }
  //selection present...
  else
  {
    //first paint using unselected coloring
    bufferPainter.drawImage( QPoint(xOffset, yOffset), unselectedScaledImage );
    
    //convert selection coordinates to display space
    QRect displayRect = imageToDisplay( selection );
    QPoint topLeft = displayRect.topLeft() + QPoint( xOffset, yOffset );
    QPoint bottomRight = displayRect.bottomRight() + QPoint( xOffset, yOffset );
    
    //now paint selected region in color
    bufferPainter.drawImage( topLeft.x(), topLeft.y(),
                             scaledImage,
                             displayRect.left(), displayRect.top(),
                             displayRect.width(), displayRect.height() );                                   
    

    //paint thin line around selected region to help it stand out even more
    if( selection.width() < origImageSize.width() ||
        selection.height() < origImageSize.height() )
    {
      QPen pen;
      pen.setColor( gray );
      pen.setStyle( Qt::SolidLine );
      pen.setWidth( 2 );
      bufferPainter.setPen( pen);
    
      QRect selctRect( topLeft, bottomRight );
      bufferPainter.drawRect(selctRect);       
    }
  }
  
  //end painter  
  bufferPainter.end();
  
  //blit buffer to screen
  bitBlt( this,
          e->rect().x(), e->rect().y(),
          &buffer, 
          e->rect().x(), e->rect().y(),
          e->rect().width(), e->rect().height() );
}
//==============================================
QSize SelectionPlacementInterface::sizeHint() const
{ return minimumSizeHint(); }
//==============================================
QSize SelectionPlacementInterface::minimumSizeHint() const
{ return scaledImage.size(); }
//==============================================                              
bool SelectionPlacementInterface::overRegion( QPoint p )
{
  if( selection.width()  == 0 || selection.height() == 0 )
  { return false; }
  
  QRect displayRect = imageToDisplay( selection );

  //if the entire image is visible then no rectangle can be dragged so just
  //return false so a drag cursor never becomes visible since it might
  //confuse the user into thinking s/he could actually drag it
  if( displayRect.width() == scaledImage.width() &&
      displayRect.height() == scaledImage.height() )
    return false;

  //determine if mouse cursor is over region
  int xOffset = (width()  - scaledImage.width() ) / 2;
  int yOffset = (height() - scaledImage.height()) / 2;

  return ( p.x() >= xOffset + displayRect.left()  &&
           p.x() <= xOffset + displayRect.right() &&
           p.y() >= yOffset + displayRect.top()   &&
           p.y() <= yOffset + displayRect.bottom() );
}
//==============================================
void SelectionPlacementInterface::recenterSelection(QPoint mousePosition)
{
  //compute new viewing center
  QPoint center = QPoint( ((origImageSize.width()-1) * mousePosition.x()) / (width()-1),
                          ((origImageSize.height()-1) * mousePosition.y()) / (height()-1) );
  //move selection
  int sW = selection.width();
  int sH = selection.height();
  selection.setLeft( center.x() - sW/2 );
  selection.setTop( center.y() - sH/2 );
  selection.setRight( selection.left() + sW -1 );
  selection.setBottom( selection.top() + sH -1 );
  
  //ensure selection window never goes out of bounds
  if(selection.left() < 0 )
    selection.moveBy( -selection.left(), 0 );
  
  if(selection.right() > origImageSize.width() - 1 )
    selection.moveBy( (origImageSize.width() - 1) - selection.right(), 0 );
  
  if(selection.top() < 0 )
    selection.moveBy( 0, -selection.top() );
  
  if(selection.bottom() > origImageSize.height() - 1 )
    selection.moveBy( 0, (origImageSize.height() - 1) - selection.bottom() );
  
  //repaint and emit placement changed signal
  repaint(false); 
  emit placementChanged( selection );
}
//==============================================
void SelectionPlacementInterface::mousePressEvent( QMouseEvent *e)
{ 
  //if mouse press is not over the region then center viewed area over mouse press
  if( !currentMouseShapeIsDrag )
  { 
    recenterSelection(e->pos()); 
    currentMouseShapeIsDrag = true;
    setCursor( getCursor(MOVE_SELECTION_CURSOR) );
  }

  //enter dragging mode
  currentlyDragging = true; 
}
//==============================================
void SelectionPlacementInterface::mouseMoveEvent( QMouseEvent *e)
{
  //if not dragging update mosue cursor
  if(!currentlyDragging)
  { 
    if( !overRegion(e->pos() ) && currentMouseShapeIsDrag )
    { 
      currentMouseShapeIsDrag = false;
      setCursor( Qt::ArrowCursor ); 
    }
    else if( overRegion(e->pos() ) && !currentMouseShapeIsDrag )
    { 
      currentMouseShapeIsDrag = true;
      setCursor( getCursor(MOVE_SELECTION_CURSOR) );

    }
  }
  //move selection
  else { recenterSelection(e->pos()); }
}
//==============================================
void SelectionPlacementInterface::mouseReleaseEvent( QMouseEvent *)
{
  //disable dragging
  currentlyDragging = false;
}
//==============================================
QRect SelectionPlacementInterface::imageToDisplay( QRect r )
{
  //set top left
  QRect res;
  res.setTopLeft(QPoint( (int) (0.5+ (1.0*scaledImage.width()*r.left()) / origImageSize.width()),
                         (int) (0.5+ (1.0*scaledImage.height()*r.top()) / origImageSize.height()) ));                 

  //set width/height
  res.setWidth( (scaledImage.width() *r.width()) / origImageSize.width() );
  res.setHeight( (scaledImage.height() *r.height()) / origImageSize.height() );
  
  //if against the right hand size make sure scaled display coordiantes are also
  //against edge. rounding prevents this from occuring and is noticeable since selection
  //rectangle appears to never be at every edge.
  if( r.right() == origImageSize.width() - 1)
  { res.moveBy( (scaledImage.width()-1) - res.right(), 0 ); }

  if( r.bottom() == origImageSize.height() - 1)
  { res.moveBy( 0, (scaledImage.height()-1) - res.bottom() ); }
  
  //return new rect
  return res; 
}
//==============================================
QRect SelectionPlacementInterface::getSelectedRegion()
{
  return selection;
}
//==============================================
void SelectionPlacementInterface::setSelectedRegion( QRect selection )
{
  this->selection = selection;
  repaint(false); 
}
//==============================================