/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2025, Bryan O'Donoghue.
 *
 * Authors:
 * Bryan O'Donoghue <bryan.odonoghue@linaro.org>
 *
 */

#pragma once

#include <deque>
#include <memory>
#include <stdint.h>
#include <tuple>
#include <vector>

#define GL_GLEXT_PROTOTYPES
#define EGL_EGLEXT_PROTOTYPES
#include <libcamera/base/object.h>

#include "libcamera/internal/egl.h"
#include "libcamera/internal/mapped_framebuffer.h"
#include "libcamera/internal/software_isp/benchmark.h"
#include "libcamera/internal/software_isp/swstats_cpu.h"

#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl32.h>

#include "debayer.h"

namespace libcamera {

#define DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS 4
#define DEBAYER_OPENGL_COORDS 4

class CameraManager;

class DebayerEGL : public Debayer
{
public:
	DebayerEGL(std::unique_ptr<SwStatsCpu> stats, const CameraManager &cm);
	~DebayerEGL();

	int configure(const StreamConfiguration &inputCfg,
		      const std::vector<std::reference_wrapper<const StreamConfiguration>> &outputCfgs,
		      bool ccmEnabled) override;

	Size patternSize(PixelFormat inputFormat) override;

	std::vector<PixelFormat> formats(PixelFormat input) override;
	std::tuple<unsigned int, unsigned int> strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) override;
	uint32_t preferredInputStride(const PixelFormat &inputFormat, const Size &size) override;

	void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, const DebayerParams &params) override;
	int start() override;
	void stop() override;

	const SharedFD &getStatsFD() override { return stats_->getStatsFD(); }

	SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) override;

private:
	static int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
	static int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
	int initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat);
	int getShaderVariableLocations();
	void setShaderVariableValues(eGLImage &eGLImageIn, const DebayerParams &params);
	int debayerGPU(FrameBuffer *input, FrameBuffer *output, const DebayerParams &params, std::optional<MappedFrameBuffer> *mappedInputBuffer, std::optional<DmaSyncer> *inputBufferDmaSyncer);

	eGLImage *getCachedInputFrameBuffer(FrameBuffer *input, std::optional<MappedFrameBuffer> *inMapped, std::optional<DmaSyncer> *inDmaSyncer);
	eGLImage *getCachedOutputFrameBuffer(FrameBuffer *output);

	/* Shader program identifiers */
	GLuint vertexShaderId_ = 0;
	GLuint fragmentShaderId_ = 0;
	GLuint programId_ = 0;

	/* Pointer to object representing input texture */
	std::deque<std::pair<SharedFD, std::unique_ptr<eGLImage>>> eglImageInCache_;
	std::deque<std::pair<SharedFD, std::unique_ptr<eGLImage>>> eglImageOutCache_;
	unsigned int inputBufferCount_;
	unsigned int outputBufferCount_;

	/* Shader parameters */
	float firstRed_x_;
	float firstRed_y_;
	GLint attributeVertex_;
	GLint attributeTexture_;
	GLint textureUniformStep_;
	GLint textureUniformSize_;
	GLint textureUniformStrideFactor_;
	GLint textureUniformBayerFirstRed_;
	GLint textureUniformProjMatrix_;

	GLint textureUniformBayerDataIn_;

	/* Per-frame AWB gains */
	GLint awbUniformDataIn_;

	/* Represent per-frame CCM as a uniform vector of floats 3 x 3 */
	GLint ccmUniformDataIn_;

	/* Black Level compensation */
	GLint blackLevelUniformDataIn_;

	/* Gamma */
	GLint gammaUniformDataIn_;

	/* Contrast */
	GLint contrastExpUniformDataIn_;

	Size nativeOutputSize_;
	Rectangle window_;
	std::unique_ptr<SwStatsCpu> stats_;
	eGL egl_;
	uint32_t width_;
	uint32_t height_;
	GLint glFormat_;
	unsigned int bytesPerPixel_;
	uint32_t shaderStridePixels_;
};

} /* namespace libcamera */
