#include "FFVideoDecoder.h"
#include <fourcc.h>
#include <string.h>
#include <iostream>

#include <cstdio>
using namespace std;

FFVideoDecoder::FFVideoDecoder(AVCodec* av, const CodecInfo& info, const BITMAPINFOHEADER& bh, int flip)
    :IVideoDecoder(info, bh), m_bOpened(false)
{
    m_pAvCodec = av;
    m_Caps = CAP_YV12;

    if (!flip)
        m_obh.biHeight *= -1;
    memcpy(&m_obh, &bh, sizeof(bh));
    m_decoder = m_obh;
    cout << "FFVideoDecoder opened" << endl;
}

FFVideoDecoder::~FFVideoDecoder()
{
    Stop();
}

int FFVideoDecoder::DecodeInternal(void* src, size_t size, int is_keyframe, CImage* pImage)
{
    if (!m_bOpened)
    {
        m_AvContext.width = m_decoder.biWidth;
	m_AvContext.height = (m_decoder.biHeight > 0) ? m_decoder.biHeight : -m_decoder.biHeight;
	m_AvContext.pix_fmt = PIX_FMT_YUV422;
	//m_AvContext.pix_fmt = PIX_FMT_RGB24;
        //cout << "WxH " << m_AvContext.width  << "   " << m_AvContext.height << "    " << (void*)m_pAvCodec << endl;
	if (avcodec_open(&m_AvContext, m_pAvCodec) < 0)
	{
	    cerr << "FFVideoDecoder::DecodeInternal() can't open avcodec" <<endl;
            return -1;
	}
        else
	    m_bOpened = true;
    }

    int got_picture = 0;

    int hr = avcodec_decode_video(&m_AvContext, &m_AvPicture,
				  &got_picture, (unsigned char*) src, size);
    if (hr < 0)
    {
	cerr << "FFVideoDecoder: warning: hr=" << hr << endl;
	return hr;
    }

    CImage* ci = pImage;
    if (m_decoder.biCompression != fccYV12)
    {
	BitmapInfo bi(*m_bh);
        bi.SetSpace(fccYV12);
	ci = new CImage(&bi);
	//m_decoder.Print();
    }
    if (0)
	cout << "DECODE STATUS " << hr << "   " << got_picture << "  "
	    << (void*)m_AvPicture.data[0] << " "
	    << (void*)m_AvPicture.data[1] << " "
	    << (void*)m_AvPicture.data[2] << " *  "
	    << m_AvPicture.linesize[0] << " "
	    << m_AvPicture.linesize[1] << " "
	    << m_AvPicture.linesize[2] << " "
	    << m_decoder.biWidth
	    << endl;
    //cout << "Q: " << m_AvContext.quality << "  " << m_AvContext.key_frame << endl;

    m_fQuality = (31 - m_AvContext.quality) / 31.0;
    //
    //  ULGLY PART - CLEANME later
    //  implement direct rendering for YV12!!!
    //

    unsigned char* out = ci->Data(0);
    unsigned char* end = out + ci->Pixels();
    unsigned char* in = m_AvPicture.data[0];
    while (out < end)
    {
	memcpy(out, in, m_decoder.biWidth);
	out += m_decoder.biWidth;
	in += m_AvPicture.linesize[0];
    }

    out = ci->Data(2);
    if (!out)
	out = ci->Data() + ci->Pixels();
    end = out + ci->Pixels() / 4;
    in = m_AvPicture.data[1];
    while (out < end)
    {
	memcpy(out, in, m_decoder.biWidth / 2);
	out += m_decoder.biWidth / 2;
	in += m_AvPicture.linesize[1];
    }

    out = ci->Data(1);
    if (!out)
	out = ci->Data() + ci->Pixels() * 5 / 4;
    end = out + ci->Pixels() / 4;
    in = m_AvPicture.data[2];
    while (out < end)
    {
	memcpy(out, in, m_decoder.biWidth / 2);
	out += m_decoder.biWidth / 2;
	in += m_AvPicture.linesize[2];
    }

    if (ci != pImage)
    {
	pImage->Convert(ci->Data(), ci->GetFmt());
        ci->Release();
    }

    return hr;
}

int FFVideoDecoder::SetDestFmt(int bits = 24, fourcc_t csp = 0)
{
    if (!CImage::Supported(csp, bits))
	return -1;

    if (csp)
    {
	switch (csp)
	{
	case fccYV12:
	    m_obh.SetSpace(csp);
	    break;
	default:
	    return -1;
	}
    }
    else if (bits)
    {
	switch(bits)
	{
	case 15:
	case 16:
	case 24:
	case 32:
	    m_obh.SetBits(bits);
	    break;
	default:
	    return -1;
	}
    }
    m_decoder = m_obh;
    Restart();
    return 0;
}

void FFVideoDecoder::StartInternal()
{
    // empty
}

void FFVideoDecoder::StopInternal()
{
    if (m_bOpened)
	avcodec_close(&m_AvContext);
    m_bOpened = false;
}
