#include "image_view.h"

#include <QBuffer>
#include <QDebug>
#include <QEvent>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QMouseEvent>
#include <QScrollBar>
#include <QWheelEvent>
#include <qmath.h>


ImageView::ImageView(QWidget * parent): QGraphicsView(parent), item(new QGraphicsPixmapItem) {
	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	setDragMode(QGraphicsView::NoDrag);
	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
	setScene(new QGraphicsScene());
	setRenderHint(QPainter::Antialiasing, true);
	setRenderHint(QPainter::SmoothPixmapTransform, true);
	item->setTransformationMode(Qt::SmoothTransformation);
	item->setFlags(QGraphicsItem::GraphicsItemFlags());
	scene()->addItem(item);
	viewport()->setAutoFillBackground(false);
	viewport()->installEventFilter(this);
	autofit_ = smooth_big = true;
	interactive_          = false;
}


ImageView::~ImageView() {
	scene()->deleteLater();
}


QPixmap ImageView::pixmap() const {
	return map;
}


void ImageView::setPixmap(QPixmap pixmap) {
	map = pixmap;
	adjustView();
}


void ImageView::setViewInteractive(bool yes) {
	interactive_ = yes;
}


void ImageView::setImage(const QImage & i, bool save) {
	im_data.clear();
	if (i.isNull()) {
		item->setPixmap(QPixmap());
		map = QPixmap();
		return;
	}
	if (save) {
		QBuffer b(&im_data);
		b.open(QIODevice::ReadWrite);
		i.save(&b, "png");
		b.close();
	}
	map = QPixmap::fromImage(i);
	adjustView();
}


void ImageView::setImage(const QByteArray & i) {
	im_data = i;
	if (i.isEmpty()) {
		item->setPixmap(QPixmap());
		map = QPixmap();
		return;
	}
	map = QPixmap::fromImage(QImage::fromData(i));
	adjustView();
}


void ImageView::clear() {
	im_data.clear();
	item->setPixmap(QPixmap());
	map = QPixmap();
}


void ImageView::setSmoothBigImages(bool yes) {
	smooth_big = yes;
	adjustView();
}


void ImageView::mouseDoubleClickEvent(QMouseEvent *) {
	autofit();
}


void ImageView::mousePressEvent(QMouseEvent * e) {
	QGraphicsView::mousePressEvent(e);
	emit clicked(mapToScene(e->pos()), e->buttons());
}


void ImageView::mouseMoveEvent(QMouseEvent * e) {
	QGraphicsView::mouseMoveEvent(e);
	emit hovered(mapToScene(e->pos()), e->buttons());
}


void ImageView::mouseReleaseEvent(QMouseEvent * e) {
	QGraphicsView::mouseReleaseEvent(e);
	emit released(mapToScene(e->pos()), e->buttons());
}


void ImageView::wheelEvent(QWheelEvent * e) {
	if (!e->modifiers().testFlag(Qt::ControlModifier) || !viewInteractive()) return;
	double scl = 1. + e->angleDelta().y() / 500.;
	autofit_   = false;
	scale(scl, scl);
}


bool ImageView::eventFilter(QObject * o, QEvent * e) {
	QMouseEvent * me = reinterpret_cast<QMouseEvent *>(e);
	switch (e->type()) {
	case QEvent::Resize: adjustView(); break;
	case QEvent::MouseButtonPress:
		prev_pos = me->pos();
		if (me->buttons().testFlag(Qt::RightButton) && !autofit_ && viewInteractive()) viewport()->setCursor(Qt::ClosedHandCursor);
		break;
	case QEvent::MouseButtonRelease:
		viewport()->unsetCursor();
		prev_pos = me->pos();
		break;
	case QEvent::MouseMove:
		if (me->buttons().testFlag(Qt::RightButton) && !autofit_ && viewInteractive()) {
			QPoint dp = QPoint(me->pos() - prev_pos);
			horizontalScrollBar()->setValue(horizontalScrollBar()->value() - dp.x());
			verticalScrollBar()->setValue(verticalScrollBar()->value() - dp.y());
			prev_pos = me->pos();
		}
		break;
	default: break;
	}
	return QGraphicsView::eventFilter(o, e);
}


void ImageView::adjustView() {
	item->setScale(1.);
	if (smooth_big) {
		QSize ws = size();
#if QT_VERSION_MAJOR >= 5
		ws *= devicePixelRatio();
#endif
		int nw = map.size().boundedTo(ws).width();
		if (nw > 0) {
			qreal mp = map.width() / nw;
			if (mp > 1.) {
				QSize ss = map.size();
				item->setPixmap(map.scaled(map.size() / mp, Qt::KeepAspectRatio, Qt::SmoothTransformation));
				item->setScale(double(ss.width()) / item->pixmap().width());
			} else
				item->setPixmap(map);
		} else
			item->setPixmap(map);
	} else
		item->setPixmap(map);
	if (!autofit_) return;
	QRectF r = item->mapRectToScene(item->boundingRect());
	setSceneRect(r);
	fitInView(r, Qt::KeepAspectRatio);
	centerOn(r.center());
}


void ImageView::autofit() {
	autofit_ = true;
	adjustView();
}
