#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <stdarg.h>
#include <time.h>

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#include <vdr/tools.h>

#include "iframe.h"

#define	_EM84_YUV_FRAME_WIDTH  720
#define	_EM84_YUV_FRAME_HEIGHT 480

void cIFrameDecoder::InitFFMpeg(void)
{
	avcodec_init();
	// register all the codecs (you can also register only the codec
	// you wish to have smaller code)
	avcodec_register_all();
}

cIFrameDecoder::cIFrameDecoder()
{
	mInitialized = false;
	mFrameWidth = -1;
	mFrameHeight = -1;
	mFrameValid = false;
}

bool cIFrameDecoder::Init(void)
{
	mMpeg2Codec = avcodec_find_decoder(CODEC_ID_MPEG2VIDEO);
	if (!mMpeg2Codec)
	{
		esyslog("em84xx: failed to find ffmpeg mpeg2 video decoder");
		return false;
	}
	mCodecContext = avcodec_alloc_context();
	if (avcodec_open(mCodecContext, mMpeg2Codec) < 0)
	{
		esyslog("em84xx: failed to open ffmpeg mpeg2 video codec");
		return false;
	}

	mDecodedFrame = avcodec_alloc_frame();
	mDecodedYUVFrame = avcodec_alloc_frame();
	mYUVFrameBuffer = NULL;
	//mResizeFrame  = avcodec_alloc_frame();
	//mBufferResize = new unsigned char[avpicture_get_size(PIX_FMT_YUV420P, _DECODE_FRAME_WIDTH, _DECODE_FRAME_HEIGHT)];
	//avpicture_fill((AVPicture *)mResizeFrame, mBufferResize, PIX_FMT_YUV420P, _DECODE_FRAME_WIDTH, _DECODE_FRAME_HEIGHT);

	mInitialized = true;
	return true;
}

void cIFrameDecoder::Release(void)
{
	av_free(mDecodedFrame);
	av_free(mDecodedYUVFrame);
	//av_free(mResizeFrame);
	//delete[] mBufferResize;
	avcodec_close(mCodecContext);
	if (mYUVFrameBuffer)
	{
		delete[] mYUVFrameBuffer;
		mYUVFrameBuffer = NULL;
	}
	//free(mCodecContext);
	
	mInitialized = false;
}


/*void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
    FILE *pFile;
    char szFilename[32];
    int  y;

    // Open file
    sprintf(szFilename, "frame%d.ppm", iFrame);
    pFile=fopen(szFilename, "wb");
    if(pFile==NULL)
        return;

    // Write header
    fprintf(pFile, "P6\n%d %d\n255\n", width, height);

    // Write pixel data
    for(y=0; y<height; y++)
        fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

    // Close file
    fclose(pFile);
}*/

void cIFrameDecoder::GetVideoES(unsigned char* Data, size_t buffersize, unsigned char* outbuffer, size_t* outbuffersize)
{
	int pos = 0;
	int Length = (int)buffersize;
	*outbuffersize = 0;
	int esLength = 0;

	for (; pos + 9 < Length; pos++)
	{
		if (!Data[pos] && !Data[pos+1] && Data[pos+2] == 0x01 && (Data[pos+3] >= 0xE0 && Data[pos+3] <= 0xEF))
		{
			if ((Data[pos + 6] & 0xC0) == 0x80)
			{
		              if (pos + 9 + Data[pos + 8] <= Length)
				{
					if ((((int)Data[pos + 4]) * (int)256 + (int)Data[pos + 5]) > 0)
					{
						esLength = ((int)Data[pos + 4]) * (int)256 + (int)Data[pos + 5] + (int)6 - (9 + (int)Data[pos + 8]);

						if (pos + 9 + Data[pos + 8] + esLength <= Length)
						{
							if (((int)(*outbuffersize) + esLength) > Length)
							{
								esyslog("em84xx: cIFrameDecoder::GetVideoES - target buffer too small\n");
								return;
							}

//printf("%d bytes at position %d (inputlength: %d, total: %d)\n",esLength,pos+9+Data[pos + 8],Length,*outbuffersize);
							memcpy(&outbuffer[*outbuffersize],&Data[pos+9+Data[pos + 8]],esLength);
							*outbuffersize += esLength;

							pos = pos + 9 + Data[pos + 8] + esLength -1;
						}

						
					}
		              }
			}
			/*else
			{
				int fpos = pos + 6;
				esLength = ((int)Data[pos + 4]) * (int)256 + (int)Data[pos + 5];
					// this looked suspicious
				if (Length >= pos + 6 + esLength)
				{
					while (Data[fpos] == 0xff) ++fpos; // skip stuffing bytes
					if ((Data[fpos] & 0xC0) == 0x40) fpos += 2; // skip std buffer scale and size
                   
					if ((Data[fpos] & 0xF0) == 0x20) { // pts only
						fpos += 5;
					} else if ((Data[fpos] & 0xF0) == 0x30) { // pts and dts
						fpos += 10;
					} else {
						++fpos;
					}
                   
					if (esLength) esLength = esLength - (fpos - pos - 6);
					//if ((fpos+esLength) < Length)
			
					memcpy(&outbuffer[*outbuffersize],&Data[pos+9+Data[pos + 8]],esLength);
					*outbuffersize += esLength;
				}
      			}*/
		}
	}
}


