/*
 *    Copyright (C) 2001 Robert Kesterson <robertk@robertk.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *    This utility is based on "yuvmedianfilter.c" from mjpegtools.
 * 	 (basically used it as a framework and just replaced the filter part).
 *
 *    This filter does a 3:2 pulldown on a video...  hopefully useful for
 *    converting PAL to NTSC.  You also need to convert the audio, using a 
 *    sox command like:
 *    sox -r 46080 -t wav audio.wav -t wav -r 44100 fixedaudio.wav
 *    (or of course the appropriate pipes from/to whatever you're using).
 *
 *    Many thanks to Andrew Stevens for the 3-2 pulldown info
 *
 */
#include <config.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "yuv4mpeg.h"
#include "mjpeg_logging.h"


int verbose = 1;
unsigned char   *input_frame[3];
unsigned char   *output_frame[3];
unsigned char *saved0[3], *saved1[3];
y4m_stream_info_t istream, ostream;
y4m_frame_info_t iframe;
int frame_count;
int output_fd = 1;

void    filter(int width, int height, unsigned char *input[], unsigned char *output[]);


static void Usage(char *name )
{
	fprintf(stderr,
			"Usage: %s: [-h] [-b num] [-v num]\n"
			"-h   - Print out this help\n"
			"-v   - Verbosity [0..2]\n", name);
}

int
main(int argc, char *argv[])
{
	int i;
	int input_fd = 0;
	int horz;
	int vert;
	int c;
	int chromasize;
	y4m_ratio_t ratio;

	while ((c = getopt(argc, argv, "v:h")) != EOF)
	{
		switch (c)
		{
			case 'v':
				verbose = atoi (optarg);
				if ( verbose < 0 || verbose >2 )
				{
					Usage (argv[0]);
					exit (1);
				}
				break;        

			case 'h':
				Usage (argv[0]);
			default:
				exit(0);
		}
	}

	(void)mjpeg_default_handler_verbosity(verbose);

	y4m_init_stream_info(&istream);
	y4m_init_stream_info(&ostream);
	y4m_init_frame_info(&iframe);

	i = y4m_read_stream_header(input_fd, &istream);
	if (i != Y4M_OK)
		mjpeg_error_exit1("Input stream error: %s\n", y4m_strerr(i));

	horz = istream.width;
	vert = istream.height;
	mjpeg_debug( "width=%d height=%d\n", horz, vert);

	y4m_copy_stream_info(&ostream, &istream);
	ratio = y4m_si_get_framerate(&ostream);
	// calculate new frame rate -- ie, for 25-fps, becomes 30-fps.
	ratio.n *= 6;
	ratio.n /= 5;
	ratio.d *= 6;
	ratio.d /= 5;
	y4m_si_set_framerate(&ostream, ratio);

	chromasize = (horz / 2) * (vert / 2);
	input_frame[0] = (unsigned char *)alloca(horz * vert);
	input_frame[1] = (unsigned char *)alloca(chromasize);
	input_frame[2] = (unsigned char *)alloca(chromasize);

	output_frame[0] = (unsigned char *)alloca(horz * vert);
	output_frame[1] = (unsigned char *)alloca(chromasize);
	output_frame[2] = (unsigned char *)alloca(chromasize);

	saved0[0] = (unsigned char *)alloca(horz * vert);
	saved0[1] = (unsigned char *)alloca(chromasize);
	saved0[2] = (unsigned char *)alloca(chromasize);
	saved1[0] = (unsigned char *)alloca(horz * vert);
	saved1[1] = (unsigned char *)alloca(chromasize);
	saved1[2] = (unsigned char *)alloca(chromasize);

	y4m_write_stream_header(output_fd, &ostream);

	frame_count = 0;
	while (y4m_read_frame(input_fd, &istream, &iframe, input_frame) == Y4M_OK)
	{
		filter(horz, vert, input_frame, output_frame);
		mjpeg_info("\rframes=%d", ++frame_count);
	}

	mjpeg_info("\nProcessed %d frames\n", frame_count);
	y4m_fini_stream_info(&istream);
	y4m_fini_stream_info(&ostream);
	y4m_fini_frame_info(&iframe);
	exit(0);
}


void CopyField(int which, int width, int height, unsigned char *input[], unsigned char *output[])
{
	// "which" is 0 for top field, 1 for bottom field
	int r = 0;
	int chromasize = width / 2;
	for (r = which; r < height; r += 2)
	{
		int chromapos = (r / 2) * (width / 2); 
		memcpy(&output[0][r * width], &input[0][r * width], width);
		memcpy(&output[1][chromapos], &input[1][chromapos], chromasize);
		memcpy(&output[2][chromapos], &input[2][chromapos], chromasize);
	}
}

/*
3-2 pulldown info from Andrew Stevens

24 frame/sec Movies encoded as PAL using 2-2 pulldown (a fancy way of saying 
they're played 25/24% too fast - audio too) are dead easy to turn into NTSC.
Pretend they run at 24 frame/sec and do 3-2 pulldown:

Out                             In
0: Top field 0 Bot field 0              0: Top field 0 Bot field 0
1:  Top field 0 Bot field 1             1: Top field 1 Bot field 1
2: Top field 1 Bot field 2              2: Top field 2 Bot field 2
3:  Top field 2 Bot field 2             3: Top field 3 Bot field 3
4:  Top field 3 Bot field 3
*/                     
void filter(int width, int height, unsigned char *input[], unsigned char *output[])
{
	int chromasize = (width / 2) * (height / 2);

	switch (frame_count % 4)
	{
		case 0:
			// save for pulldown
			memcpy(saved0[0], input[0], width * height);
			memcpy(saved0[1], input[1], chromasize);
			memcpy(saved0[2], input[2], chromasize);
			// and write current
			memcpy(output[0], input[0], width * height);
			memcpy(output[1], input[1], chromasize);
			memcpy(output[2], input[2], chromasize);
			y4m_write_frame(output_fd, &ostream, &iframe, output);
			break;
		case 1:
			// save frame for pulldown on next frame
			memcpy(saved1[0], input[0], width * height);
			memcpy(saved1[1], input[1], chromasize);
			memcpy(saved1[2], input[2], chromasize);
			break;
		case 2:
			// do pulldown...  
			// 1:  Top field 0 Bot field 1
			CopyField(0, width, height, saved0, output);
			CopyField(1, width, height, saved1, output);
			y4m_write_frame(output_fd, &ostream, &iframe, output);
			// 2: Top field 1 Bot field 2
			CopyField(0, width, height, saved1, output);
			CopyField(1, width, height, input, output);
			y4m_write_frame(output_fd, &ostream, &iframe, output);
			// 3:  Top field 2 Bot field 2 (basically a pass through)
			memcpy(output[0], input[0], width * height);
			memcpy(output[1], input[1], chromasize);
			memcpy(output[2], input[2], chromasize);
			y4m_write_frame(output_fd, &ostream, &iframe, output);
			break;
		case 3:
		default:
			// output frame
			memcpy(output[0], input[0], width * height);
			memcpy(output[1], input[1], chromasize);
			memcpy(output[2], input[2], chromasize);
			y4m_write_frame(output_fd, &ostream, &iframe, output);
			break;
	}
}


