/*
 * Copyright 2009-2010  Stefan Gehn <stefan@srcbox.net>
 * 
 * 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.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "PhotoDraggable.hpp"
#include "PhotoPage.hpp"
#include "DropTarget.hpp"

#include <QAction>
#include <QGraphicsSceneEvent>
#include <QMenu>
#include <QPainter>
#include <QStyleOption>
#include <QDebug>

#include <cmath>


class ZoomRotateButton: public QGraphicsRectItem
{
public:
  ZoomRotateButton(PhotoDraggable *parent): QGraphicsRectItem(0,0,16,16,parent)
  {
    setBrush(QBrush(Qt::blue));
    setPen(Qt::NoPen);
    setAcceptedMouseButtons(Qt::LeftButton);
    setToolTip("Zoom");
  }

  ~ZoomRotateButton()
  {
  }

protected:
  void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  {
    QPointF delta = event->pos() - event->lastPos();
    //qDebug() << "delta" << delta;

    QRectF oldParentRect = parentItem()->boundingRect();
    oldParentRect.setTopLeft(oldParentRect.center()); // scaling is based on center
    QRectF newParentRect = oldParentRect.adjusted(0, 0, delta.x(), delta.y());

    // convert delta values to scaling factor
    qreal dxs = newParentRect.width() / oldParentRect.width();
    qreal dys = newParentRect.height() / oldParentRect.height();
    qreal scalefactor = (dxs + dys) / 2;
    //qDebug() << "dxs" << dxs << "; dys" << dys << "=> factor" << scalefactor;

    // position of parent center
    QPointF parentCenter = parentItem()->boundingRect().center();
    // current and last position in parent coords
    QPointF curPos = mapToParent(event->pos()) - parentCenter;
    QPointF lastPos = mapToParent(event->lastPos()) - parentCenter;
    //qDebug() << "curPos" << curPos << " lastPos" << lastPos;

    qreal diffAngle = (std::atan2(lastPos.x(), lastPos.y()) - std::atan2(curPos.x(), curPos.y())) * (180 / M_PI);
    //qDebug() << "diffAngle" << diffAngle;

    static_cast<PhotoDraggable *>(parentItem())->scaleAndRotate(scalefactor, diffAngle);
    event->accept();
  }

  void mousePressEvent(QGraphicsSceneMouseEvent *event)
  {
		event->setAccepted(event->button() == Qt::LeftButton);
  }

  void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
  {
		event->setAccepted(event->button() == Qt::LeftButton);
  }
}; // class ZoomRotateButton


// ----------------------------------------------------------------------------


PhotoDraggable::PhotoDraggable(QGraphicsItem *parent) : Draggable(parent)
{
  qDebug() << "PhotoDraggable::PhotoDraggable()";
	//setZValue(1);
  mZoomRotateButton = new ZoomRotateButton(this);
	mDisableOverlay = new QGraphicsRectItem(this);
	mDisableOverlay->setVisible(false);
	mDisableOverlay->setPen(Qt::NoPen);
	mDisableOverlay->setBrush(QBrush(QColor(255, 255, 255, 200)));
	mDisableOverlay->setAcceptedMouseButtons(0);

	QAction *delAction = new QAction(tr("Foto entfernen"), this);
	connect(delAction, SIGNAL(triggered()), this, SLOT(deleteLater()));
	addAction(delAction);
}

PhotoDraggable::~PhotoDraggable()
{
  qDebug() << "PhotoDraggable::~PhotoDraggable()";
}

void PhotoDraggable::scaleAndRotate(qreal scaleFactor, qreal rotateAngle)
{
  // Ignore scaling/rotation for disabled items
  if (!isEnabled())
    return;

  // rotate a bit
  if (qAbs(rotateAngle) > 0.0001)
  {
    setZRotation(zRotation() + rotateAngle);
  }

  // scale the item and keep aspect-ratio
  // also do not allow to scale a photo down to a tiny dot ;)
  qreal xScaleNew = xScale() * scaleFactor;
  qreal yScaleNew = yScale() * scaleFactor;
  if ((size().width() * xScaleNew > 100) && (size().height() * yScaleNew > 100))
  {
    setXScale(xScaleNew);
    setYScale(yScaleNew);
  }
}

void PhotoDraggable::contextMenuEvent(QGraphicsSceneContextMenuEvent * event)
{
	QMenu contextMenu;
	event->accept();
	contextMenu.addActions(actions());
	contextMenu.exec(event->screenPos());
}

void PhotoDraggable::resizeEvent(QGraphicsSceneResizeEvent *rev)
{
  // adjust position of zoom-rotate button
  QPointF bPos = geometry().bottomRight();
  bPos.rx() -= mZoomRotateButton->rect().width();
  bPos.ry() -= mZoomRotateButton->rect().height();
  mZoomRotateButton->setPos(bPos);
  rev->accept();
}

#if 0
void PhotoDraggable::wheelEvent(QGraphicsSceneWheelEvent * event)
{
	qDebug() << "PhotoDraggable::wheelEvent()";
	if (isSelected())
  {
    if (event->modifiers() & Qt::AltModifier)
    {
      qreal divisor = (event->modifiers() & Qt::ShiftModifier) ? 180.0 : 60.0;
      scaleAndRotate(0, event->delta() / divisor);
    }
    else
    {
      qreal scaleFactor = event->delta() > 0 ? 1.1 : (1 / 1.1);
      scaleAndRotate(scaleFactor, 0);
    }
    event->accept();
    return;
  }

	qDebug() << "Ignoring wheelEvent on PhotoDraggable that is not selected";
  Draggable::wheelEvent(event);
}
#endif

void PhotoDraggable::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
  Q_UNUSED(widget);

  //-------------
  // taken from QGraphicsPixmapItem::paint()
  painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
  QRectF exposed = option->exposedRect.adjusted(-1, -1, 1, 1);
  exposed &= QRectF(0, 0, mPixmap.width(), mPixmap.height());
  painter->drawPixmap(exposed, mPixmap, exposed);
  //-------------

	// draw border if item is enabled
	if (option->state & QStyle::State_Enabled)
  {
    painter->setClipRect(option->exposedRect);

    QPen newPen(painter->pen());
		newPen.setWidth(7);
		newPen.setColor(mBorderColor);
		painter->setPen(newPen);

    qreal hpenw = painter->pen().widthF() / 2.0;
    QRectF lr = boundingRect().adjusted(hpenw, hpenw, -hpenw, -hpenw);

    painter->drawRect(lr);
  }
}

void PhotoDraggable::updateBorderColor()
{
	DropTarget *dt = collidingDropTarget();

	if (dt)
		mBorderColor = QColor(255, 0, 0, 180);
	else if (isSelected())
		mBorderColor = QColor(0, 0, 255, 180);
	else
		mBorderColor = QColor(128, 128, 128, 180);
}

QVariant PhotoDraggable::itemChange(GraphicsItemChange change, const QVariant &value)
{
	if (change == ItemEnabledHasChanged)
  {
		mDisableOverlay->setVisible(!value.toBool());
		mZoomRotateButton->setVisible(value.toBool());
		updateBorderColor();
	}
	else if (change == ItemPositionHasChanged || change == ItemSelectedHasChanged)
  {
		updateBorderColor();
	}
  return Draggable::itemChange(change, value);
}

const QPixmap &PhotoDraggable::pixmap() const
{
  return mPixmap;
}

void PhotoDraggable::setPixmap(const QPixmap &pm)
{
  if (pm.isNull())
    return;
	mPixmap = pm;
  resize(mPixmap.size());
	mDisableOverlay->setRect(QRectF(QPointF(0,0), size()));
  setTransformOrigin(rect().center());
}

/*static*/
PhotoDraggable *PhotoDraggable::createDraggable(const QString &photoPath,
    const QPointF &initialScenePos)
{
  QPixmap pm(photoPath);
  if (pm.isNull())
    return 0L; // don't create draggable for invalid pixmaps
  PhotoDraggable *dr = new PhotoDraggable();
  dr->setPixmap(pm);

  // scale to a sane size but keep aspect ratio
  const QSizeF orgSize = dr->size();
  QSizeF newSize(orgSize);
  newSize.scale(160, 160, Qt::KeepAspectRatio);
  dr->setXScale(newSize.width() / orgSize.width());
  dr->setYScale(newSize.height() / orgSize.height());

  // center new draggable around the position that we were given
	QPointF initialPos(initialScenePos);
	initialPos.rx() -= orgSize.width() / 2.0;
	initialPos.ry() -= orgSize.height() / 2.0;
	dr->setPos(initialPos);

  return dr;
}

QDataStream & operator<< (QDataStream &stream, const PhotoDraggable &draggable)
{
	// save photo from stream
	stream << draggable.pixmap();
  stream << draggable.pos();
  stream << draggable.xScale();
  stream << draggable.yScale();
  stream << draggable.zRotation();
  return stream;
}

QDataStream & operator>> (QDataStream &stream, PhotoDraggable &draggable)
{
  QPixmap pm;
  QPointF pos;
  qreal xScale;
  qreal yScale;
  qreal zRotation;
	// read photo from stream
  stream >> pm >> pos >> xScale >> yScale >> zRotation;
	//TODO: Range/validity checks for read data
  draggable.setPixmap(pm);
  draggable.setPos(pos);
  draggable.setXScale(xScale);
  draggable.setYScale(yScale);
  draggable.setZRotation(zRotation);
  return stream;
}

#include "moc_PhotoDraggable.cpp"
