// SPDX-License-Identifier: MIT
/*
 * Copyright © 2024 Arthur Grillo
 */

#include "igt.h"

#define FRAME_COUNT 100
#define NUM_FBS 2

struct rect_t {
	int x, y;
	int width, height;
};

struct plane_t {
	igt_plane_t *base;
	struct rect_t rect;
	uint32_t format;
	struct igt_fb fbs[NUM_FBS];
};

struct kms_t {
	struct rect_t crtc;
	struct plane_t primary;
	struct plane_t overlay_a;
	struct plane_t overlay_b;
	struct plane_t writeback;
};

struct data_t {
	int fd;
	igt_display_t display;
	igt_output_t *wb_output;
	drmModeModeInfo *mode;
	struct kms_t kms;
};

static void plane_setup(struct plane_t *plane, int index)
{
	igt_plane_set_size(plane->base, plane->rect.width, plane->rect.height);
	igt_plane_set_position(plane->base, plane->rect.x, plane->rect.y);
	igt_plane_set_fb(plane->base, &plane->fbs[index]);
}

static void gen_fbs(struct data_t *data)
{
	struct kms_t *kms = &data->kms;
	drmModeModeInfo *mode = igt_output_get_mode(data->wb_output);

	for (int i = 0; i < NUM_FBS; i++) {
		igt_create_color_fb(data->fd, kms->primary.rect.width, kms->primary.rect.height,
				    kms->primary.format, DRM_FORMAT_MOD_LINEAR,
				    !i, i, i,
				    &kms->primary.fbs[i]);

		igt_create_color_fb(data->fd, kms->overlay_a.rect.width, kms->overlay_a.rect.height,
				    kms->overlay_a.format, DRM_FORMAT_MOD_LINEAR,
				    i, !i, i,
				    &kms->overlay_a.fbs[i]);

		igt_create_color_fb(data->fd, kms->overlay_b.rect.width, kms->overlay_b.rect.height,
				    kms->overlay_b.format, DRM_FORMAT_MOD_LINEAR,
				    i, i, !i,
				    &kms->overlay_b.fbs[i]);

		kms->writeback.rect.width = mode->hdisplay;
		kms->writeback.rect.height = mode->vdisplay;
		igt_create_fb(data->fd, kms->writeback.rect.width, kms->writeback.rect.height,
			      kms->writeback.format, DRM_FORMAT_MOD_LINEAR,
			      &kms->writeback.fbs[i]);
	}
}

static igt_output_t *find_wb_output(struct data_t *data)
{
	for (int i = 0; i < data->display.n_outputs; i++) {
		igt_output_t *output = &data->display.outputs[i];

		if (output->config.connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
			continue;

		return output;

	}

	return NULL;
}

static void set_crtc_size(struct data_t *data)
{
	drmModeModeInfo *mode;
	struct rect_t *crtc = &data->kms.crtc;

	for_each_connector_mode(data->wb_output) {
		mode = &data->wb_output->config.connector->modes[j__];
		if (mode->hdisplay == crtc->width && mode->vdisplay == crtc->height) {
			igt_output_override_mode(data->wb_output, mode);
			return;
		}
	}


	igt_assert_f(0, "CRTC size %dx%d not supported\n", crtc->width, crtc->height);
}

static struct kms_t default_kms = {
	.crtc = {
		.width = 4096, .height = 2160,
	},
	.primary = {
		.rect = {
			.x = 101, .y = 0,
			.width = 3639, .height = 2160,
		},
		.format = DRM_FORMAT_XRGB8888,
	},
	.overlay_a = {
		.rect = {
			.x = 201, .y = 199,
			.width = 3033, .height = 1777,
		},
		.format = DRM_FORMAT_XRGB16161616,
	},
	.overlay_b = {
		.rect = {
			.x = 1800, .y = 250,
			.width = 1507, .height = 1400,
		},
		.format = DRM_FORMAT_ARGB8888,
	},
	.writeback = {
		.rect = {
			.x = 0, .y = 0,
			// Size is to be determined at runtime
		},
		.format = DRM_FORMAT_XRGB8888,
	},
};


igt_simple_main
{
	struct data_t data = {0};
	enum pipe pipe = PIPE_NONE;
	struct timespec then, now;
	double elapsed;

	data.kms = default_kms;

	data.fd = drm_open_driver_master(DRIVER_ANY);

	kmstest_set_vt_graphics_mode();

	igt_display_require(&data.display, data.fd);
	igt_require(data.display.is_atomic);

	igt_display_require_output(&data.display);

	igt_display_reset(&data.display);

	data.wb_output = find_wb_output(&data);
	igt_require(data.wb_output);

	for_each_pipe(&data.display, pipe) {
		igt_debug("Selecting pipe %s to %s\n",
			  kmstest_pipe_name(pipe),
			  igt_output_name(data.wb_output));
		igt_output_set_pipe(data.wb_output, pipe);
		break;
	}

	set_crtc_size(&data);

	gen_fbs(&data);

	data.kms.primary.base = igt_output_get_plane_type(data.wb_output, DRM_PLANE_TYPE_PRIMARY);
	data.kms.overlay_a.base = igt_output_get_plane_type_index(data.wb_output,
								  DRM_PLANE_TYPE_OVERLAY, 0);
	data.kms.overlay_b.base = igt_output_get_plane_type_index(data.wb_output,
								  DRM_PLANE_TYPE_OVERLAY, 1);

	igt_assert_eq(igt_gettime(&then), 0);

	for (int i = 0; i < FRAME_COUNT; i++) {
		int fb_index = i % NUM_FBS;

		plane_setup(&data.kms.primary, fb_index);

		plane_setup(&data.kms.overlay_a, fb_index);

		plane_setup(&data.kms.overlay_b, fb_index);

		igt_output_set_writeback_fb(data.wb_output, &data.kms.writeback.fbs[fb_index]);

		igt_display_commit2(&data.display, COMMIT_ATOMIC);
	}

	igt_assert_eq(igt_gettime(&now), 0);
	elapsed = igt_time_elapsed(&then, &now);

	igt_info("Time spent in the loop with %d frames: %lfs.\n", FRAME_COUNT, elapsed);

	igt_display_fini(&data.display);
	drm_close_driver(data.fd);
}