bool cIFrameDecoder::DecodeFrame(unsigned char * data, int length)
{
        if (!mInitialized)
        {
                esyslog("em84xx: cannot decode frame - not initialized");
                return false;
        }

        mFrameValid = false;

        int wGotPicture, wBytesUsed;

        avcodec_flush_buffers (mCodecContext) ;
        wBytesUsed = avcodec_decode_video(mCodecContext, mDecodedFrame, &wGotPicture, data, length);
        wBytesUsed = avcodec_decode_video(mCodecContext, mDecodedFrame, &wGotPicture, data, length);
        wBytesUsed = avcodec_decode_video(mCodecContext, mDecodedFrame, &wGotPicture, data, length);

        if (wBytesUsed < 0)
        {
                esyslog("em84xx: error while decoding iframe\n");
                return false;
        }
        if (!wGotPicture)
        {
                esyslog("em84xx: got no picture from iframe\n");
                return false;
        }

        if (/*(mCodecContext->pix_fmt != PIX_FMT_YUV420P) && */((!mYUVFrameBuffer) || (mFrameWidth != mCodecContext->width) || (mFrameHeight != mCodecContext->height)))
        {
                if (mYUVFrameBuffer)
                        delete[] mYUVFrameBuffer;
                mYUVFrameBuffer = new unsigned char[avpicture_get_size(PIX_FMT_YUV420P, mCodecContext->width, mCodecContext->height)];

                avpicture_fill((AVPicture *)mDecodedYUVFrame, mYUVFrameBuffer, PIX_FMT_YUV420P, mCodecContext->width, mCodecContext->height);
        }

	// TODO: we always convert ... not necc.
        // if (mCodecContext->pix_fmt != PIX_FMT_YUV420P)
        img_convert((AVPicture *)mDecodedYUVFrame, PIX_FMT_YUV420P, (AVPicture*)mDecodedFrame,
                mCodecContext->pix_fmt, mCodecContext->width, mCodecContext->height);

	// TODO: check if 480er resizing is necc.
        unsigned char wResizeBuffer[avpicture_get_size(PIX_FMT_YUV420P, _EM84_YUV_FRAME_WIDTH, _EM84_YUV_FRAME_HEIGHT)];
        AVFrame* wResizeFrame=avcodec_alloc_frame();
        avpicture_fill((AVPicture *)wResizeFrame, wResizeBuffer, PIX_FMT_YUV420P, _EM84_YUV_FRAME_WIDTH, _EM84_YUV_FRAME_HEIGHT);

        ImgReSampleContext* wResampleContext = img_resample_init(_EM84_YUV_FRAME_WIDTH, _EM84_YUV_FRAME_HEIGHT, mCodecContext->width, mCodecContext->height);
        img_resample(wResampleContext, (AVPicture *) wResizeFrame, (AVPicture *) mDecodedYUVFrame);
        img_resample_close(wResampleContext);

	if (avpicture_get_size(PIX_FMT_YUV420P, mCodecContext->width, mCodecContext->height) < avpicture_get_size(PIX_FMT_YUV420P, _EM84_YUV_FRAME_WIDTH, _EM84_YUV_FRAME_HEIGHT))
	{
		delete[] mYUVFrameBuffer;
		mYUVFrameBuffer = new unsigned char[avpicture_get_size(PIX_FMT_YUV420P, _EM84_YUV_FRAME_WIDTH, _EM84_YUV_FRAME_HEIGHT)];
	}
        memcpy(mYUVFrameBuffer, wResizeBuffer, (_EM84_YUV_FRAME_WIDTH*_EM84_YUV_FRAME_HEIGHT*3)/2);
        av_free(wResizeFrame);

        int wYSize = _EM84_YUV_FRAME_WIDTH*_EM84_YUV_FRAME_HEIGHT;//mCodecContext->width * mCodecContext->height;
        unsigned char wTempUV[wYSize/2];
        for (int t=0; t < (wYSize/4); t++)
        {
                wTempUV[2*t] = mYUVFrameBuffer[wYSize+t];
                wTempUV[2*t+1] = mYUVFrameBuffer[wYSize+(wYSize/4)      +t];
        }
        memcpy(&mYUVFrameBuffer[wYSize],wTempUV, (wYSize/2));

        mFrameWidth = _EM84_YUV_FRAME_WIDTH;//mCodecContext->width;
        mFrameHeight = _EM84_YUV_FRAME_HEIGHT;//mCodecContext->height;
        mFrameValid = true;

        return true;
}