#include <cameraModeRequest.h>
#include <cameraSession.h>
#include <gtest/gtest.h>
#include <sessionModeConfigure.h>
#include <memory>

namespace lcamera_dev {
namespace {

LcameraDevConfiguredCameraMode syntheticResolvedMode(
	unsigned width,
	unsigned height,
	const char *pixelFormatName)
{
	LcameraDevConfiguredCameraMode mode;
	mode.width = width;
	mode.height = height;
	mode.colourSpace = LcameraDevColourSpace::Yuv;
	mode.pixelFormatName = pixelFormatName;
	mode.isFullyPlanar = true;
	mode.planeCount = 3;
	return mode;
}

LcameraDevCameraModeRequest makeRequest(unsigned width, unsigned height)
{
	LcameraDevCameraModeRequest request;
	request.width = width;
	request.height = height;
	request.colourSpace = LcameraDevColourSpace::Yuv;
	request.fullPlanarIsOptional = false;
	return request;
}

TEST(SessionModeConfigureStateTest, FirstConfigureMarksSessionConfigured)
{
	CameraSessionResources resources(
		CameraIdentityRecord{},
		std::shared_ptr<libcamera::Camera>());

	const LcameraDevCameraModeRequest request = makeRequest(640, 480);
	const LcameraDevConfiguredCameraMode resolvedMode =
		syntheticResolvedMode(640, 480, "YU12");

	const ConfigureSessionModeStatus status =
		applyModeRequestToSessionState(
			resources,
			request,
			resolvedMode,
			nullptr);

	EXPECT_EQ(status, ConfigureSessionModeStatus::Configured);
	EXPECT_TRUE(resources.configuredMode.has_value());
	EXPECT_EQ(resources.configuredMode->width, 640u);
	EXPECT_EQ(resources.configuredMode->pixelFormatName, "YU12");
	EXPECT_EQ(resources.libcameraConfigureCallCount, 1);
}

TEST(SessionModeConfigureStateTest, IdenticalReconfigureIsNoOp)
{
	CameraSessionResources resources(
		CameraIdentityRecord{},
		std::shared_ptr<libcamera::Camera>());

	const LcameraDevCameraModeRequest request = makeRequest(640, 480);
	const LcameraDevConfiguredCameraMode resolvedMode =
		syntheticResolvedMode(640, 480, "YU12");

	applyModeRequestToSessionState(
		resources,
		request,
		resolvedMode,
		nullptr);

	const ConfigureSessionModeStatus status =
		applyModeRequestToSessionState(
			resources,
			request,
			resolvedMode,
			nullptr);

	EXPECT_EQ(status, ConfigureSessionModeStatus::NoOpAlreadyConfigured);
	EXPECT_EQ(resources.libcameraConfigureCallCount, 1);
	EXPECT_EQ(resources.configuredMode->pixelFormatName, "YU12");
}

TEST(SessionModeConfigureStateTest, ConflictingReconfigureThrows)
{
	CameraSessionResources resources(
		CameraIdentityRecord{},
		std::shared_ptr<libcamera::Camera>());

	const LcameraDevCameraModeRequest firstRequest = makeRequest(640, 480);
	const LcameraDevConfiguredCameraMode firstMode =
		syntheticResolvedMode(640, 480, "YU12");

	applyModeRequestToSessionState(
		resources,
		firstRequest,
		firstMode,
		nullptr);

	const LcameraDevCameraModeRequest conflictingRequest = makeRequest(1280, 720);

	EXPECT_THROW(
		applyModeRequestToSessionState(
			resources,
			conflictingRequest,
			syntheticResolvedMode(1280, 720, "YU12"),
			nullptr),
		std::runtime_error);
}

TEST(SessionModeConfigureStateTest, GetConfiguredModeReturnsStoredValues)
{
	CameraSessionResources resources(
		CameraIdentityRecord{},
		std::shared_ptr<libcamera::Camera>());

	const LcameraDevCameraModeRequest request = makeRequest(800, 600);
	const LcameraDevConfiguredCameraMode resolvedMode =
		syntheticResolvedMode(800, 600, "YU12");

	applyModeRequestToSessionState(
		resources,
		request,
		resolvedMode,
		nullptr);

	EXPECT_EQ(resources.configuredMode->width, 800u);
	EXPECT_EQ(resources.configuredMode->height, 600u);
	EXPECT_EQ(resources.configuredMode->colourSpace, LcameraDevColourSpace::Yuv);
	EXPECT_TRUE(resources.configuredMode->isFullyPlanar);
	EXPECT_EQ(resources.configuredMode->planeCount, 3u);
}

} // namespace
} // namespace lcamera_dev
