/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2023, Ideas On Board Oy
 *
 * transform.cpp - Transform and Orientation tests
 */

#include <iostream>

#include <libcamera/orientation.h>
#include <libcamera/transform.h>

#include "test.h"

using namespace std;
using namespace libcamera;

class TransformTest : public Test
{
protected:
	int run();
};

int TransformTest::run()
{
	/*
	 * RotationTestEntry collects two Orientation and one Transform that
	 * gets combined to validate that (o1 / o2 = T) and (o1 = o2 * T)
	 *
	 * o1 / o2 = t computes the Transform to apply to o2 to obtain o1
	 * o2 * t = o1 combines o2 with t by applying o2 first then t
	 *
	 * The comments on the (most complex) transform show how applying to
	 * an image with orientation o2 the Transform t allows to obtain o1.
	 *
	 * The image with basic rotation0 is assumed to be:
	 *
	 * 	AB
	 * 	CD
	 *
	 * And the Transform operators are:
	 *
	 * 	V = vertical flip
	 * 	H = horizontal flip
	 * 	T = transpose
	 *
	 * the operator '* (T|V)' applies V first then T.
	 */
	static const struct RotationTestEntry {
		Orientation o1;
		Orientation o2;
		Transform t;
	} testEntries[] = {
		/* Test identities transforms first. */
		{
			Orientation::Rotate0, Orientation::Rotate0,
			Transform::Identity,
		},
		{
			Orientation::Rotate0Mirror, Orientation::Rotate0Mirror,
			Transform::Identity,
		},
		{
			Orientation::Rotate180, Orientation::Rotate180,
			Transform::Identity,
		},
		{
			Orientation::Rotate180Mirror, Orientation::Rotate180Mirror,
			Transform::Identity,
		},
		{
			Orientation::Rotate90, Orientation::Rotate90,
			Transform::Identity,
		},
		{
			Orientation::Rotate90Mirror, Orientation::Rotate90Mirror,
			Transform::Identity,
		},
		{
			Orientation::Rotate270, Orientation::Rotate270,
			Transform::Identity,
		},
		{
			Orientation::Rotate270Mirror, Orientation::Rotate270Mirror,
			Transform::Identity,
		},
		/*
		 * Combine 0 and 180 degrees rotation as they're the most common
		 * ones.
		 */
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	CD  * (H|V) =  	BA	AB
			 *	BA		CD	CD
			 */
			Orientation::Rotate0, Orientation::Rotate180,
			Transform::Rot180,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	AB  * (H|V) =  	CD	DC
			 *	CD		AB	BA
			 */
			Orientation::Rotate180, Orientation::Rotate0,
			Transform::Rot180
		},
		/* Test that transpositions are handled correctly. */
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	AB  * (T|V) =  	CD	CA
			 *	CD		AB	DB
			 */
			Orientation::Rotate90, Orientation::Rotate0,
			Transform::Rot90,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	CA  * (T|H) =  	AC	AB
			 *	DB		BD	CD
			 */
			Orientation::Rotate0, Orientation::Rotate90,
			Transform::Rot270,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	AB  * (T|H) =  	BA	BD
			 *	CD		DC	AC
			 */
			Orientation::Rotate270, Orientation::Rotate0,
			Transform::Rot270,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	BD  * (T|V) =  	AC	AB
			 *	AC		BD	CD
			 */
			Orientation::Rotate0, Orientation::Rotate270,
			Transform::Rot90,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	CD  * (T|H) =  	DC	DA
			 *	BA		AB	CB
			 */
			Orientation::Rotate90, Orientation::Rotate180,
			Transform::Rot270,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	DA  * (T|V) =  	CB	CD
			 *	CB		DA	BA
			 */
			Orientation::Rotate180, Orientation::Rotate90,
			Transform::Rot90,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	CD  * (T|V) =  	BA	BC
			 *	BA		CD	AD
			 */
			Orientation::Rotate270, Orientation::Rotate180,
			Transform::Rot90,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	BC  * (T|H) =  	CB	CD
			 *	AD		DA	BA
			 */
			Orientation::Rotate180, Orientation::Rotate270,
			Transform::Rot270,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	DA  * (V|H) =  	AD	BC
			 *	CB		BC	AD
			 */
			Orientation::Rotate270, Orientation::Rotate90,
			Transform::Rot180,
		},
		/* Test that mirroring is handled correctly. */
		{
			Orientation::Rotate0, Orientation::Rotate0Mirror,
			Transform::HFlip
		},
		{
			Orientation::Rotate0Mirror, Orientation::Rotate0,
			Transform::HFlip
		},
		{
			Orientation::Rotate180, Orientation::Rotate180Mirror,
			Transform::HFlip
		},
		{
			Orientation::Rotate180Mirror, Orientation::Rotate180,
			Transform::HFlip
		},
		{
			Orientation::Rotate90, Orientation::Rotate90Mirror,
			Transform::HFlip
		},
		{
			Orientation::Rotate90Mirror, Orientation::Rotate90,
			Transform::HFlip
		},
		{
			Orientation::Rotate270, Orientation::Rotate270Mirror,
			Transform::HFlip
		},
		{
			Orientation::Rotate270Mirror, Orientation::Rotate270,
			Transform::HFlip
		},
		{
			Orientation::Rotate0, Orientation::Rotate0Mirror,
			Transform::HFlip
		},
		/*
		 * More exotic transforms which include Transpositions and
		 * mirroring.
		 */
		{
			/*
			 *      o2      t       o1
			 *      ------------------
			 *	BC  * (V) =  	AD
			 *	AD		BC
			 */
			Orientation::Rotate90Mirror, Orientation::Rotate270,
			Transform::VFlip,
		},
		{
			/*
			 *      o2      t       o1
			 *      ------------------
			 *	CB  * (T) =  	CD
			 *	DA		BA
			 */
			Orientation::Rotate180, Orientation::Rotate270Mirror,
			Transform::Transpose,
		},
		{
			/*
			 *      o2      t       o1
			 *      ------------------
			 *	AD  * (T) =  	AB
			 *	BC		DC
			 */
			Orientation::Rotate0, Orientation::Rotate90Mirror,
			Transform::Transpose,
		},
		{
			/*
			 *      o2      t       o1
			 *      ------------------
			 *	AD  * (V) =  	BC
			 *	BC		AD
			 */
			Orientation::Rotate270, Orientation::Rotate90Mirror,
			Transform::VFlip,
		},
		{
			/*
			 *      o2      t       o1
			 *      ------------------
			 *	DA  * (V) =  	CB
			 *	CB		DA
			 */
			Orientation::Rotate270Mirror, Orientation::Rotate90,
			Transform::VFlip,
		},
		{
			/*
			 *      o2      t               o1
			 *      --------------------------
			 *	CB  * (V|H) =	BC  	AD
			 *	DA		AD	BC
			 */
			Orientation::Rotate90Mirror, Orientation::Rotate270Mirror,
			Transform::Rot180,
		},
	};

	for (const auto &entry : testEntries) {
		Transform transform = entry.o1 / entry.o2;
		if (transform != entry.t) {
			cerr << "Failed to validate: " << entry.o1
			     << " / " << entry.o2
			     << " = " << transformToString(entry.t) << endl;
			cerr << "Got back: "
			     << transformToString(transform) << endl;
			return TestFail;
		}

		Orientation adjusted = entry.o2 * entry.t;
		if (adjusted != entry.o1) {
			cerr << "Failed to validate: " << entry.o2
			     << " * " << transformToString(entry.t)
			     << " = " << entry.o1 << endl;
			cerr << "Got back: " << adjusted << endl;
			return TestFail;
		}
	}

	return TestPass;
}

TEST_REGISTER(TransformTest)
