/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org

Telegram Desktop 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 3 of the License, or
(at your option) any later version.

It 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.

Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#include "stdafx.h"
#include "lang.h"

#include "layerwidget.h"
#include "application.h"
#include "window.h"
#include "mainwidget.h"
#include "gui/filedialog.h"

BackgroundWidget::BackgroundWidget(QWidget *parent, LayeredWidget *w) : QWidget(parent), w(w), _hidden(0),
	aBackground(0), aBackgroundFunc(anim::easeOutCirc), hiding(false), shadow(st::boxShadow) {
	w->setParent(this);
	setGeometry(0, 0, App::wnd()->width(), App::wnd()->height());
	aBackground.start(1);
	anim::start(this);
	show();
	connect(w, SIGNAL(closed()), this, SLOT(onInnerClose()));
	connect(w, SIGNAL(resized()), this, SLOT(update()));
	w->setFocus();
}

void BackgroundWidget::paintEvent(QPaintEvent *e) {
	bool trivial = (rect() == e->rect());

	QPainter p(this);
	if (!trivial) {
		p.setClipRect(e->rect());
	}
	p.setOpacity(st::layerAlpha * aBackground.current());
	p.fillRect(rect(), st::layerBG->b);

	p.setOpacity(aBackground.current());
	shadow.paint(p, w->boxRect());
}

void BackgroundWidget::keyPressEvent(QKeyEvent *e) {
	if (e->key() == Qt::Key_Escape) {
		startHide();
	}
}

void BackgroundWidget::mousePressEvent(QMouseEvent *e) {
}

void BackgroundWidget::onClose() {
	startHide();
}

void BackgroundWidget::onInnerClose() {
	if (_hidden) {
		w->deleteLater();
		w = _hidden;
		_hidden = 0;
		w->show();
		resizeEvent(0);
		w->animStep(1);
		update();
	} else {
		onClose();
	}
}

void BackgroundWidget::startHide() {
	if (App::main()) App::main()->setInnerFocus();
	hiding = true;
	aBackground.start(0);
	anim::start(this);
	w->startHide();
}

void BackgroundWidget::resizeEvent(QResizeEvent *e) {
	w->parentResized();
}

void BackgroundWidget::replaceInner(LayeredWidget *n) {
	if (_hidden) _hidden->deleteLater();
	_hidden = w;
	_hidden->hide();
	w = n;
	w->setParent(this);
	connect(w, SIGNAL(closed()), this, SLOT(onInnerClose()));
	connect(w, SIGNAL(resized()), this, SLOT(update()));
	w->show();
	resizeEvent(0);
	w->animStep(1);
	update();
}

bool BackgroundWidget::animStep(float64 ms) {
	float64 dt = ms / (hiding ? st::layerHideDuration : st::layerSlideDuration);
	w->animStep(dt);
	bool res = true;
	if (dt >= 1) {
		aBackground.finish();
		if (hiding)	{
			QTimer::singleShot(0, App::wnd(), SLOT(layerHidden()));
		}
		res = false;
	} else {
		aBackground.update(dt, aBackgroundFunc);
	}
	update();
	return res;
}

BackgroundWidget::~BackgroundWidget() {
	if (App::wnd()) App::wnd()->noBox(this);
	w->deleteLater();
	if (_hidden) _hidden->deleteLater();
}

LayerWidget::LayerWidget(QWidget *parent, PhotoData *photo, HistoryItem *item) : QWidget(parent)
, photo(photo)
, video(0)
, aBackground(0)
, aOver(0)
, iX(App::wnd()->width() / 2)
, iY(App::wnd()->height() / 2)
, iW(0)
, iCoordFunc(anim::sineInOut)
, aBackgroundFunc(anim::easeOutCirc)
, aOverFunc(anim::linear)
, hiding(false)
, _touchPress(false)
, _touchMove(false)
, _touchRightButton(false)
, _menu(0)
{
	int32 x, y, w;
	if (App::wnd()->getPhotoCoords(photo, x, y, w)) {
		iX = anim::ivalue(x);
		iY = anim::ivalue(y);
		iW = anim::ivalue(w);
	}
	photo->full->load();
	setGeometry(0, 0, App::wnd()->width(), App::wnd()->height());
	aBackground.start(1);
	aOver.start(1);
	anim::start(this);
	show();
	setFocus();
	App::contextItem(item);
	
	setAttribute(Qt::WA_AcceptTouchEvents);
	_touchTimer.setSingleShot(true);
	connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));
}

