/******************************************************************************
 *
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *****************************************************************************
 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
*/
/**
*******************************************************************************
* @file
*  ideint_utils.c
*
* @brief
*  This file contains the definitions of the core  processing of the de
* interlacer.
*
* @author
*  Ittiam
*
* @par List of Functions:
*  ideint_weave_pic()
*  init_bob_indices()
*  ideint_weave_blk()
*  ideint_spatial_filter()
*
* @remarks
*  None
*
*******************************************************************************
*/
/*****************************************************************************/
/* File Includes                                                             */
/*****************************************************************************/
/* System include files */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>


/* User include files */
#include "icv_datatypes.h"
#include "icv_macros.h"
#include "icv_platform_macros.h"
#include "icv.h"
#include "icv_variance.h"
#include "icv_sad.h"
#include "ideint.h"
#include "ideint_defs.h"
#include "ideint_structs.h"
#include "ideint_utils.h"
#include "ideint_cac.h"

/**
*******************************************************************************
*
* @brief
*  Weaves two fields to produce a frame
*
* @par   Description
*  Weaves two fields to produce a frame
*
* @param[in] ps_src_top
*  Top field source
*
* @param[in] ps_src_bot
*  Bottom field source
*
* @param[in] ps_dst_frm
*  Destination frame
*
* @returns
*   0 on Success
*
* @remarks
*
*******************************************************************************
*/
WORD32 ideint_weave_pic(icv_pic_t *ps_src_top,
                        icv_pic_t *ps_src_bot,
                        icv_pic_t *ps_dst_frm,
                        WORD32 start_row,
                        WORD32 num_rows)
{
    UWORD8 *pu1_src, *pu1_dst;
    WORD32 i, j, num_comp;
    icv_pic_t *ps_src_fld;
    WORD32 fld;
    icv_pic_t *ps_src_flds[2];

    num_comp = 3;
    ps_src_flds[0] = ps_src_top;
    ps_src_flds[1] = ps_src_bot;

    for(fld = 0; fld < 2; fld++)
    {
        ps_src_fld = ps_src_flds[fld];
        for(i = 0; i < num_comp; i++)
        {
            WORD32 src_strd;
            WORD32 dst_strd;
            WORD32 comp_row_start, comp_row_end;
            comp_row_start = start_row;
            comp_row_end = comp_row_start + num_rows;
            if(i)
            {
                comp_row_start >>= 1;
                comp_row_end >>= 1;
            }

            comp_row_end = MIN(comp_row_end, ps_dst_frm->ai4_ht[i]);

            pu1_src = ps_src_fld->apu1_buf[i];
            pu1_dst = ps_dst_frm->apu1_buf[i];

            src_strd = ps_src_fld->ai4_strd[i];
            dst_strd = ps_dst_frm->ai4_strd[i];

            /* If source field is bottom, increment destination */
            pu1_dst += fld * dst_strd;

            /* In case input and output are pointing to same buffer, then no need to copy */
            if((pu1_src != pu1_dst) || ((2 * dst_strd) != src_strd))
            {
                pu1_dst += ps_dst_frm->ai4_strd[i] * comp_row_start;
                pu1_src += ps_src_fld->ai4_strd[i] * comp_row_start / 2;

                for(j = comp_row_start; j < comp_row_end; j += 2)
                {
                    memcpy(pu1_dst, pu1_src, ps_dst_frm->ai4_wd[i]);
                    pu1_dst += ps_dst_frm->ai4_strd[i] * 2;
                    pu1_src += ps_src_fld->ai4_strd[i];
                }
            }
        }
    }
    return 0;
}


/**
*******************************************************************************
*
* @brief
*  Weaves a 8x8 block
*
* @par   Description
*  Weaves a 8x8 block from two fields
*
* @param[in] pu1_top
*  Top field source
*
* @param[in] pu1_bot
*  Bottom field source
*
* @param[in] pu1_dst
*  Destination
*
* @param[in] dst_strd
*  Destination stride
*
* @param[in] src_strd
*  Source stride
*
* @returns
*  0 on success
*
* @remarks
*
*******************************************************************************
*/
WORD32 ideint_weave_blk(UWORD8 *pu1_top,
                        UWORD8 *pu1_bot,
                        UWORD8 *pu1_dst,
                        WORD32 dst_strd,
                        WORD32 src_strd,
                        WORD32 wd,
                        WORD32 ht)
{
    WORD32 j;

    for(j = 0; j < ht; j += 2)
    {
        memcpy(pu1_dst, pu1_top, wd);
        pu1_dst += dst_strd;
        pu1_top += src_strd;

        memcpy(pu1_dst, pu1_bot, wd);
        pu1_dst += dst_strd;
        pu1_bot += src_strd;
    }
    return 0;
}

