/*
 *  Copyright (c) 2011 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */
#include <assert.h>
#include <stdlib.h>
#include "vpx_config.h"
#include "lookahead.h"
#include "vp8/common/extend.h"

#define MAX_LAG_BUFFERS (CONFIG_REALTIME_ONLY? 1 : 25)

struct lookahead_ctx
{
    unsigned int max_sz;         /* Absolute size of the queue */
    unsigned int sz;             /* Number of buffers currently in the queue */
    unsigned int read_idx;       /* Read index */
    unsigned int write_idx;      /* Write index */
    struct lookahead_entry *buf; /* Buffer list */
};


/* Return the buffer at the given absolute index and increment the index */
static struct lookahead_entry *
pop(struct lookahead_ctx *ctx,
    unsigned int         *idx)
{
    unsigned int            index = *idx;
    struct lookahead_entry *buf = ctx->buf + index;

    assert(index < ctx->max_sz);
    if(++index >= ctx->max_sz)
        index -= ctx->max_sz;
    *idx = index;
    return buf;
}


void
vp8_lookahead_destroy(struct lookahead_ctx *ctx)
{
    if(ctx)
    {
        if(ctx->buf)
        {
            unsigned int i;

            for(i = 0; i < ctx->max_sz; i++)
                vp8_yv12_de_alloc_frame_buffer(&ctx->buf[i].img);
            free(ctx->buf);
        }
        free(ctx);
    }
}


struct lookahead_ctx*
vp8_lookahead_init(unsigned int width,
                   unsigned int height,
                   unsigned int depth)
{
    struct lookahead_ctx *ctx = NULL;
    unsigned int i;

    /* Clamp the lookahead queue depth */
    if(depth < 1)
        depth = 1;
    else if(depth > MAX_LAG_BUFFERS)
        depth = MAX_LAG_BUFFERS;

    /* Keep last frame in lookahead buffer by increasing depth by 1.*/
    depth += 1;

    /* Align the buffer dimensions */
    width = (width + 15) & ~15;
    height = (height + 15) & ~15;

    /* Allocate the lookahead structures */
    ctx = calloc(1, sizeof(*ctx));
    if(ctx)
    {
        ctx->max_sz = depth;
        ctx->buf = calloc(depth, sizeof(*ctx->buf));
        if(!ctx->buf)
            goto bail;
        for(i=0; i<depth; i++)
            if (vp8_yv12_alloc_frame_buffer(&ctx->buf[i].img,
                                            width, height, VP8BORDERINPIXELS))
                goto bail;
    }
    return ctx;
bail:
    vp8_lookahead_destroy(ctx);
    return NULL;
}


int
vp8_lookahead_push(struct lookahead_ctx *ctx,
                   YV12_BUFFER_CONFIG   *src,
                   int64_t               ts_start,
                   int64_t               ts_end,
                   unsigned int          flags,
                   unsigned char        *active_map)
{
    struct lookahead_entry* buf;
    int row, col, active_end;
    int mb_rows = (src->y_height + 15) >> 4;
    int mb_cols = (src->y_width + 15) >> 4;

    if(ctx->sz + 2 > ctx->max_sz)
        return 1;
    ctx->sz++;
    buf = pop(ctx, &ctx->write_idx);

    /* Only do this partial copy if the following conditions are all met:
     * 1. Lookahead queue has has size of 1.
     * 2. Active map is provided.
     * 3. This is not a key frame, golden nor altref frame.
     */
    if (ctx->max_sz == 1 && active_map && !flags)
    {
        for (row = 0; row < mb_rows; ++row)
        {
            col = 0;

            while (1)
            {
                /* Find the first active macroblock in this row. */
                for (; col < mb_cols; ++col)
                {
                    if (active_map[col])
                        break;
                }

                /* No more active macroblock in this row. */
                if (col == mb_cols)
                    break;

                /* Find the end of active region in this row. */
                active_end = col;

                for (; active_end < mb_cols; ++active_end)
                {
                    if (!active_map[active_end])
                        break;
                }

                /* Only copy this active region. */
                vp8_copy_and_extend_frame_with_rect(src, &buf->img,
                                                    row << 4,
                                                    col << 4, 16,
                                                    (active_end - col) << 4);

                /* Start again from the end of this active region. */
                col = active_end;
            }

            active_map += mb_cols;
        }
    }
    else
    {
        vp8_copy_and_extend_frame(src, &buf->img);
    }
    buf->ts_start = ts_start;
    buf->ts_end = ts_end;
    buf->flags = flags;
    return 0;
}


struct lookahead_entry*
vp8_lookahead_pop(struct lookahead_ctx *ctx,
                  int                   drain)
{
    struct lookahead_entry* buf = NULL;

    assert(ctx != NULL);
    if(ctx->sz && (drain || ctx->sz == ctx->max_sz - 1))
    {
        buf = pop(ctx, &ctx->read_idx);
        ctx->sz--;
    }
    return buf;
}


struct lookahead_entry*
vp8_lookahead_peek(struct lookahead_ctx *ctx,
                   unsigned int          index,
                   int                   direction)
{
    struct lookahead_entry* buf = NULL;

    if (direction == PEEK_FORWARD)
    {
        assert(index < ctx->max_sz - 1);
        if(index < ctx->sz)
        {
            index += ctx->read_idx;
            if(index >= ctx->max_sz)
                index -= ctx->max_sz;
            buf = ctx->buf + index;
        }
    }
    else if (direction == PEEK_BACKWARD)
    {
        assert(index == 1);

        if(ctx->read_idx == 0)
            index = ctx->max_sz - 1;
        else
            index = ctx->read_idx - index;
        buf = ctx->buf + index;
    }

    return buf;
}


unsigned int
vp8_lookahead_depth(struct lookahead_ctx *ctx)
{
    return ctx->sz;
}