LayerWidget::LayerWidget(QWidget *parent, VideoData *video, HistoryItem *item) : QWidget(parent)
, photo(0)
, video(video)
, aBackground(0)
, aOver(0)
, iX(App::wnd()->width() / 2)
, iY(App::wnd()->height() / 2)
, iW(0)
, iCoordFunc(anim::sineInOut)
, aBackgroundFunc(anim::easeOutCirc)
, aOverFunc(anim::linear)
, hiding(false)
, _touchPress(false)
, _touchMove(false)
, _touchRightButton(false)
, _menu(0)
{
	int32 x, y, w;
	if (App::wnd()->getVideoCoords(video, x, y, w)) {
		iX = anim::ivalue(x);
		iY = anim::ivalue(y);
		iW = anim::ivalue(w);
	}
	setGeometry(0, 0, App::wnd()->width(), App::wnd()->height());
	aBackground.start(1);
	aOver.start(1);
	anim::start(this);
	show();
	setFocus();
	App::contextItem(item);

	setAttribute(Qt::WA_AcceptTouchEvents);
	_touchTimer.setSingleShot(true);
	connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));
}

PhotoData *LayerWidget::photoShown() {
	return hiding ? 0 : photo;
}

void LayerWidget::onTouchTimer() {
	_touchRightButton = true;
}

bool LayerWidget::event(QEvent *e) {
	if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
		QTouchEvent *ev = static_cast<QTouchEvent*>(e);
		if (ev->device()->type() == QTouchDevice::TouchScreen) {
			touchEvent(ev);
			return true;
		}
	}
	return QWidget::event(e);
}

void LayerWidget::touchEvent(QTouchEvent *e) {
	switch (e->type()) {
	case QEvent::TouchBegin:
		if (_touchPress || e->touchPoints().isEmpty()) return;
		_touchTimer.start(QApplication::startDragTime());
		_touchPress = true;
		_touchMove = _touchRightButton = false;
		_touchStart = e->touchPoints().cbegin()->screenPos().toPoint();
	break;

	case QEvent::TouchUpdate:
		if (!_touchPress || e->touchPoints().isEmpty()) return;
		if (!_touchMove && (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart).manhattanLength() >= QApplication::startDragDistance()) {
			_touchMove = true;
		}
	break;

	case QEvent::TouchEnd:
		if (!_touchPress) return;
		if (!_touchMove && App::wnd()) {
			Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton);
			QPoint mapped(mapFromGlobal(_touchStart)), winMapped(App::wnd()->mapFromGlobal(_touchStart));

			QMouseEvent pressEvent(QEvent::MouseButtonPress, mapped, winMapped, _touchStart, btn, Qt::MouseButtons(btn), Qt::KeyboardModifiers());
			pressEvent.accept();
			mousePressEvent(&pressEvent);

			QMouseEvent releaseEvent(QEvent::MouseButtonRelease, mapped, winMapped, _touchStart, btn, Qt::MouseButtons(btn), Qt::KeyboardModifiers());
			mouseReleaseEvent(&releaseEvent);

			if (_touchRightButton) {
				QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, mapped, _touchStart);
				contextMenuEvent(&contextEvent);
			}
		}
		_touchTimer.stop();
		_touchPress = _touchMove = _touchRightButton = false;
	break;

	case QEvent::TouchCancel:
		_touchPress = false;
		_touchTimer.stop();
	break;
	}
}

void LayerWidget::onMenuDestroy(QObject *obj) {
	if (_menu == obj) {
		_menu = 0;
	}
}

void LayerWidget::paintEvent(QPaintEvent *e) {
	bool trivial = (rect() == e->rect());

	QPainter p(this);
	if (!trivial) {
		p.setClipRect(e->rect());
	}
	p.setOpacity(st::layerAlpha * aBackground.current());
	p.fillRect(rect(), st::layerBG->b);
	if (iW.current()) {
		if (!hiding) p.setOpacity(aOver.current());
		if (animating()) {
			const QPixmap &pm((photo ? (photo->full->loaded() ? photo->full : photo->thumb) : video->thumb)->pix());
			int32 h = pm.width() ? (pm.height() * iW.current() / pm.width()) : 1;
			p.drawPixmap(iX.current(), iY.current(), iW.current(), h, pm);
			if (!hiding) {
				p.setOpacity(1);
				p.setClipRect(App::wnd()->photoRect(), Qt::IntersectClip);
				p.drawPixmap(iX.current(), iY.current(), iW.current(), h, pm);
			}
		} else {
			const QPixmap &pm((photo ? (photo->full->loaded() ? photo->full : photo->thumb) : video->thumb)->pixNoCache(iW.current(), 0, !animating()));
			p.drawPixmap(iX.current(), iY.current(), pm);
		}
	}
}

void LayerWidget::keyPressEvent(QKeyEvent *e) {
	if (!_menu && e->key() == Qt::Key_Escape) {
		startHide();
	} else if (photo && photo->full->loaded() && (e == QKeySequence::Save || e == QKeySequence::SaveAs)) {
		QString file;
		if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")))) {
			if (!file.isEmpty()) {
				photo->full->pix().toImage().save(file, "JPG");
			}
		}
	} else if (photo && photo->full->loaded() && (e->key() == Qt::Key_Copy || (e->key() == Qt::Key_C && e->modifiers().testFlag(Qt::ControlModifier)))) {
		QApplication::clipboard()->setPixmap(photo->full->pix());
	}
}

void LayerWidget::mousePressEvent(QMouseEvent *e) {
	if (_menu) return;
	if (e->button() == Qt::LeftButton) startHide();
}