/**
*******************************************************************************
*
* @brief
*  Copy a boundary block and pad
*
* @par   Description
*  Copies a block on one of the boundaries and pads
*
* @param[in] pu1_top
*  Top field source
*
* @param[in] pu1_bot
*  Bottom field source
*
* @param[in] pu1_pad
*  Padded destination
*
* @param[in] cur_strd
*  Stride for pu1_top and pu1_bot
*
* @param[in] row
*  Current block's row
*
* @param[in] col
*  Current block's column
*
* @param[in] num_blks_y
*  Number of blocks in Y direction
*
* @param[in] num_blks_x
*  Number of blocks in X direction

* @returns
*  None
*
* @remarks
*
*******************************************************************************
*/
void ideint_pad_blk(UWORD8 *pu1_top,
                    UWORD8 *pu1_bot,
                    UWORD8 *pu1_pad,
                    WORD32 cur_strd,
                    WORD32 row,
                    WORD32 col,
                    WORD32 num_blks_y,
                    WORD32 num_blks_x,
                    WORD32 blk_wd,
                    WORD32 blk_ht)
{
    WORD32 i;
    WORD32 num_cols, num_rows;
    UWORD8 *pu1_dst;
    UWORD8 *pu1_src_top;
    UWORD8 *pu1_src_bot;

    num_rows = blk_ht + 4;
    num_cols = blk_wd + 4;

    pu1_src_top = pu1_top - cur_strd - 2;
    pu1_src_bot = pu1_bot - cur_strd - 2;
    pu1_dst = pu1_pad;

    if(0 == col)
    {
        num_cols -= 2;
        pu1_dst += 2;
        pu1_src_top += 2;
        pu1_src_bot += 2;
    }

    if(0 == row)
    {
        num_rows -= 2;
        pu1_dst += 2 * (BLK_WD + 4);
        pu1_src_top += cur_strd;
        pu1_src_bot += cur_strd;
    }

    if((num_blks_x - 1) == col)
        num_cols -= 2;

    if((num_blks_y - 1) == row)
        num_rows -= 2;

    for(i = 0; i < num_rows; i += 2)
    {
        memcpy(pu1_dst, pu1_src_top, num_cols);
        pu1_dst += (BLK_WD + 4);

        memcpy(pu1_dst, pu1_src_bot, num_cols);
        pu1_dst += (BLK_WD + 4);

        pu1_src_top += cur_strd;
        pu1_src_bot += cur_strd;
    }


    /* Pad Left */
    if(0 == col)
    {
       for(i = 0; i < (BLK_HT + 4); i++)
       {
           WORD32 ofst = i * (BLK_WD + 4) + 2;
           pu1_pad[ofst - 1] = pu1_pad[ofst];
           pu1_pad[ofst - 2] = pu1_pad[ofst];
       }
    }

    /* Pad right */
    if((num_blks_x - 1) == col)
    {
       for(i = 0; i < (BLK_HT + 4); i++)
       {
           WORD32 ofst =  i * (BLK_WD + 4) + 2 + blk_wd - 1;
           WORD32 size = (BLK_WD - blk_wd) + 2;
           /* Padding on right should include padding for boundary
            * blocks when width is non-multiple of 8
            */
           memset(&pu1_pad[ofst + 1], pu1_pad[ofst], size);
       }
    }

    /* Pad Top */
    if(0 == row)
    {
        WORD32 src_ofst = 2 * (BLK_WD + 4);
        WORD32 dst_ofst = 0;
        memcpy(pu1_pad + dst_ofst, pu1_pad + src_ofst, (BLK_WD + 4));
        src_ofst += (BLK_WD + 4);
        dst_ofst += (BLK_WD + 4);
        memcpy(pu1_pad + dst_ofst, pu1_pad + src_ofst, (BLK_WD + 4));
    }

    /* Pad Bottom */
    if((num_blks_y - 1) == row)
    {
        WORD32 src_ofst = (0 + blk_ht) * (BLK_WD + 4);
        WORD32 dst_ofst = (1 + blk_ht) * (BLK_WD + 4);
        WORD32 size = (BLK_HT - blk_ht) + 2;

        /* Padding on bottom should include padding for boundary
         * blocks when height is non-multiple of 8
         */
        for(i = 0; i < size; i++)
        {
            memcpy(pu1_pad + dst_ofst, pu1_pad + src_ofst, (BLK_WD + 4));
            dst_ofst += (BLK_WD + 4);
        }
    }
}

