/************************************************************************/
/* ʹõİ͹ߺ                                                                     */
/************************************************************************/

#include <assert.h>
#include "avcodec.h"
#include "dsputil.h"

#define EDGE_WIDTH   16
#define STRIDE_ALIGN 16

#define INT_MAX 2147483647

#define FFMAX(a,b) ((a) > (b) ? (a) : (b))

void *av_malloc(unsigned int size)
{
    void *ptr;

    if (size > INT_MAX)
        return NULL;
    ptr = malloc(size);

    return ptr;
}

void *av_realloc(void *ptr, unsigned int size)
{
    if (size > INT_MAX)
        return NULL;

    return realloc(ptr, size);
}

void av_free(void *ptr)
{
    if (ptr)
        free(ptr);
}

void *av_mallocz(unsigned int size)
{
    void *ptr;

    ptr = av_malloc(size);
    if (!ptr)
        return NULL;

    memset(ptr, 0, size);
    return ptr;
}

void *av_fast_realloc(void *ptr, unsigned int *size, unsigned int min_size)
{
    if (min_size <  *size)
        return ptr;

    *size = FFMAX(17 *min_size / 16+32, min_size);

    return av_realloc(ptr,  *size);
}

void av_freep(void *arg)
{
    void **ptr = (void **)arg;
    av_free(*ptr);
    *ptr = NULL;
}

/* Ϊֶ֧Ҫеı */
AVCodec *first_avcodec = NULL;

/* ע */
void register_avcodec(AVCodec *format)
{
	/* ѱӵ */
    AVCodec **p;
    p = &first_avcodec;
    while (*p != NULL)
        p = &(*p)->next;
    *p = format;
    format->next = NULL;
}

typedef struct InternalBuffer
{
    uint8_t *base[4];
    uint8_t *data[4];
    int  linesize[4];
} InternalBuffer;

#define INTERNAL_BUFFER_SIZE 32

#define ALIGN(x, a) (((x)+(a)-1)&~((a)-1))

void avcodec_align_dimensions(AVCodecContext *s, int *width, int *height)
{
    int w_align = 1;
    int h_align = 1;

    switch (s->pix_fmt)
    {
    case PIX_FMT_YUV420P:
    case PIX_FMT_YUV422:
    case PIX_FMT_UYVY422:
    case PIX_FMT_YUV422P:
    case PIX_FMT_YUV444P:
    case PIX_FMT_GRAY8:
    case PIX_FMT_YUVJ420P:
    case PIX_FMT_YUVJ422P:
    case PIX_FMT_YUVJ444P: //FIXME check for non mpeg style codecs and use less alignment
        w_align = 16;
        h_align = 16;
        break;
    case PIX_FMT_YUV411P:
    case PIX_FMT_UYVY411:
        w_align = 32;
        h_align = 8;
        break;
    case PIX_FMT_YUV410P:
    case PIX_FMT_RGB555:
    case PIX_FMT_PAL8:
        break;
    case PIX_FMT_BGR24:
        break;
    default:
        w_align = 1;
        h_align = 1;
        break;
    }

    *width = ALIGN(*width, w_align);
    *height = ALIGN(*height, h_align);
}

int avcodec_check_dimensions(void *av_log_ctx, unsigned int w, unsigned int h)
{
    if ((int)w > 0 && (int)h > 0 && (w + 128)*(uint64_t)(h + 128) < INT_MAX / 4)
        return 0;

    return  - 1;
}

int avcodec_default_get_buffer(AVCodecContext *s, AVFrame *pic)
{
    int i;
    int w = s->width;
    int h = s->height;
    int align_off;
    InternalBuffer *buf;

    assert(pic->data[0] == NULL);
    assert(INTERNAL_BUFFER_SIZE > s->internal_buffer_count);

    if (avcodec_check_dimensions(s, w, h))
        return  - 1;

    if (s->internal_buffer == NULL)
        s->internal_buffer = av_mallocz(INTERNAL_BUFFER_SIZE *sizeof(InternalBuffer));

    buf = &((InternalBuffer*)s->internal_buffer)[s->internal_buffer_count];

    if (buf->base[0])
    {}
    else
    {
        int h_chroma_shift, v_chroma_shift;
        int pixel_size, size[3];

        AVPicture picture;

        avcodec_get_chroma_sub_sample(s->pix_fmt, &h_chroma_shift, &v_chroma_shift);

        avcodec_align_dimensions(s, &w, &h);

        w+= EDGE_WIDTH*2;
        h+= EDGE_WIDTH*2;

        avpicture_fill(&picture, NULL, s->pix_fmt, w, h);
        pixel_size = picture.linesize[0] * 8 / w;
        assert(pixel_size >= 1);

        if (pixel_size == 3 *8)
            w = ALIGN(w, STRIDE_ALIGN << h_chroma_shift);
        else
            w = ALIGN(pixel_size *w, STRIDE_ALIGN << (h_chroma_shift + 3)) / pixel_size;

        size[1] = avpicture_fill(&picture, NULL, s->pix_fmt, w, h);
        size[0] = picture.linesize[0] *h;
        size[1] -= size[0];
        if (picture.data[2])
            size[1] = size[2] = size[1] / 2;
        else
            size[2] = 0;

        memset(buf->base, 0, sizeof(buf->base));
        memset(buf->data, 0, sizeof(buf->data));

        for (i = 0; i < 3 && size[i]; i++)
        {
            const int h_shift = i == 0 ? 0 : h_chroma_shift;
            const int v_shift = i == 0 ? 0 : v_chroma_shift; 		    

            buf->linesize[i] = picture.linesize[i];

            buf->base[i] = av_malloc(size[i] + 16); //FIXME 16
            if (buf->base[i] == NULL)
                return  - 1;
            memset(buf->base[i], 128, size[i]);

            align_off = ALIGN((buf->linesize[i] * EDGE_WIDTH >> v_shift) + ( EDGE_WIDTH >> h_shift), STRIDE_ALIGN);

            if ((s->pix_fmt == PIX_FMT_PAL8) || !size[2])
                buf->data[i] = buf->base[i];
            else
                buf->data[i] = buf->base[i] + align_off;
        }
    }

    for (i = 0; i < 4; i++)
    {
        pic->base[i] = buf->base[i];
        pic->data[i] = buf->data[i];
        pic->linesize[i] = buf->linesize[i];
    }
    s->internal_buffer_count++;

    return 0;
}

void avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic)
{
    int i;
    InternalBuffer *buf,  *last, temp;

    assert(s->internal_buffer_count);

    buf = NULL;
    for (i = 0; i < s->internal_buffer_count; i++)
    {
        buf = &((InternalBuffer*)s->internal_buffer)[i]; //just 3-5 checks so is not worth to optimize
        if (buf->data[0] == pic->data[0])
            break;
    }
    assert(i < s->internal_buffer_count);
    s->internal_buffer_count--;
    last = &((InternalBuffer*)s->internal_buffer)[s->internal_buffer_count];

    temp =  *buf;
    *buf =  *last;
    *last = temp;

    for (i = 0; i < 3; i++)
    {
        pic->data[i] = NULL;
    }
}

int avcodec_default_reget_buffer(AVCodecContext *s, AVFrame *pic)
{
    if (pic->data[0] == NULL)  // If no picture return a new buffer
    {
        return s->get_buffer(s, pic);
    }

    return 0;
}

void avcodec_default_free_buffers(AVCodecContext *s)
{
    int i, j;

    if (s->internal_buffer == NULL)
        return ;

    for (i = 0; i < INTERNAL_BUFFER_SIZE; i++)
    {
        InternalBuffer *buf = &((InternalBuffer*)s->internal_buffer)[i];
        for (j = 0; j < 4; j++)
        {
            av_freep(&buf->base[j]);
            buf->data[j] = NULL;
        }
    }
    av_freep(&s->internal_buffer);

    s->internal_buffer_count = 0;
}

AVCodecContext *avcodec_alloc_context(void)
{
    AVCodecContext *s = av_malloc(sizeof(AVCodecContext));

    if (s == NULL)
        return NULL;

    memset(s, 0, sizeof(AVCodecContext));

    s->get_buffer = avcodec_default_get_buffer;
    s->release_buffer = avcodec_default_release_buffer;

    s->pix_fmt = PIX_FMT_NONE;

    s->palctrl = NULL;
    s->reget_buffer = avcodec_default_reget_buffer;

    return s;
}

int avcodec_open(AVCodecContext *avctx, AVCodec *codec)
{
    int ret =  - 1;

    if (avctx->codec)
        goto end;

    if (codec->priv_data_size > 0)
    {
        avctx->priv_data = av_mallocz(codec->priv_data_size);
        if (!avctx->priv_data)
            goto end;
    }
    else
    {
        avctx->priv_data = NULL;
    }

    avctx->codec = codec;
    avctx->codec_id = codec->id;
    avctx->frame_number = 0;
    ret = avctx->codec->init(avctx);
    if (ret < 0)
    {
        av_freep(&avctx->priv_data);
        avctx->codec = NULL;
        goto end;
    }
    ret = 0;
end: 
	return ret;
}

int avcodec_decode_video(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr,
						                            uint8_t *buf, int buf_size)
{
    int ret;

    *got_picture_ptr = 0;

    if (buf_size)
    {
        ret = avctx->codec->decode(avctx, picture, got_picture_ptr, buf, buf_size); // ʵʵıı뺯msrleȱ

        if (*got_picture_ptr)
            avctx->frame_number++;
    }
    else
        ret = 0;

    return ret;
}

int avcodec_decode_audio(AVCodecContext *avctx, int16_t *samples, int *frame_size_ptr,
						                            uint8_t *buf, int buf_size)
{
    int ret;

    *frame_size_ptr = 0;
    if (buf_size)
    {
        ret = avctx->codec->decode(avctx, samples, frame_size_ptr, buf, buf_size);
        avctx->frame_number++;
    }
    else
        ret = 0;
    return ret;
}

int avcodec_close(AVCodecContext *avctx)
{
    if (avctx->codec->close)
        avctx->codec->close(avctx);
    avcodec_default_free_buffers(avctx);
    av_freep(&avctx->priv_data);
    avctx->codec = NULL;
    return 0;
}

AVCodec *avcodec_find_decoder(enum CodecID id)
{
    AVCodec *p;
    p = first_avcodec;
    while (p)
    {
        if (p->decode != NULL && p->id == id)
            return p;
        p = p->next;
    } 
    return NULL;
}

/* ʼ */
void avcodec_init(void)
{
    static int inited = 0;

    if (inited != 0)
        return ;
    inited = 1;

    dsputil_static_init();
}
