#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <avifile.h>
#include <fourcc.h>
#include <except.h>
#include <ReadHandlers.h>
#include <iostream>
#include <cpuinfo.h>
#include <utils.h>
#include <creators.h>

#include <math.h>
#include <encore2.h>
#include <decore.h>
#include <inttypes.h>

extern "C" int mb_counter;
extern "C" int blk_counter;
extern "C" void clear_timer();
extern "C" void clear_etimer();
extern "C" void write_timer();
extern "C" void write_etimer();
extern "C" int quiet_encore;

using namespace std;

struct timer_holder
{
    timer_holder();
    ~timer_holder();
};
static timer_holder th;
timer_holder::timer_holder()
{
    clear_timer();
    clear_etimer();
}
timer_holder::~timer_holder()
{
    printf("\tDecoder:\n");
    write_timer();
    printf("\tEncoder:\n");
    write_etimer();
    printf("SAD_* fns: Block() %d times, Macroblock() %d times\n",
	blk_counter, mb_counter);
}

extern "C" const std::vector<CodecInfo>& RegisterPlugin();
extern "C" const double frequency=700000.;

using namespace std;
#define __MODULE__ "test codec"

bool no_encode=false;
bool no_decode=false;
bool quiet;

int intra_decisions;
int inter_decisions;
double avgsnr=0;
int BLOCK_DEBUG;
int MOTION_DEBUG;
int num_frames=5;
double ac_effect;
float start_time=599.;
bool use_deblock_filter;
int averager;
int quant=10;
int quant2=10;
bool no_16;
bool no_8;
bool draw=false;
bool use_stretcher;
extern "C" int neigh_tend_16x16;
extern "C" int neigh_tend_8x8;
extern "C" int32_t iMv16x16;
extern "C" int32_t mv_size_parameter;
static bool exact=false;
bool verify=true;    
int quality=9;
const char* filename="/c/trailers/matrix-div4-500.avi";
template <class T> inline T max(const T x, const T y) { return (x>y)?x:y; }

inline int get_dc_scaler(int qp, int i)
{
    if(qp<=4)return 8;
    if(i<4)
    {
	if(qp<=8)return 2*qp;
	if(qp<=25)return qp+8;
	return 2*qp-16;
    }
    if(qp<=25)return (qp+13)/2;
    return qp-6;
}

double diff(CImage* im1, CImage* im2)
{
    int sum=0;
    for(unsigned i=0; i<im1->Bytes(); i++)
	sum+=abs((int)im1->Data()[i]-(int)im2->Data()[i]);
    return (double)sum/im1->Bytes();
}

