/* The copyright in this software is being made available under the BSD
 * License, included below. This software may be subject to other third party
 * and contributor rights, including patent rights, and no such rights are
 * granted under this license.
 *
 * Copyright (c) 2010-2023, ITU/ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

/** \file     SEIFilmGrainSynthesizer.h
    \brief    SMPTE RDD5 based film grain synthesis functionality from SEI messages
*/

#ifndef __SEIFILMGRAINSYNTHESIZER__
#define __SEIFILMGRAINSYNTHESIZER__

#include "SEI.h"
#include "Unit.h"
#include "Buffer.h"
#include "Unit.h"

#include "TrQuant_EMT.h"


//! \ingroup SEIFilmGrainSynthesizer
//! \{

// ====================================================================================================================
// Class definition
// ====================================================================================================================

static constexpr int MIN_LOG2SCALE_VALUE             = 2;
static constexpr int MAX_LOG2SCALE_VALUE             = 7;
static constexpr int FILM_GRAIN_MODEL_ID_VALUE       = 0;
static constexpr int BLENDING_MODE_VALUE             = 0;
static constexpr int MIN_CUT_OFF_FREQUENCY           = 2;
static constexpr int MAX_CUT_OFF_FREQUENCY           = 14;
static constexpr int DEFAULT_HORZ_CUT_OFF_FREQUENCY  = 8;
static constexpr int NUM_CUT_OFF_FREQ                = 13;

static constexpr int SCALE_DOWN_422                  = 181; /* in Q-format of 8 : 1/sqrt(2) */
static constexpr int Q_FORMAT_SCALING                = 8;
static constexpr int GRAIN_SCALE                     = 6;
static constexpr int MIN_CHROMA_FORMAT_IDC           = 0;
static constexpr int MAX_CHROMA_FORMAT_IDC           = 3;
static constexpr int MIN_BIT_DEPTH                   = 8;
static constexpr int MAX_BIT_DEPTH                   = 16;
static constexpr int BLK_8_shift                     = 6;
static constexpr int BLK_16_shift                    = 8;
static constexpr int BLK_32_shift                    = 10;
static constexpr int NUM_8x8_BLKS_16x16              = 4;
static constexpr int NUM_16x16_BLKS_32x32            = 4;
static constexpr int BLK_AREA_8x8                    = 64;
static constexpr int BLK_AREA_16x16                  = 256;
static constexpr int INTENSITY_INTERVAL_MATCH_FAIL   = -1;
static constexpr int COLOUR_OFFSET_LUMA              = 0;
static constexpr int COLOUR_OFFSET_CR                = 85;
static constexpr int COLOUR_OFFSET_CB                = 170;

static constexpr int MIN_WIDTH                       = 128;
static constexpr int MAX_WIDTH                       = 7680;
static constexpr int MIN_HEIGHT                      = 128;
static constexpr int MAX_HEIGHT                      = 4320;

#define CLIP3(min, max, x)              (((x) > (max)) ? (max) :(((x) < (min))? (min):(x)))
#define MIN(x,y)                        (((x) > (y)) ? (y) : (x))
#define MAX(x,y)                        (((x) > (y)) ? (x) : (y))
#define MSB16(x)                        ((x&0xFFFF0000)>>16)
#define LSB16(x)                        (x&0x0000FFFF)
#define BIT0(x)                         (x&0x1)
#define POS_30                          (1<<30)
#define POS_2                           (1<<2)

/* Error start codes for various classes of errors */
#define FGS_FILE_IO_ERROR               0x0010
#define FGS_PARAM_ERROR                 0x0020

/* Error codes for various errors in SMPTE-RDD5 standalone grain synthesizer */
typedef enum
{
  /* No error */
  FGS_SUCCESS = 0,
  /* Invalid input width */
  FGS_INVALID_WIDTH = FGS_FILE_IO_ERROR + 0x01,
  /* Invalid input height */
  FGS_INVALID_HEIGHT = FGS_FILE_IO_ERROR + 0x02,
  /* Invalid Chroma format idc */
  FGS_INVALID_CHROMA_FORMAT = FGS_FILE_IO_ERROR + 0x03,
  /* Invalid bit depth */
  FGS_INVALID_BIT_DEPTH = FGS_FILE_IO_ERROR + 0x04,
  /* Invalid Film grain characteristic cancel flag */
  FGS_INVALID_FGC_CANCEL_FLAG = FGS_PARAM_ERROR + 0x01,
  /* Invalid film grain model id */
  FGS_INVALID_GRAIN_MODEL_ID = FGS_PARAM_ERROR + 0x02,
  /* Invalid separate color description present flag */
  FGS_INVALID_SEP_COL_DES_FLAG = FGS_PARAM_ERROR + 0x03,
  /* Invalid blending mode */
  FGS_INVALID_BLEND_MODE = FGS_PARAM_ERROR + 0x04,
  /* Invalid log_2_scale_factor value */
  FGS_INVALID_LOG2_SCALE_FACTOR = FGS_PARAM_ERROR + 0x05,
  /* Invalid component model present flag */
  FGS_INVALID_COMP_MODEL_PRESENT_FLAG = FGS_PARAM_ERROR + 0x06,
  /* Invalid number of model values */
  FGS_INVALID_NUM_MODEL_VALUES = FGS_PARAM_ERROR + 0x07,
  /* Invalid bound values, overlapping boundaries */
  FGS_INVALID_INTENSITY_BOUNDARY_VALUES = FGS_PARAM_ERROR + 0x08,
  /* Invalid standard deviation */
  FGS_INVALID_STANDARD_DEVIATION = FGS_PARAM_ERROR + 0x09,
  /* Invalid cut off frequencies */
  FGS_INVALID_CUT_OFF_FREQUENCIES = FGS_PARAM_ERROR + 0x0A,
  /* Invalid number of cut off frequency pairs */
  FGS_INVALID_NUM_CUT_OFF_FREQ_PAIRS = FGS_PARAM_ERROR + 0x0B,
  /* Invalid film grain characteristics repetition period */
  FGS_INVALID_FGC_REPETETION_PERIOD = FGS_PARAM_ERROR + 0x0C,

  /* Failure error code */
  FGS_FAIL = 0xFF
}FGS_ERROR_T;
/* FGC Error Codes END */

