/*
 * VVC transform and residual DSP
 *
 * Copyright (C) 2021 Nuo Mi
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
#include "libavutil/frame.h"
#include "libavcodec/bit_depth_template.c"

#include "vvcdec.h"
#include "vvc_data.h"

#include "vvc_inter_template.c"
#include "vvc_intra_template.c"
#include "vvc_filter_template.c"

static void FUNC(add_residual)(uint8_t *_dst, const int *res,
    const int w, const int h, const ptrdiff_t _stride)
{
    pixel *dst          = (pixel *)_dst;

    const int stride    = _stride / sizeof(pixel);

    for (int y = 0; y < h; y++) {
        for (int x = 0; x < w; x++) {
            dst[x] = av_clip_pixel(dst[x] + *res);
            res++;
        }
        dst += stride;
    }
}

static void FUNC(add_residual_joint)(uint8_t *_dst, const int *res,
    const int w, const int h, const ptrdiff_t _stride, const int c_sign, const int shift)
{
    pixel *dst = (pixel *)_dst;

    const int stride = _stride / sizeof(pixel);

    for (int y = 0; y < h; y++) {
        for (int x = 0; x < w; x++) {
            const int r = ((*res) * c_sign) >> shift;
            dst[x] = av_clip_pixel(dst[x] + r);
            res++;
        }
        dst += stride;
    }
}

static void FUNC(pred_residual_joint)(int *buf, const int w, const int h,
    const int c_sign, const int shift)
{
    for (int y = 0; y < h; y++) {
        for (int x = 0; x < w; x++) {
            *buf = ((*buf) * c_sign) >> shift;
            buf++;
        }
    }
}

static void FUNC(transform_bdpcm)(int *coeffs, const int width, const int height,
    const int vertical, const int log2_transform_range)
{
    int x, y;

    if (vertical) {
        coeffs += width;
        for (y = 0; y < height - 1; y++) {
            for (x = 0; x < width; x++)
                coeffs[x] = av_clip_intp2(coeffs[x] + coeffs[x - width], log2_transform_range);
            coeffs += width;
        }
    } else {
        for (y = 0; y < height; y++) {
            for (x = 1; x < width; x++)
                coeffs[x] = av_clip_intp2(coeffs[x] + coeffs[x - 1], log2_transform_range);
            coeffs += width;
        }
    }
}

static void FUNC(ff_vvc_itx_dsp_init)(VVCItxDSPContext *const itx)
{
#define VVC_ITX(TYPE, type, s)                                                  \
        itx->itx[TYPE][TX_SIZE_##s]      = ff_vvc_inv_##type##_##s;             \

#define VVC_ITX_COMMON(TYPE, type)                                              \
        VVC_ITX(TYPE, type, 4);                                                 \
        VVC_ITX(TYPE, type, 8);                                                 \
        VVC_ITX(TYPE, type, 16);                                                \
        VVC_ITX(TYPE, type, 32);

    itx->add_residual                = FUNC(add_residual);
    itx->add_residual_joint          = FUNC(add_residual_joint);
    itx->pred_residual_joint         = FUNC(pred_residual_joint);
    itx->transform_bdpcm             = FUNC(transform_bdpcm);
    VVC_ITX(DCT2, dct2, 2)
    VVC_ITX(DCT2, dct2, 64)
    VVC_ITX_COMMON(DCT2, dct2)
    VVC_ITX_COMMON(DCT8, dct8)
    VVC_ITX_COMMON(DST7, dst7)

#undef VVC_ITX
#undef VVC_ITX_COMMON
}
