#if 0
Component 0 characteristics: 1024x512x12 unsigned
Component 1 characteristics: 1024x512x12 unsigned
Component 2 characteristics: 1024x512x12 unsigned
Component 3 characteristics: 1024x512x12 unsigned
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int width, height;
short planes[4][2048*1152];
short outplane[2048*1152][4][3];

inline int tobyte(int in, int base)
{
	int rv = in * 255 / base;
	if (rv > 255) {
		rv = 255;
	} else if (rv < 0) {
		rv = 0;
	}
	return rv;
}

void write_out_plane(const char * fname, short * p)
{
	FILE * fp = fopen(fname, "wb");
	int i;

	fprintf(fp, "P6\n# CREATOR: debayer\n%d %d\n255\n",
                width, height);

	for (i = 0; i < width*height; i++) {
		fputc(tobyte(p[i], 4096), fp);
		fputc(tobyte(p[i], 4096), fp);
		fputc(tobyte(p[i], 4096), fp);
	}

	fclose(fp);
}

void ycrcb2rgb(const char * fname1, const char * fname2)
{
	FILE * fp1 = fopen(fname1, "wb");
	FILE * fp2 = fopen(fname2, "wb");

	int i;
	int base = 16384;
	int pix_max = 4096;
	// int Kb = 0.0722 * base;
	// int Kr = 0.2126 * base;
	int Kb = 0.0;
	int Kr = 0.0;

	fprintf(fp1, "P6\n# CREATOR: debayer left output\n%d %d\n255\n",
                width, height);
	fprintf(fp2, "P6\n# CREATOR: debayer right output\n%d %d\n255\n",
                width, height);

	for (i = 0; i < width*height; i++) {
		int i_p = (i > width) ? i-width : i;
		int i_n = i + width;
		int y1n = planes[0][i_n] * (base / pix_max);
		int y1  = planes[0][i]   * (base / pix_max);
		int cb  = planes[1][i]   - pix_max/2;
		int cr  = planes[2][i]   - pix_max/2;
		int y2  = planes[3][i]   * (base / pix_max);
		int y2p = planes[3][i_p] * (base / pix_max);

		int b_ = cb * (base - Kb)/(pix_max/2);
		int r_ = cr * (base - Kr)/(pix_max/2);
		int g_ = (- Kr * r_ - Kb * b_)/(base - Kr - Kb);
		
		int y[4] = {y1, (y2 + y2p)/2, (y1 + y1n)/2, y2};

		int j;

		for (j = 0; j < 4; j++) {
			outplane[i][j][0] = r_ + y[j];
			outplane[i][j][1] = g_ + y[j];
			outplane[i][j][2] = b_ + y[j];
		}

		fputc(tobyte(r_ + y1,base), fp1);
		fputc(tobyte(g_ + y1,base), fp1);
		fputc(tobyte(b_ + y1,base), fp1);

		fputc(tobyte(r_ + y2,base), fp2);
		fputc(tobyte(g_ + y2,base), fp2);
		fputc(tobyte(b_ + y2,base), fp2);
	}

	fclose(fp1);
	fclose(fp2);
}

void naive_debayer(const char * fname)
{
	FILE * fp = fopen(fname, "wb");
	int x,y;
	int r;
	int base = 16384;

	fprintf(fp, "P6\n# CREATOR: naive debayer output\n%d %d\n255\n",
                width * 2, height * 2);

	for (y = 0; y < height; y++) {
		for (x = 0; x < width; x++) {
			fputc(tobyte(outplane[y*width + x][0][0], base), fp);
			fputc(tobyte(outplane[y*width + x][0][1], base), fp);
			fputc(tobyte(outplane[y*width + x][0][2], base), fp);

			fputc(tobyte(outplane[y*width + x][1][0], base), fp);
			fputc(tobyte(outplane[y*width + x][1][1], base), fp);
			fputc(tobyte(outplane[y*width + x][1][2], base), fp);
		}
		for (x = 0; x < width; x++) {
			fputc(tobyte(outplane[y*width + x][2][0], base), fp);
			fputc(tobyte(outplane[y*width + x][2][1], base), fp);
			fputc(tobyte(outplane[y*width + x][2][2], base), fp);

			fputc(tobyte(outplane[y*width + x][3][0], base), fp);
			fputc(tobyte(outplane[y*width + x][3][1], base), fp);
			fputc(tobyte(outplane[y*width + x][3][2], base), fp);
		}
	}
	fclose(fp);
}

