#include <stdlib.h>
#include <linux/v4l2-subdev.h>
#include <strings.h>
#include <assert.h>
#include <stdio.h>
#include "mode.h"
#include "libmegapixels.h"


// TODO: The 16 bit formats are imported from millipixels and seem broken
static struct libmegapixels_modename mode_lut[] = {
	{
		.name = "unsupported",
		.v4l_pixel_format = 0,
		.media_bus_format = 0,
		.bpc = 0,
		.bpp = 0,
		.cfa = LIBMEGAPIXELS_CFA_NONE,
	},
	{
		.name = "BGGR8",
		.v4l_pixel_format = V4L2_PIX_FMT_SBGGR8,
		.media_bus_format = MEDIA_BUS_FMT_SBGGR8_1X8,
		.bpc = 8,
		.bpp = 8,
		.cfa = LIBMEGAPIXELS_CFA_BGGR,
	},
	{
		.name = "GBRG8",
		.v4l_pixel_format = V4L2_PIX_FMT_SGBRG8,
		.media_bus_format = MEDIA_BUS_FMT_SGBRG8_1X8,
		.bpc = 8,
		.bpp = 8,
		.cfa = LIBMEGAPIXELS_CFA_GBRG,
	},
	{
		.name = "GRBG8",
		.v4l_pixel_format = V4L2_PIX_FMT_SGRBG8,
		.media_bus_format = MEDIA_BUS_FMT_SGRBG8_1X8,
		.bpc = 8,
		.bpp = 8,
		.cfa = LIBMEGAPIXELS_CFA_GRBG,
	},
	{
		.name = "RGGB8",
		.v4l_pixel_format = V4L2_PIX_FMT_SRGGB8,
		.media_bus_format = MEDIA_BUS_FMT_SRGGB8_1X8,
		.bpc = 8,
		.bpp = 8,
		.cfa = LIBMEGAPIXELS_CFA_RGGB,
	},
	{
		.name = "BGGR10P",
		.v4l_pixel_format = V4L2_PIX_FMT_SBGGR10P,
		.media_bus_format = MEDIA_BUS_FMT_SBGGR10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_BGGR,
	},
	{
		.name = "GBRG10P",
		.v4l_pixel_format = V4L2_PIX_FMT_SGBRG10P,
		.media_bus_format = MEDIA_BUS_FMT_SGBRG10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_GBRG,
	},
	{
		.name = "GRBG10P",
		.v4l_pixel_format = V4L2_PIX_FMT_SGRBG10P,
		.media_bus_format = MEDIA_BUS_FMT_SGRBG10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_GRBG,
	},
	{
		.name = "RGGB10P",
		.v4l_pixel_format = V4L2_PIX_FMT_SRGGB10P,
		.media_bus_format = MEDIA_BUS_FMT_SRGGB10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_RGGB,
	},
	{
		.name = "BGGR10",
		.v4l_pixel_format = V4L2_PIX_FMT_SBGGR10,
		.media_bus_format = MEDIA_BUS_FMT_SBGGR10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_BGGR,
	},
	{
		.name = "GBRG10",
		.v4l_pixel_format = V4L2_PIX_FMT_SGBRG10,
		.media_bus_format = MEDIA_BUS_FMT_SGBRG10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_GBRG,
	},
	{
		.name = "GRBG10",
		.v4l_pixel_format = V4L2_PIX_FMT_SGRBG10,
		.media_bus_format = MEDIA_BUS_FMT_SGRBG10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_GRBG,
	},
	{
		.name = "RGGB10",
		.v4l_pixel_format = V4L2_PIX_FMT_SRGGB10,
		.media_bus_format = MEDIA_BUS_FMT_SRGGB10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_RGGB,
	},
	{
		.name = "BGGR12",
		.v4l_pixel_format = V4L2_PIX_FMT_SBGGR12,
		.media_bus_format = MEDIA_BUS_FMT_SBGGR12_1X12,
		.bpc = 12,
		.bpp = 12,
		.cfa = LIBMEGAPIXELS_CFA_BGGR,
	},
	{
		.name = "GBRG12",
		.v4l_pixel_format = V4L2_PIX_FMT_SGBRG12,
		.media_bus_format = MEDIA_BUS_FMT_SGBRG12_1X12,
		.bpc = 12,
		.bpp = 12,
		.cfa = LIBMEGAPIXELS_CFA_GBRG,
	},
	{
		.name = "GRBG12",
		.v4l_pixel_format = V4L2_PIX_FMT_SGRBG12,
		.media_bus_format = MEDIA_BUS_FMT_SGRBG12_1X12,
		.bpc = 12,
		.bpp = 12,
		.cfa = LIBMEGAPIXELS_CFA_GRBG,
	},
	{
		.name = "RGGB12",
		.v4l_pixel_format = V4L2_PIX_FMT_SRGGB12,
		.media_bus_format = MEDIA_BUS_FMT_SRGGB12_1X12,
		.bpc = 12,
		.bpp = 12,
		.cfa = LIBMEGAPIXELS_CFA_RGGB,
	},
	{
		.name = "BGGR16",
		.v4l_pixel_format = V4L2_PIX_FMT_SBGGR16,
		.media_bus_format = MEDIA_BUS_FMT_SBGGR10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_BGGR,
	},
	{
		.name = "GBRG16",
		.v4l_pixel_format = V4L2_PIX_FMT_SGBRG16,
		.media_bus_format = MEDIA_BUS_FMT_SGBRG10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_GBRG,
	},
	{
		.name = "GRBG16",
		.v4l_pixel_format = V4L2_PIX_FMT_SGRBG16,
		.media_bus_format =MEDIA_BUS_FMT_SGRBG10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_GRBG,
	},
	{
		.name = "RGGB16",
		.v4l_pixel_format = V4L2_PIX_FMT_SRGGB16,
		.media_bus_format = MEDIA_BUS_FMT_SRGGB10_1X10,
		.bpc = 10,
		.bpp = 10,
		.cfa = LIBMEGAPIXELS_CFA_RGGB,
	},
	{
		.name = "UYVY",
		.v4l_pixel_format = V4L2_PIX_FMT_UYVY,
		.media_bus_format = MEDIA_BUS_FMT_UYVY8_2X8,
		.bpc = 8,
		.bpp = 16,
		.cfa = LIBMEGAPIXELS_CFA_NONE,
	},
	{
		.name = "YUYV",
		.v4l_pixel_format = V4L2_PIX_FMT_YUYV,
		.media_bus_format = MEDIA_BUS_FMT_YUYV8_2X8,
		.bpc = 8,
		.bpp = 16,
		.cfa = LIBMEGAPIXELS_CFA_NONE,
	},
	{
		.name = "VYUY",
		.v4l_pixel_format = V4L2_PIX_FMT_VYUY,
		.media_bus_format = MEDIA_BUS_FMT_VYUY8_2X8,
		.bpc = 8,
		.bpp = 16,
		.cfa = LIBMEGAPIXELS_CFA_NONE,
	},
	{
		.name = "YVYU",
		.v4l_pixel_format = V4L2_PIX_FMT_YVYU,
		.media_bus_format = MEDIA_BUS_FMT_YVYU8_2X8,
		.bpc = 8,
		.bpp = 16,
		.cfa = LIBMEGAPIXELS_CFA_NONE,
	},
};

