/*
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 "media/view/media_view_pip_raster.h"

#include "ui/image/image_prepare.h"
#include "ui/widgets/shadow.h"
#include "styles/style_calls.h" // st::callShadow.

namespace Media::View {
namespace {

[[nodiscard]] Streaming::FrameRequest UnrotateRequest(
		const Streaming::FrameRequest &request,
		int rotation) {
	if (!rotation) {
		return request;
	}
	const auto unrotatedCorner = [&](RectPart corner) {
		if (!(request.corners & corner)) {
			return RectPart(0);
		}
		switch (corner) {
		case RectPart::TopLeft:
			return (rotation == 90)
				? RectPart::BottomLeft
				: (rotation == 180)
				? RectPart::BottomRight
				: RectPart::TopRight;
		case RectPart::TopRight:
			return (rotation == 90)
				? RectPart::TopLeft
				: (rotation == 180)
				? RectPart::BottomLeft
				: RectPart::BottomRight;
		case RectPart::BottomRight:
			return (rotation == 90)
				? RectPart::TopRight
				: (rotation == 180)
				? RectPart::TopLeft
				: RectPart::BottomLeft;
		case RectPart::BottomLeft:
			return (rotation == 90)
				? RectPart::BottomRight
				: (rotation == 180)
				? RectPart::TopRight
				: RectPart::TopLeft;
		}
		Unexpected("Corner in rotateCorner.");
	};
	auto result = request;
	result.outer = FlipSizeByRotation(request.outer, rotation);
	result.resize = FlipSizeByRotation(request.resize, rotation);
	result.corners = unrotatedCorner(RectPart::TopLeft)
		| unrotatedCorner(RectPart::TopRight)
		| unrotatedCorner(RectPart::BottomRight)
		| unrotatedCorner(RectPart::BottomLeft);
	return result;
}

} // namespace

Pip::RendererSW::RendererSW(not_null<Pip*> owner)
: _owner(owner)
, _roundRect(ImageRoundRadius::Large, st::radialBg) {
}

void Pip::RendererSW::paintFallback(
		Painter &&p,
		const QRegion &clip,
		Ui::GL::Backend backend) {
	_p = &p;
	_clip = &clip;
	_clipOuter = clip.boundingRect();
	_owner->paint(this);
	_p = nullptr;
	_clip = nullptr;
}

void Pip::RendererSW::paintTransformedVideoFrame(
		ContentGeometry geometry) {
	paintTransformedImage(
		_owner->videoFrame(frameRequest(geometry)),
		geometry);
}

void Pip::RendererSW::paintTransformedStaticContent(
		const QImage &image,
		ContentGeometry geometry) {
	paintTransformedImage(
		staticContentByRequest(image, frameRequest(geometry)),
		geometry);
}

void Pip::RendererSW::paintFade(ContentGeometry geometry) const {
	using Part = RectPart;
	const auto sides = geometry.attached;
	const auto rounded = RectPart(0)
		| ((sides & (Part::Top | Part::Left)) ? Part(0) : Part::TopLeft)
		| ((sides & (Part::Top | Part::Right)) ? Part(0) : Part::TopRight)
		| ((sides & (Part::Bottom | Part::Right))
			? Part(0)
			: Part::BottomRight)
		| ((sides & (Part::Bottom | Part::Left))
			? Part(0)
			: Part::BottomLeft);
	_roundRect.paintSomeRounded(
		*_p,
		geometry.inner,
		rounded | Part::NoTopBottom | Part::Top | Part::Bottom);
}

void Pip::RendererSW::paintButtonsStart() {
}

void Pip::RendererSW::paintButton(
		const Button &button,
		int outerWidth,
		float64 shown,
		float64 over,
		const style::icon &icon,
		const style::icon &iconOver) {
	if (over < 1.) {
		_p->setOpacity(shown);
		icon.paint(*_p, button.icon.x(), button.icon.y(), outerWidth);
	}
	if (over > 0.) {
		_p->setOpacity(over * shown);
		iconOver.paint(*_p, button.icon.x(), button.icon.y(), outerWidth);
	}
}

Pip::FrameRequest Pip::RendererSW::frameRequest(
		ContentGeometry geometry) const {
	auto result = FrameRequest();
	result.outer = geometry.inner.size() * style::DevicePixelRatio();
	result.resize = result.outer;
	result.corners = RectPart(0)
		| ((geometry.attached & (RectPart::Left | RectPart::Top))
			? RectPart(0)
			: RectPart::TopLeft)
		| ((geometry.attached & (RectPart::Top | RectPart::Right))
			? RectPart(0)
			: RectPart::TopRight)
		| ((geometry.attached & (RectPart::Right | RectPart::Bottom))
			? RectPart(0)
			: RectPart::BottomRight)
		| ((geometry.attached & (RectPart::Bottom | RectPart::Left))
			? RectPart(0)
			: RectPart::BottomLeft);
	result.radius = ImageRoundRadius::Large;
	return UnrotateRequest(result, geometry.rotation);
}

QImage Pip::RendererSW::staticContentByRequest(
		const QImage &image,
		const FrameRequest &request) {
	if (request.resize.isEmpty()) {
		return QImage();
	} else if (!_preparedStaticContent.isNull()
		&& _preparedStaticRequest == request
		&& image.cacheKey() == _preparedStaticKey) {
		return _preparedStaticContent;
	}
	_preparedStaticKey = image.cacheKey();
	_preparedStaticRequest = request;
	//_preparedCoverStorage = Streaming::PrepareByRequest(
	//	_instance.info().video.cover,
	//	false,
	//	_instance.info().video.rotation,
	//	request,
	//	std::move(_preparedCoverStorage));
	using Option = Images::Option;
	const auto corner = [&](RectPart part, Option skip) {
		return !(request.corners & part) ? skip : Option();
	};
	const auto options = Option::RoundLarge
		| corner(RectPart::TopLeft, Option::RoundSkipTopLeft)
		| corner(RectPart::TopRight, Option::RoundSkipTopRight)
		| corner(RectPart::BottomLeft, Option::RoundSkipBottomLeft)
		| corner(RectPart::BottomRight, Option::RoundSkipBottomRight);
	_preparedStaticContent = Images::Prepare(
		image,
		request.resize,
		{ .options = options, .outer = request.outer });
	return _preparedStaticContent;
}

void Pip::RendererSW::paintTransformedImage(
		const QImage &image,
		ContentGeometry geometry) {
	const auto rect = geometry.inner;
	const auto rotation = geometry.rotation;
	if (geometry.useTransparency) {
		Ui::Shadow::paint(*_p, rect, geometry.outer.width(), st::callShadow);
	}

	if (UsePainterRotation(rotation)) {
		if (rotation) {
			_p->save();
			_p->rotate(rotation);
		}
		PainterHighQualityEnabler hq(*_p);
		_p->drawImage(RotatedRect(rect, rotation), image);
		if (rotation) {
			_p->restore();
		}
	} else {
		_p->drawImage(rect, RotateFrameImage(image, rotation));
	}

	if (geometry.fade > 0) {
		_p->setOpacity(geometry.fade);
		paintFade(geometry);
	}
}

void Pip::RendererSW::paintRadialLoading(
		QRect inner,
		float64 controlsShown) {
	_owner->paintRadialLoadingContent(*_p, inner, st::radialFg->c);
}

void Pip::RendererSW::paintPlayback(QRect outer, float64 shown) {
	_owner->paintPlaybackContent(*_p, outer, shown);
}

void Pip::RendererSW::paintVolumeController(QRect outer, float64 shown) {
	_owner->paintVolumeControllerContent(*_p, outer, shown);
}

} // namespace Media::View