void linear_debayer(const char * fname)
{
	FILE * fp = fopen(fname, "wb");
	int x,y;
	int base = 16384;

	fprintf(fp, "P6\n# CREATOR: debayer output\n%d %d\n255\n",
                width * 2, height * 2);

	for (y = 1; y < height -1; y++) {
		for (x = 1; x < width - 1; x++) {
			outplane[y*width + x][1][2]
				= (outplane[y*width + x][0][2]
				   + outplane[y*width + (x+1)][0][2])/2;
			outplane[y*width + x][2][2]
				= (outplane[y*width + x][0][2]
				   + outplane[(y+1)*width + x][0][2])/2;
			outplane[y*width + x][3][2]
				= (outplane[y*width + x][0][2]
				   + outplane[(y+1)*width + x][0][2]
				   + outplane[y*width + x+1][0][2]
				   + outplane[(y+1)*width + x+1][0][2])/4;

			outplane[y*width + x][2][0]
				= (outplane[y*width + x][3][0]
				   + outplane[y*width + (x-1)][3][0])/2;
			outplane[y*width + x][1][0]
				= (outplane[y*width + x][3][0]
				   + outplane[(y-1)*width + x][3][0])/2;
			outplane[y*width + x][0][0]
				= (outplane[y*width + x][3][0]
				   + outplane[(y-1)*width + x][3][0]
				   + outplane[y*width + x-1][3][0]
				   + outplane[(y-1)*width + x-1][3][0])/4;


			outplane[y*width + x][0][1]
				= (outplane[y*width + x][1][1]
				   + outplane[y*width + x][2][1]
				   + outplane[y*width + x-1][1][1]
				   + outplane[(y-1)*width + x][2][1])/4;

			outplane[y*width + x][3][1]
				= (outplane[y*width + x][1][1]
				   + outplane[y*width + x][2][1]
				   + outplane[y*width + x+1][2][1]
				   + outplane[(y+1)*width + x][1][1])/4;
		}
	}

	for (y = 0; y < height; y++) {
		for (x = 0; x < width; x++) {
			fputc(tobyte(outplane[y*width + x][0][0],base), fp);
			fputc(tobyte(outplane[y*width + x][0][1] ,base), fp);
			fputc(tobyte(outplane[y*width + x][0][2] ,base), fp);

			fputc(tobyte(outplane[y*width + x][1][0] ,base), fp);
			fputc(tobyte(outplane[y*width + x][1][1] ,base), fp);
			fputc(tobyte(outplane[y*width + x][1][2] ,base), fp);
		}
		for (x = 0; x < width; x++) {
			fputc(tobyte(outplane[y*width + x][2][0] ,base), fp);
			fputc(tobyte(outplane[y*width + x][2][1] ,base), fp);
			fputc(tobyte(outplane[y*width + x][2][2] ,base), fp);

			fputc(tobyte(outplane[y*width + x][3][0] ,base), fp);
			fputc(tobyte(outplane[y*width + x][3][1] ,base), fp);
			fputc(tobyte(outplane[y*width + x][3][2] ,base), fp);
		}
	}
	fclose(fp);
}

int main(int argc, const char ** argv)
{
	int i;
	int byteswap = 0;

	if (argc < 3) {
		fprintf(stderr, "Usage: debayer [width] [height] [options...] < frame.raw\n"
			"options: byteswap: swap bytes on input\n");
		return -1;
	}

	width = atol(argv[1]);
	height = atol(argv[2]);

	if (argc == 4 && strcmp(argv[3], "byteswap") == 0) {
		byteswap = 1;
	}

	for (i = 0; i <4; i++) {
		if (fread(planes[i], 2, width*height, stdin) != width*height) {
			fprintf(stderr, "short read\n");
			return -1;
		}
		if (byteswap) {
			int j;
			for (j = 0; j < width * height; j++) {
				unsigned short x = 
					((unsigned short*) planes[i])[j];
				((unsigned short*) planes[i])[j] =
					 ((x >> 8) & 0xff) 
					| ((x << 8) & 0xff00);
			}
		}
	}

	write_out_plane("1.ppm", planes[0]);
	write_out_plane("2.ppm", planes[1]);
	write_out_plane("3.ppm", planes[2]);
	write_out_plane("4.ppm", planes[3]);

	ycrcb2rgb("rgb1.ppm", "rgb2.ppm");
	naive_debayer("naive.ppm");
	/* linear_debayer("linear.ppm"); */

	return 0;
}