int
libmegapixels_format_name_to_index(const char *name)
{
	int count = sizeof(mode_lut) / sizeof(mode_lut[0]);
	for (int i = 0; i < count; i++) {
		if (strcasecmp(mode_lut[i].name, name) == 0) {
			return i;
		}
	}
	return 0;
}

uint32_t
libmegapixels_format_to_v4l_pixfmt(int index)
{
	return mode_lut[index].v4l_pixel_format;
}

uint32_t
libmegapixels_format_to_media_busfmt(int index)
{
	return mode_lut[index].media_bus_format;
}

int
libmegapixels_v4l_pixfmt_to_index(uint32_t pixfmt)
{
	int count = sizeof(mode_lut) / sizeof(mode_lut[0]);
	for (int i = 0; i < count; i++) {
		if (mode_lut[i].v4l_pixel_format == pixfmt) {
			return i;
		}
	}
	return 0;
}

char *
libmegapixels_v4l_pixfmt_to_string(uint32_t pixfmt)
{
	int count = sizeof(mode_lut) / sizeof(mode_lut[0]);
	for (int i = 0; i < count; i++) {
		if (mode_lut[i].v4l_pixel_format == pixfmt) {
			return mode_lut[i].name;
		}
	}
	return "unknown";
}

char *
libmegapixels_format_name(int format)
{
	return mode_lut[format].name;
}

// mp_pixel_format_bits_per_pixel
uint32_t
libmegapixels_format_bits_per_pixel(int format)
{
	return mode_lut[format].bpp;
}

const char *
libmegapixels_format_cfa(int format)
{
	switch (mode_lut[format].cfa) {
		case LIBMEGAPIXELS_CFA_BGGR:
			return "BGGR";
		case LIBMEGAPIXELS_CFA_GBRG:
			return "GBRG";
		case LIBMEGAPIXELS_CFA_GRBG:
			return "GRBG";
		case LIBMEGAPIXELS_CFA_RGGB:
			return "RGGB";
		default:
			return NULL;
	}
}

