//============================================================================
// Name        : plates.cpp
// Author      : Alexander
// Version     :
// Copyright   : planned to be GPL.
// Description : Plates recognition
//============================================================================

#include "plates.h"

#include <stdio.h>
#include "GBuffer.h"
#include "HaarWavelet.h"
#include "AffineTransform.h"
#include "math.h"

#define BORDER			10
#define EXPECTED_RECT_H 35
#define EXPECTED_RECT_W 170

#define WORK_RECT_H 40
#define WORK_RECT_W 172

#define ADD_H ((WORK_RECT_H - EXPECTED_RECT_H) / 2)
#define ADD_W ((WORK_RECT_W - EXPECTED_RECT_W) / 2)

//                                    m                    7                     8                 8                   x                  b
//                           0                   10                  20                  30                  40                  50                  60                  70                  80
int russianPlate_data[82] = {2,2,2,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2};

/*
int russianPlate_data[82] = {2,2,1,0,0,0,0,0,0,0,1,2,2,2,1,0,0,0,0,0,1,2,2,2,1,0,0,0,0,1,2,2,2,1,0,0,0,0,1,2,2,2,1,0,0,0,0,0,0,1,2,2,2,1,0,0,0,0,0,1,2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2};
*/
Hystogramm *russianPlate = new Hystogramm(82, russianPlate_data);


int fetchNewFrame (AVFrame *pFrame, AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx, int videoStream)
{
	AVPacket packet;
	int frameFinished;

	if (av_read_frame(pFormatCtx, &packet) < 0)
		return 0;

	// Is this a packet from the video stream?
	if (packet.stream_index == videoStream) {
		// Decode video frame
		avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
				packet.data, packet.size);
	}
	av_free_packet(&packet);
	return frameFinished;

}

int done = 0;
int pause = 0;
int state = 0;
int redraw = 1;

int lowcut=9, highcut=54;


int processEvent (SDL_Event *event){

	switch (event->type) {
		case SDL_QUIT:
			SDL_Quit();
			exit(0);
			break;
		case SDL_KEYDOWN:
			switch (event->key.keysym.sym)
			{
				case SDLK_SPACE:
						pause = ~pause;
						redraw = 1;
					break;
				case SDLK_1:
						state = (state + 1) % 6;
						redraw = 1;

					break;
				case SDLK_2:
						state = (state + 5) % 6;
						redraw = 1;

					break;
				case SDLK_w:
						lowcut++;
						printf("lowcut=%d\n", lowcut);
						redraw = 1;

					break;
				case SDLK_s:
						lowcut--;
						printf("lowcut=%d\n", lowcut);
						redraw = 1;
					break;
				case SDLK_e:
						highcut++;
						printf("highcut=%d\n", highcut);
						redraw = 1;
					break;
				case SDLK_d:
						highcut--;
						printf("highcut=%d\n", highcut);
						redraw = 1;
					break;
				case SDLK_ESCAPE:
				case SDLK_q:
						printf("GBuffers left in memory = %d", GBuffer::count);
						SDL_Quit();
						exit(0);
					break;
			}
			break;
		case SDL_MOUSEBUTTONDOWN:
//			x1 = event.motion.x;
//			y1 = event.motion.y;
			break;
		case SDL_MOUSEBUTTONUP:
//			x2 = event.motion.x;
//			y2 = event.motion.y;
			break;
		default:
			break;
	}
}