void LayerWidget::contextMenuEvent(QContextMenuEvent *e) {
	if (photo && photo->full->loaded() && !hiding) {
		if (_menu) {
			_menu->deleteLater();
			_menu = 0;
		}
		_menu = new QMenu(this);
		_menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextImage()))->setEnabled(true);
		_menu->addAction(lang(lng_context_copy_image), this, SLOT(copyContextImage()))->setEnabled(true);
		_menu->addAction(lang(lng_context_close_image), this, SLOT(startHide()))->setEnabled(true);
		if (App::contextItem()) {
			if (dynamic_cast<HistoryMessage*>(App::contextItem())) {
				_menu->addAction(lang(lng_context_forward_image), this, SLOT(forwardMessage()))->setEnabled(true);
			}
			_menu->addAction(lang(lng_context_delete_image), this, SLOT(deleteMessage()))->setEnabled(true);
		} else if ((App::self() && App::self()->photoId == photo->id) || (photo->chat && photo->chat->photoId == photo->id)) {
			_menu->addAction(lang(lng_context_delete_image), this, SLOT(deleteMessage()))->setEnabled(true);
		}
		_menu->setAttribute(Qt::WA_DeleteOnClose);

		_menu->setAttribute(Qt::WA_DeleteOnClose);
		connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*)));
		_menu->popup(e->globalPos());
		e->accept();
	}
}

void LayerWidget::deleteMessage() {
	if (!App::contextItem()) {
		if (App::self() && photo && App::self()->photoId == photo->id) {
			App::app()->peerClearPhoto(App::self()->id);
		} else if (photo->chat && photo->chat->photoId == photo->id) {
			App::app()->peerClearPhoto(photo->chat->id);
		}
		startHide();
	} else {
		App::wnd()->layerHidden();
		App::main()->deleteLayer();
	}
}

void LayerWidget::forwardMessage() {
	startHide();
	App::main()->forwardLayer();
}

void LayerWidget::saveContextImage() {
	if (!photo || !photo->full->loaded() || hiding) return;

	QString file;
	if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")))) {
		if (!file.isEmpty()) {
			photo->full->pix().toImage().save(file, "JPG");
		}
	}
}

void LayerWidget::copyContextImage() {
	if (!photo || !photo->full->loaded() || hiding) return;

	QApplication::clipboard()->setPixmap(photo->full->pix());
}

void LayerWidget::startHide() {
	hiding = true;
	aBackground.start(0);
	anim::start(this);
}

void LayerWidget::resizeEvent(QResizeEvent *e) {
	int32 w = width() - st::layerPadding.left() - st::layerPadding.right(), h = height() - st::layerPadding.top() - st::layerPadding.bottom();
	int32 iw = (photo ? photo->full : video->thumb)->width(), ih = (photo ? photo->full : video->thumb)->height();
	if (!iw || !ih) {
		iw = ih = 1;
	} else {
		switch (cScale()) {
		case dbisOneAndQuarter: iw = qRound(float64(iw) * 1.25 - 0.01); ih = qRound(float64(ih) * 1.25 - 0.01); break;
		case dbisOneAndHalf: iw = qRound(float64(iw) * 1.5 - 0.01); ih = qRound(float64(ih) * 1.5 - 0.01); break;
		case dbisTwo: iw *= 2; ih *= 2; break;
		}
	}
	if (w >= iw && h >= ih) {
		iW.start(iw);
		iX.start(st::layerPadding.left() + (w - iw) / 2);
		iY.start(st::layerPadding.top() + (h - ih) / 2);
	} else if (w * ih > iw * h) {
		int32 nw = qRound(iw * float64(h) / ih);
		iW.start(nw);
		iX.start(st::layerPadding.left() + (w - nw) / 2);
		iY.start(st::layerPadding.top());
	} else {
		int32 nh = qRound(ih * float64(w) / iw);
		iW.start(w);
		iX.start(st::layerPadding.left());
		iY.start(st::layerPadding.top() + (h - nh) / 2);
	}
	if (!animating() || hiding) {
		iX.finish();
		iY.finish();
		iW.finish();
	}
}

bool LayerWidget::animStep(float64 ms) {
	float64 dt = ms / (hiding ? st::layerHideDuration : st::layerSlideDuration);
	bool res = true;
	if (dt >= 1) {
		aBackground.finish();
		aOver.finish();
		iX.finish();
		iY.finish();
		iW.finish();
		if (hiding)	{
			QTimer::singleShot(0, App::wnd(), SLOT(layerHidden()));
		}
		res = false;
	} else {
		aBackground.update(dt, aBackgroundFunc);
		if (!hiding) {
			aOver.update(dt, aOverFunc);
			iX.update(dt, iCoordFunc);
			iY.update(dt, iCoordFunc);
			iW.update(dt, iCoordFunc);
		}
	}
	update();
	return res;
}

LayerWidget::~LayerWidget() {
	if (App::wnd()) App::wnd()->noLayer(this);
	delete _menu;
}