const char *
libmegapixels_format_cfa_pattern(int format)
{
	switch (mode_lut[format].cfa) {
		case LIBMEGAPIXELS_CFA_BGGR:
			return "\002\001\001\000";
		case LIBMEGAPIXELS_CFA_GBRG:
			return "\001\002\000\001";
		case LIBMEGAPIXELS_CFA_GRBG:
			return "\001\000\002\001";
		case LIBMEGAPIXELS_CFA_RGGB:
			return "\000\001\001\002";
		default:
			return NULL;
	}
}

// Useful for the calculation: https://www.1stvision.com/cameras/IDS/IDS-manuals/en/basics-raw-bayer-pixel-formats.html
// Note that BayerGB12 is packed according to that document, but this is likely a mistake
uint32_t
libmegapixels_mode_width_to_bytes(int index, uint32_t width)
{
	uint32_t bits_per_pixel = mode_lut[index].bpp;
	// For bpp like 10/12/14, for formats that are not packed, we want to set bpp to something like 16
	if (bits_per_pixel % 8 != 0 && !libmegapixels_mode_is_packed(index))
		bits_per_pixel = bits_per_pixel + 8 - (bits_per_pixel % 8);
	uint64_t bits_per_width = width * (uint64_t) bits_per_pixel;
	uint64_t remainder = bits_per_width % 8;
	if (remainder == 0)
		return bits_per_width / 8;

	return (bits_per_width + 8 - remainder) / 8;
}

uint32_t
libmegapixels_mode_width_to_padding(int index, uint32_t width)
{
	if (mode_lut[index].bpp == 8) {
		return 0;
	}
	uint64_t bytes_per_width = libmegapixels_mode_width_to_bytes(index, width);
	uint64_t remainder = bytes_per_width % 8;
	if (remainder == 0)
		return remainder;

	return 8 - remainder;
}

uint32_t
libmegapixels_mode_raw_width_to_width(int index, uint32_t width)
{
	switch (mode_lut[index].v4l_pixel_format) {
		case V4L2_PIX_FMT_SBGGR8:
		case V4L2_PIX_FMT_SGBRG8:
		case V4L2_PIX_FMT_SGRBG8:
		case V4L2_PIX_FMT_SRGGB8:
			return width / 2;
		case V4L2_PIX_FMT_SBGGR10P:
		case V4L2_PIX_FMT_SGBRG10P:
		case V4L2_PIX_FMT_SGRBG10P:
		case V4L2_PIX_FMT_SRGGB10P:
			return width / 2 * 5;
		case V4L2_PIX_FMT_UYVY:
		case V4L2_PIX_FMT_YUYV:
			return width;
		default:
			return 0;
	}
}

uint32_t
libmegapixels_mode_raw_height_to_height(int index, uint32_t height)
{
	switch (mode_lut[index].v4l_pixel_format) {
		case V4L2_PIX_FMT_SBGGR8:
		case V4L2_PIX_FMT_SGBRG8:
		case V4L2_PIX_FMT_SGRBG8:
		case V4L2_PIX_FMT_SRGGB8:
		case V4L2_PIX_FMT_SBGGR10P:
		case V4L2_PIX_FMT_SGBRG10P:
		case V4L2_PIX_FMT_SGRBG10P:
		case V4L2_PIX_FMT_SRGGB10P:
			return height / 2;
		case V4L2_PIX_FMT_UYVY:
		case V4L2_PIX_FMT_YUYV:
			return height;
		default:
			return 0;
	}
}

int
libmegapixels_mode_equals(libmegapixels_mode *a, libmegapixels_mode *b)
{
	if (a == NULL || b == NULL) {
		return 0;
	}
	if (a->width != b->width) {
		return 0;
	}
	if (a->height != b->height) {
		return 0;
	}
	if (a->rate != b->rate) {
		return 0;
	}
	if (a->format != b->format) {
		return 0;
	}
	return 1;
}

int
libmegapixels_mode_is_packed(int index)
{
	switch (mode_lut[index].v4l_pixel_format) {
		case V4L2_PIX_FMT_SBGGR10P:
		case V4L2_PIX_FMT_SGBRG10P:
		case V4L2_PIX_FMT_SGRBG10P:
		case V4L2_PIX_FMT_SRGGB10P:
			return 1;
		default:
			return 0;
	}
}

int
mode_snprintf(char *buf, size_t maxlen, libmegapixels_mode *mode)
{
	return snprintf(buf, maxlen, "%dx%d@%d %s", mode->width, mode->height, mode->rate, mode_lut[mode->format].name);
}