typedef struct GrainSynthesisStruct_t
{
  int8_t  dataBase[NUM_CUT_OFF_FREQ][NUM_CUT_OFF_FREQ][DATA_BASE_SIZE][DATA_BASE_SIZE];
  int16_t intensityInterval[MAX_NUM_COMPONENT][MAX_NUM_INTENSITIES];
}GrainSynthesisStruct;

typedef struct fgsProcessArgs
{
  uint8_t                      numComp;
  uint32_t *                   fgsOffsets[MAX_NUM_COMPONENT];
  Pel *                        decComp[MAX_NUM_COMPONENT];
  uint32_t                     widthComp[MAX_NUM_COMPONENT];
  uint32_t                     heightComp[MAX_NUM_COMPONENT];
  ptrdiff_t                    strideComp[MAX_NUM_COMPONENT];
  SEIFilmGrainCharacteristics *pFgcParameters;
  GrainSynthesisStruct *       pGrainSynt;
  uint8_t                      bitDepth;
  uint8_t                      blkSize;
} fgsProcessArgs;

class SEIFilmGrainSynthesizer
{

private:
  uint32_t                     m_width;
  uint32_t                     m_height;
  ChromaFormat                 m_chromaFormat;
  uint8_t                      m_bitDepth;
  uint32_t                     m_idrPicId;

  fgsProcessArgs               m_fgsArgs;
  GrainSynthesisStruct        *m_grainSynt;
  uint8_t                      m_fgsBlkSize;

public:
  uint32_t                     m_poc;
  int32_t                      m_errorCode;
  SEIFilmGrainCharacteristics *m_fgcParameters;

public:
  SEIFilmGrainSynthesizer();
  virtual ~SEIFilmGrainSynthesizer();

  void      create(uint32_t width, uint32_t height, ChromaFormat fmt, uint8_t bitDepth, uint32_t idrPicId);
  void      destroy   ();

  void      fgsInit   ();
  void      grainSynthesizeAndBlend (PelStorage* pGrainBuf, bool isIdrPic);
  uint8_t   grainValidateParams     ();

private:
  void            deriveFGSBlkSize    ();
  void            dataBaseGen         ();
  static uint32_t prng                (uint32_t x_r);
  static uint32_t fgsProcess          (fgsProcessArgs &inArgs);

  static void     deblockGrainStripe  (Pel *grainStripe, uint32_t widthComp, uint32_t heightComp, uint32_t strideComp,
                                      uint32_t blkSize);
  static void     blendStripe(Pel *decSampleOffsetY, Pel *grainStripe, uint32_t widthComp, ptrdiff_t strideSrc,
                              ptrdiff_t strideGrain, uint32_t blockHeight, uint8_t bitDepth);
  static void     blendStripe_32x32(Pel *decSampleOffsetY, Pel *grainStripe, uint32_t widthComp, ptrdiff_t strideSrc,
                                    ptrdiff_t strideGrain, uint32_t blockHeight, uint8_t bitDepth);

  static Pel      blockAverage_8x8(Pel *decSampleBlk8, ptrdiff_t widthComp, uint16_t *pNumSamples, uint8_t ySize,
                                   uint8_t xSize, uint8_t bitDepth);
  static uint32_t blockAverage_16x16(Pel *decSampleBlk8, ptrdiff_t widthComp, uint16_t *pNumSamples, uint8_t ySize,
                                     uint8_t xSize, uint8_t bitDepth);
  static uint32_t blockAverage_32x32(Pel *decSampleBlk32, ptrdiff_t strideComp, uint8_t bitDepth);

  static void     simulateGrainBlk8x8 (Pel *grainStripe, uint32_t grainStripeOffsetBlk8, GrainSynthesisStruct *pGrainSynt,
                                      uint32_t width, uint8_t log2ScaleFactor, int16_t scaleFactor, uint32_t kOffset,
                                      uint32_t lOffset, uint8_t h, uint8_t v, uint32_t xSize);
  static void     simulateGrainBlk16x16(Pel *grainStripe, uint32_t grainStripeOffsetBlk8, GrainSynthesisStruct *grain_synt,
                                        uint32_t width, uint8_t log2ScaleFactor, int16_t scaleFactor, uint32_t kOffset,
                                        uint32_t lOffset, uint8_t h, uint8_t v, uint32_t xSize);
  static void     simulateGrainBlk32x32(Pel *grainStripe, uint32_t grainStripeOffsetBlk32, GrainSynthesisStruct *grain_synt,
                                        uint32_t width, uint8_t log2ScaleFactor, int16_t scaleFactor, uint32_t kOffset,
                                        uint32_t lOffset, uint8_t h, uint8_t v);

  static uint32_t fgsSimulationBlending_8x8   (fgsProcessArgs *inArgs);
  static uint32_t fgsSimulationBlending_16x16 (fgsProcessArgs *inArgs);
  static uint32_t fgsSimulationBlending_32x32 (fgsProcessArgs *inArgs);

};// END CLASS DEFINITION SEIFilmGrainSynthesizer

//! \}

#endif // __SEIFILMGRAINSYNTHESIZER__