/**
*******************************************************************************
*
* @brief
*  Performs spatial edge adaptive filtering
*
* @par   Description
*  Performs spatial edge adaptive filtering by detecting edge direction
*
* @param[in] pu1_src
*  Source buffer
*
* @param[in] pu1_out
*  Destination buffer
*
* @param[in] src_strd
*  Source stride
*
* @param[in] out_strd
*  Destination stride

* @returns
* None
*
* @remarks
*
*******************************************************************************
*/
void ideint_spatial_filter(UWORD8 *pu1_src,
                           UWORD8 *pu1_out,
                           WORD32 src_strd,
                           WORD32 out_strd)
{
    WORD32 i;
    WORD32 j;
    WORD32 k;

    /*********************************************************************/
    /* This loop is for the two halves inside the 8x4 block.             */
    /*********************************************************************/
    for(k = 0; k < 2; k++)
    {
        WORD32 adiff[3] = {0, 0, 0};
        WORD32 shift;
        WORD32 dir_45_le_90, dir_45_le_135, dir_135_le_90;
        UWORD8 *pu1_row_1, *pu1_row_2, *pu1_dst;

        /*****************************************************************/
        /* Direction detection                                           */
        /*****************************************************************/
        pu1_row_1 = pu1_src;
        pu1_row_2 = pu1_src + src_strd;

        /*****************************************************************/
        /* Calculating the difference along each of the 3 directions.    */
        /*****************************************************************/
        for(j = 0; j < SUB_BLK_HT; j ++)
        {
            for(i = 0; i < SUB_BLK_WD; i++)
            {
                adiff[0] += ABS_DIF(pu1_row_1[i], pu1_row_2[i]); /*  90 */

                adiff[1] += ABS_DIF(pu1_row_1[i - 1], pu1_row_2[i + 1]); /* 135 */

                adiff[2] += ABS_DIF(pu1_row_1[i + 1], pu1_row_2[i - 1]); /*  45 */
            }
            pu1_row_1 += src_strd;
            pu1_row_2 += src_strd;
        }

        /*****************************************************************/
        /* Applying bias, to make the diff comparision more robust.      */
        /*****************************************************************/
        adiff[0] *= EDGE_BIAS_0;
        adiff[1] *= EDGE_BIAS_1;
        adiff[2] *= EDGE_BIAS_1;

        /*****************************************************************/
        /* comapring the diffs */
        /*****************************************************************/
        dir_45_le_90  = (adiff[2] <= adiff[0]);
        dir_45_le_135 = (adiff[2] <= adiff[1]);
        dir_135_le_90 = (adiff[1] <= adiff[0]);

        /*****************************************************************/
        /* Direction selection. */
        /*****************************************************************/
        shift = 0;
        if(1 == dir_45_le_135)
        {
            if(1 == dir_45_le_90)
                shift = 1;
        }
        else
        {
            if(1 == dir_135_le_90)
                shift = -1;
        }

        /*****************************************************************/
        /* Directional interpolation */
        /*****************************************************************/
        pu1_row_1 = pu1_src + shift;
        pu1_row_2 = pu1_src + src_strd - shift;
        pu1_dst   = pu1_out;

        for(j = 0; j < SUB_BLK_HT; j++)
        {
            for(i = 0; i < SUB_BLK_WD; i++)
            {
                pu1_dst[i] = (UWORD8)AVG(pu1_row_1[i], pu1_row_2[i]);
            }
            pu1_row_1 += src_strd;
            pu1_row_2 += src_strd;
            pu1_dst   += out_strd;
        }

        pu1_out += SUB_BLK_WD;
        pu1_src += SUB_BLK_WD;
    }
}