/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "editor/photo_editor_content.h"

#include "editor/editor_crop.h"
#include "editor/editor_paint.h"
#include "history/history_drag_area.h"
#include "media/view/media_view_pip.h"
#include "storage/storage_media_prepare.h"

namespace Editor {

using Media::View::FlipSizeByRotation;
using Media::View::RotatedRect;

PhotoEditorContent::PhotoEditorContent(
	not_null<Ui::RpWidget*> parent,
	std::shared_ptr<Image> photo,
	PhotoModifications modifications,
	std::shared_ptr<Controllers> controllers,
	EditorData data)
: RpWidget(parent)
, _photoSize(photo->size())
, _paint(base::make_unique_q<Paint>(
	this,
	modifications,
	_photoSize,
	std::move(controllers)))
, _crop(base::make_unique_q<Crop>(
	this,
	modifications,
	_photoSize,
	std::move(data)))
, _photo(std::move(photo))
, _modifications(modifications) {

	rpl::combine(
		_modifications.value(),
		sizeValue()
	) | rpl::start_with_next([=](
			const PhotoModifications &mods, const QSize &size) {
		if (size.isEmpty()) {
			return;
		}
		const auto imageSizeF = [&] {
			const auto rotatedSize =
				FlipSizeByRotation(size, mods.angle);
			const auto m = _crop->cropMargins();
			const auto sizeForCrop = rotatedSize
				- QSize(m.left() + m.right(), m.top() + m.bottom());
			const auto originalSize = QSizeF(_photoSize);
			if ((originalSize.width() > sizeForCrop.width())
				|| (originalSize.height() > sizeForCrop.height())) {
				return originalSize.scaled(
					sizeForCrop,
					Qt::KeepAspectRatio);
			}
			return originalSize;
		}();
		const auto imageSize = QSize(imageSizeF.width(), imageSizeF.height());
		_imageRect = QRect(
			QPoint(-imageSize.width() / 2, -imageSize.height() / 2),
			imageSize);

		_imageMatrix.reset();
		_imageMatrix.translate(size.width() / 2, size.height() / 2);
		if (mods.flipped) {
			_imageMatrix.scale(-1, 1);
		}
		_imageMatrix.rotate(mods.angle);

		const auto geometry = _imageMatrix.mapRect(_imageRect);
		_crop->applyTransform(
			geometry + _crop->cropMargins(),
			mods.angle,
			mods.flipped, imageSizeF);
		_paint->applyTransform(geometry, mods.angle, mods.flipped);

		_innerRect = geometry;
	}, lifetime());

	paintRequest(
	) | rpl::start_with_next([=](const QRect &clip) {
		auto p = QPainter(this);

		p.fillRect(clip, Qt::transparent);
		p.setTransform(_imageMatrix);
		p.drawPixmap(_imageRect, _photo->pix(_imageRect.size()));
	}, lifetime());

	setupDragArea();
}

void PhotoEditorContent::applyModifications(
		PhotoModifications modifications) {
	_modifications = std::move(modifications);
	update();
}

void PhotoEditorContent::save(PhotoModifications &modifications) {
	modifications.crop = _crop->saveCropRect();
	_paint->keepResult();

	const auto savedScene = _paint->saveScene();
	if (!modifications.paint) {
		modifications.paint = savedScene;
	}
}

void PhotoEditorContent::applyMode(const PhotoEditorMode &mode) {
	if (mode.mode == PhotoEditorMode::Mode::Out) {
		if (mode.action == PhotoEditorMode::Action::Discard) {
			_paint->restoreScene();
		}
		return;
	}
	const auto isTransform = (mode.mode == PhotoEditorMode::Mode::Transform);
	_crop->setVisible(isTransform);

	_paint->setAttribute(Qt::WA_TransparentForMouseEvents, isTransform);
	if (!isTransform) {
		_paint->updateUndoState();
	}

	if (mode.action == PhotoEditorMode::Action::Discard) {
		_paint->cancel();
	} else if (mode.action == PhotoEditorMode::Action::Save) {
		_paint->keepResult();
	}
	_mode = mode;
}

void PhotoEditorContent::applyBrush(const Brush &brush) {
	_paint->applyBrush(brush);
}

bool PhotoEditorContent::handleKeyPress(not_null<QKeyEvent*> e) const {
	return false;
}

void PhotoEditorContent::setupDragArea() {
	auto dragEnterFilter = [=](const QMimeData *data) {
		return (_mode.mode == PhotoEditorMode::Mode::Paint)
			? Storage::ValidatePhotoEditorMediaDragData(data)
			: false;
	};

	const auto areas = DragArea::SetupDragAreaToContainer(
		this,
		std::move(dragEnterFilter),
		nullptr,
		nullptr,
		[](const QMimeData *d) { return Storage::MimeDataState::Image; },
		true);

	areas.photo->setDroppedCallback([=](const QMimeData *data) {
		_paint->handleMimeData(data);
	});
}

} // namespace Editor