//#define SDL_main main
int main(int argc, char *argv[]) {
	AVFormatContext *pFormatCtx;
	int i, videoStream;
	AVCodecContext *pCodecCtx;
	AVCodec *pCodec;
	AVFrame *pFrame;


	SDL_Surface *screen;
	SDL_Event event;

	if (argc < 2) {
		fprintf(stderr, "Usage: test <file>\n");
		exit(1);
	}
	// Register all formats and codecs
	av_register_all();

	if (SDL_Init(SDL_INIT_VIDEO| SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
		exit(1);
	}

	// Open video file
	if (av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL) != 0)
		return -1; // Couldn't open file

	// Retrieve stream information
	if (av_find_stream_info(pFormatCtx) < 0)
		return -1; // Couldn't find stream information

	// Dump information about file onto standard error
	dump_format(pFormatCtx, 0, argv[1], 0);

	// Find the first video stream
	videoStream = -1;
	for (i = 0; i < pFormatCtx->nb_streams; i++)
		if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
			videoStream = i;
			break;
		}
	if (videoStream == -1)
		return -1; // Didn't find a video stream

	// Get a pointer to the codec context for the video stream
	pCodecCtx = pFormatCtx->streams[videoStream]->codec;

	// Find the decoder for the video stream
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if (pCodec == NULL) {
		fprintf(stderr, "Unsupported codec!\n");
		return -1; // Codec not found
	}

	// Open codec
	if (avcodec_open(pCodecCtx, pCodec) < 0)
		return -1; // Could not open codec

	// Allocate video frame
	pFrame = avcodec_alloc_frame();

	// Make a screen to put our video
	screen = SDL_SetVideoMode(pCodecCtx->width + 100, pCodecCtx->height + 2 * WORK_RECT_H + WORK_RECT_W, 0, 0);
	if (!screen) {
		fprintf(stderr, "SDL: could not set video mode - exiting\n");
		exit(1);
	}

	int frameH =  pCodecCtx->height;
	int frameW =  pCodecCtx->width;
	AffineTransform shiftr = AffineTransform::getShift   (frameW / 2, frameH / 2);
	AffineTransform rot1   = AffineTransform::getRotation(0.1 / 180 * M_PI);
	AffineTransform shiftl = AffineTransform::getShift   (-frameW / 2, -frameH / 2);
	AffineTransform rot = shiftr.multiplyBy(rot1).multiplyBy(shiftl);

	// Read frames and save first five frames to disk
	i = 0;


	while (!done)
	{
		redraw = 0;
		if (pause)
		{
			SDL_WaitEvent(&event);
			processEvent(&event);
		}
		else
			redraw = 1;

		while (SDL_PollEvent(&event))
		{
			processEvent(&event);
		}

		if (!redraw)
			continue;

		if (!pause)
		{
			int fetched = fetchNewFrame(pFrame, pFormatCtx, pCodecCtx, videoStream);
                        if (fetched == 0)
			{
                            continue;
				SDL_Quit();
				exit(0);
			} else if (fetched == -1)
                            continue;

		}

		SDL_FillRect(screen, NULL, 0x202020);

		int platex, platey;

		GBuffer *yarst = new GBuffer((AVPicture *) pFrame, pCodecCtx->height, pCodecCtx->width);
		GBuffer *yar = rot.apply(yarst);
		delete yarst;

		GBuffer *gauss = yar->filterGaussian();
		GBuffer *orcode;
		GBuffer *sobelH = gauss->filterSobelOrientation(&orcode);
		GBuffer *result = sobelH->suppressNonMaximum(lowcut, highcut, orcode);
		delete orcode;
		GBuffer *haar = (new HaarWaveletFilter(result))->filter(EXPECTED_RECT_H,EXPECTED_RECT_W, BORDER, &platex, &platey);
		platex += BORDER;
		platey += BORDER;

		GBuffer *platebuf1 = new GBuffer(yar, platex - ADD_W, platey - ADD_H, platex - ADD_W + WORK_RECT_W, platey - ADD_H + WORK_RECT_H);
		Hystogramm *hyst = platebuf1->getWHyst();
		int threshold = ((hyst->getMin() / WORK_RECT_H) + hyst->getMax() / (WORK_RECT_H)) / 2;
		GBuffer *platebuf2 = platebuf1->threshold(threshold);
		Hystogramm *hyst1 = platebuf2->getWHyst();

		int start;
		int end;
		GBuffer *corelbuf = hyst1->matchWith(russianPlate, &start, &end);

		if (state == 0)
		{
			yar->drawRect(platex, platey, platex + 170, platey + 35);
			yar->drawInto(screen, 0, 0);

		}
		else if (state == 1)
		{
			gauss->drawInto(screen, 0, 0);
		}
		/*else if (state == 2)
		{
			GBuffer *haar = (new HaarWaveletFilter(yar))->filter(35,170);
			haar->drawInto(frame);
			delete haar;
		}*/
		else if (state == 3)
		{
			haar->drawInto(screen, 0,0);
		}
		else if (state == 4)
		{
			result->drawInto(screen, 0,0);
		}
		else
			sobelH->drawInto(screen, 0,0);

		russianPlate->drawHInto(screen, WORK_RECT_H, WORK_RECT_W, WORK_RECT_W * 2, frameH);
		hyst1->       drawHInto(screen, WORK_RECT_H, WORK_RECT_W, WORK_RECT_W * 3, frameH);

		russianPlate->drawHInto(screen, WORK_RECT_H, end-start, start, frameH + WORK_RECT_H );
		//printf("Interval [%d,%d]\n", start, end);






		platebuf2->drawInto(screen, 0, frameH);
		//corelbuf->drawInto(screen, WORK_RECT_W * 2, frameH + WORK_RECT_H);


		//SDL_BlitSurface(frame, &frameRect, screen, &screenRect1);
		//SDL_BlitSurface(hystog, &frameRect, screen, &screenRect4);
		//SDL_BlitSurface(hystog1, &frameRect, screen, &screenRect2);
		//SDL_BlitSurface(plate, &plateRect, screen, &screenRect3);
		//SDL_BlitSurface(corel, &corelRect, screen, &screenRect5);


		SDL_UpdateRect(screen,0,0,screen->w, screen->h);

		delete corelbuf;
		delete yar;
		delete sobelH;
		delete gauss;
		delete hyst;
		delete hyst1;
		delete platebuf2;
		delete platebuf1;
		delete result;
		delete haar;
	}

	// Free the YUV frame
	av_free(pFrame);

	// Close the codec
	avcodec_close(pCodecCtx);

	// Close the video file
	av_close_input_file(pFormatCtx);

	printf("\nOk. Video Ended\n");
	return 0;
}