int main(int argc, char** argv)
{
    quiet=true;
    for(int i=1; i<argc; i++)
    {
	if(!strcmp(argv[i], "-f"))
	{
	    i++;
	    filename=argv[i];	    
	    continue;
	}
	if(!strcmp(argv[i], "-t"))
	{
	    i++;
	    sscanf(argv[i], "%f", &start_time);
	}
	if(!strcmp(argv[i], "-quiet"))
	    quiet=true;
	if(!strcmp(argv[i], "-no-encode"))
	    no_encode=true;
	if(!strcmp(argv[i], "-no-decode"))
	    no_decode=true;
	if(!strcmp(argv[i], "-draw"))
	    draw=true;
	if(!strcmp(argv[i], "-no-16"))
	    no_16=true;
	if(!strcmp(argv[i], "-no-8"))
	    no_8=true;
	if(!strcmp(argv[i], "-no-stretcher"))
	    use_stretcher=false;
	if(!strcmp(argv[i], "-debug"))
	    BLOCK_DEBUG=MOTION_DEBUG=1;
	if(!strcmp(argv[i], "-no-verify"))
	    verify=false;
	if(!strcmp(argv[i], "-exact"))
	    exact=true;
	if(!strcmp(argv[i], "-qual"))
	{
	    i++;
	    quality=atoi(argv[i]);
	}
	if(!strcmp(argv[i], "-quant"))
	{
	    i++;
	    quant=atoi(argv[i]);
	}
	if(!strcmp(argv[i], "-quant2"))
	{
	    i++;
	    quant2=atoi(argv[i]);
	}
	if(!strcmp(argv[i], "-num-frames"))
	{
	    i++;
	    num_frames=atoi(argv[i]);
	}
	if(!strcmp(argv[i], "-averager"))
	{
	    i++;
	    averager=atoi(argv[i]);
	}
	if(!strcmp(argv[i], "-16"))
	{
	    i++;
	    iMv16x16=atoi(argv[i]);
	//    neigh_tend_16x16=atoi(argv[i]);
	}
	if(!strcmp(argv[i], "-neigh_16"))
	{
	    i++;
	    neigh_tend_16x16=atoi(argv[i]);
	}
	if(!strcmp(argv[i], "-neigh_8"))
	{
	    i++;
	    neigh_tend_8x8=atoi(argv[i]);
	}
	if(!strcmp(argv[i], "-mvsp"))
	{
	    i++;
	    mv_size_parameter=atoi(argv[i]);
	}
	if(!strcmp(argv[i], "-ac-effect"))
	{
	    i++;
	    sscanf(argv[i], "%lf", &ac_effect);
	}
	if(!strcmp(argv[i], "-deblock"))
	    use_deblock_filter=true;
    }
    IAviReadFile* file=0;
    try
    {
        int i;
	unsigned short w;
	unsigned short h;
	int fd;
        int progress=0;
	CImage* im;
	long long ttt=0;
	long long ttt2=0;
	long long ttt3=0;
	long long t1, t2;	    
	char* dest=new char[3000000];	
	intra_decisions=inter_decisions=0;

	/** Start reading uncompressed frames from file **/
	/** By default they are in BGR24 mode **/
	file=CreateIAviReadFile(filename);
	IAviReadStream* stream=file->GetStream(0, AviStream::Video);
	if(!stream)throw FATAL("No video streams");
//	stream->SetDirection(true);
	if(stream->StartStreaming())
	    throw FATAL("Could not start streaming");
	stream->SeekTimeToKeyFrame((double)start_time);
//	    stream->GetDecoder()->SetDestFmt(0, fccYV12);
	if(exact)
	    while(stream->GetTime()<start_time)
		stream->ReadFrame(false);
//	    IRtConfig* rtc=dynamic_cast<IRtConfig*>(stream->GetDecoder());
//	    if(rtc)rtc->SetValue("Quality", 4);

    	BitmapInfo bi=stream->GetDecoder()->DestFmt();
	w=bi.biWidth;
	h=abs(bi.biHeight);
	    
	/** Create encoder **/
	ENC_PARAM param;
	memset(&param, 0, sizeof(param));
	param.x_dim=w;
	param.y_dim=h;
	param.framerate=25.;
	param.min_quantizer=quant;
	param.max_quantizer=quant;
	param.bitrate=600000;
	param.quality=quality;
	encore(0, ENC_OPT_INIT, &param, 0);
	void* handle=param.handle;

	/** Create decoder **/	    
        DEC_PARAM dp;
        dp.x_dim=w;
        dp.y_dim=h;
        dp.output_format=DEC_YUY2;
        memset(&dp.buffers, 0, sizeof(dp.buffers));
        decore(1, DEC_OPT_INIT, &dp, 0);
	    
        BitmapInfo bi2(w, h, fccYUY2);
        CImage* dec_out=new CImage(&bi2);
	for(i=0; i<num_frames; i++)
	{
	    t1=longcount();
	    stream->ReadFrame();
	    im=stream->GetFrame();
	    t2=longcount();
	    ttt3+=t2-t1;
	    char s[20];
	    if(draw)
	    {
	        sprintf(s, "unc-%03d.bmp", i);
	        im->Dump(s);
	    }
	    ENC_FRAME fr;
#ifdef TEST_YUY2	    
	    BitmapInfo bi(im->Width(), im->Height(), fccYUY2);
	    fr.colorspace=ENC_CSP_YUY2;
#else
	    BitmapInfo bi(im->Width(), -im->Height(), 24);
	    fr.colorspace=ENC_CSP_RGB24;
#endif	    
	    CImage* im2=new CImage(im, &bi);
	    fr.image=im2->Data();
	    fr.bitstream=dest;
	    fr.mvs=0;
	    if(i==0)
		fr.quant=quant;
	    else
		fr.quant=quant2;
	    fr.intra=-1;

	    ENC_RESULT res;
	    t1=longcount();
	    encore(handle, ENC_OPT_ENCODE_VBR, &fr, &res);
	    t2=longcount();
	    ttt+=t2-t1;

//	    im->Release();
	    im2->Release();
	    printf("Frame %d encoded as %s into %d bytes\n",
	        i, res.is_key_frame?"INTRA":"INTER", fr.length);
	    progress+=fr.length;
	    if(i==0)
		printf("ATTN: quant %d->%d, text %d, non-text %d\n", 
		    quant, quant2, res.texture_bits, res.total_bits-res.texture_bits);
	    DEC_FRAME df;
	    df.bmp=dec_out->Data();
	    df.bitstream=dest;
	    df.length=fr.length;
	    df.render_flag=1;
	    df.stride=w;
	    t1=longcount();
	    decore(1, DEC_OPT_FRAME, &df, 0);
	    t2=longcount();
	    ttt2+=t2-t1;
	    BitmapInfo bb(w, h, 24);
	    CImage* im3=new CImage(dec_out, &bb);
	    double snr=diff(im, im3);
	    im3->Release();
	    printf("SNR: %f ( %f db )\n", snr, 10*log(snr/256.)/log(10));
	    avgsnr+=snr;
	    if(draw)
	    {
    	        sprintf(s, "dec-%03d.bmp", i);
	        dec_out->Dump(s);
	    }
	}	
        cout<<"Encoded "<<i<<" frames ( "<<i*5*w*h/4<<" bytes ) into "
	    <<progress<<" bytes; average error level "<<avgsnr/i<<endl;
    	cout<<"Encoding: "<<ttt/freq<<" ms"<<endl;
        cout<<"Decoding: "<<ttt2/freq<<" ms"<<endl;
        cout<<"Reading: "<<ttt3/freq<<" ms"<<endl;
        encore(handle, ENC_OPT_RELEASE, 0, 0);
//	    cout<<"Block decisions: intra "<<intra_decisions<<", inter "<<inter_decisions<<endl;
	return 0;
    }
    catch(FatalError& e)
    {
	e.PrintAll();
    }     
    delete file;
}

