#ifndef __TCOMDICTCODER__
#define __TCOMDICTCODER__

#include "TLibEncoder/TEncEntropy.h"
#include "TComSlice.h"
#include "TypeDef.h"
#include <stdio.h>

#define  PREDICT_LIT 1
#define  MATCH_FINDER_CUT_VALUE 2048 //1536 

#define kMaxValForNormalize ((UInt)0xFFFFFFFF)
#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */
#define kNormalizeMask (~(kNormalizeStepMin - 1))

#define NUM_REPS 8

#define REORDER_DATA 1	
#define YUV420_REORDER_TYPE 2



#define kLenNumLowBits 3
#define kLenNumLowSymbols (1 << kLenNumLowBits)
#define kLenNumMidBits 3
#define kLenNumMidSymbols (1 << kLenNumMidBits)
#define kLenNumHighBits 8
#define kLenNumHighSymbols (1 << kLenNumHighBits)
#if SUPER_EXTEND_RUN
#define kLenNumSymbolsTotal 4096 //64x64
#else 
#if EXTEND_RUN
#define kLenNumSuperHighBits 8
#define kLenNumSuperHighSymbols (1 << kLenNumSuperHighBits)
#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols + kLenNumSuperHighSymbols)
#else
#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
#endif
#endif

#if FORCE_PIXEL_MATCH
#define MATCH_LEN_MIN 1 // pixel starter
#else
#define MATCH_LEN_MIN 2
#endif
#define MATCH_LEN_MAX (MATCH_LEN_MIN + kLenNumSymbolsTotal - 1)

#define DEBUG_DICTCODER 0

#define TRY_BACK_UP_HASHBASETABLE 1

#define TRY_LOSSY_MATCH  0
//#define CRC_24_to_16     1
#define MORE_NEW_HASH   1

#define ABS_DIFF(p1,p2) (p1)>=(p2)?((p1)-(p2)):((p2)-(p1))
#define IS_LOSSY_EQUAL(p1,p2,p3) (((ABS_DIFF(p1,p2))<=(p3))?1:0)
#define IS_LOSSY_EQUAL_COPY(p1,p2,p3) ((result=IS_LOSSY_EQUAL(p1,p2,p3)),(result?(p2)=(p1):0),result)

#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist))

#define K_CRC_POLY 0xEDB88320
//#define CRC_POLY_16 0xA02B

#define  TRY_10_BIT

typedef struct _CSeqInStreamBuf
{
  const UChar *data;
  UInt rem;
} CSeqInStreamBuf;

typedef struct _CMatchFinder
{

  UChar *buffer;
  UInt pos;
  UInt posLimit;
  UInt streamPos;
  UInt lenLimit;

  UInt cyclicBufferPos;
  UInt cyclicBufferSize; /* it must be = (historySize + 1) */

  UInt matchMaxLen;
  UInt *m_pHash;
  UInt *m_pSon;
  UInt hashMask;
  UInt m_uCutValue;

  UChar *bufferBase;
  CSeqInStreamBuf *stream;

  UInt blockSize;
  UInt keepSizeBefore;
  UInt keepSizeAfter;

  UInt historySize;
  UInt hashSizeSum;
  UInt numSons;
  UInt result;
  UInt crc[256];

  bool m_bEncOrg; 
  int level;
}CMatchFinder;

typedef struct 
{
	CMatchFinder mf;
	UInt64 m_uNowPos64;
 	UInt m_uReps[NUM_REPS];
}CSaveState;



class CDictEncoder
{
public:
    //create/destroy
  CDictEncoder();
  ~CDictEncoder(){};

private:


  UInt m_uLongestMatchLength;
  UInt m_uNumPairs;
  UInt m_uNumAvail;

  UChar m_pbFastPos[1 << 11];

  UInt m_uMatches[MATCH_LEN_MAX * 2 *3 + 2 + 1];

  UInt m_uNumFastBytes;
  UInt m_uAdditionalOffset;

  UInt m_uDictSize;

  bool m_bIsLastCuRollBack;
  bool m_bFirstInit;
#if TRY_BACK_UP_HASHBASETABLE
  UInt * m_pHashBaseBak;
#endif

#if DEBUG_DICTCODER
  FILE * m_pLenPos;
#endif

 public:
  CSaveState m_cSaveState;
  UInt64 m_uNowPos64;
  UInt m_uReps[NUM_REPS];
  UInt m_uCurrPackSize;
  UInt m_uPrevPackSize;
  CMatchFinder m_matchFinderBase;
  UInt m_uCuLen;
  CSeqInStreamBuf m_cSeqBufInStream;
  int m_uCuIdx;
  int m_iDictCoderLevel;
  int m_iHashLength;
#if MORE_NEW_HASH
  int m_iHashBitDis[3];
#endif
  int m_iHashMethod;
#if CRC_24_to_16
  UInt * m_pCRC;
#endif 
#if EXTEND_RUN
  list<int64_t> m_pStrMapBufList;
#else
  list<UInt> m_pStrMapBufList;
#endif
  UInt m_uiMatchDiffAllow;
  Bool m_bFirst;


public:
  void Init(UChar * pSrcBuf);
  void MatchFinder_ReadIfRequired(CMatchFinder *p);
  void DictCodeOneBlock();
  void MatchFinder_ReadBlock(CMatchFinder *p);
  void MatchFinder_CheckLimits(CMatchFinder *p);
  void MatchFinder_MoveBlock(CMatchFinder *p);
  int MatchFinder_NeedMove(CMatchFinder *p);
  void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p);
  void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt subValue);
  void MatchFinder_Normalize3(UInt subValue, UInt *items, UInt numItems);
  UInt MatchFinder_GetSubValue(CMatchFinder *p);
  void MatchFinder_Normalize(CMatchFinder *p);
  void MatchFinder_SetLimits(CMatchFinder *p);
  void MatchFinder_Init(CMatchFinder *p);
  UInt MatchFinder_GetNumAvailableBytes(CMatchFinder *p);
  UChar * MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); 

  //int  ReadBlockData(void *pp, void *data, UInt *size); //ZF Orginal
  int  ReadBlockData(CSeqInStreamBuf*pp, UChar*data, size_t*size);
  void DictEnc_RestoreState();
  void DictEnc_SaveState();

  UInt Hc3_MatchFinder_GetMatches(CMatchFinder *p, UInt *distances);
  void Hc3_MatchFinder_Skip(CMatchFinder *p, UInt num);
  UInt createHashValue (CMatchFinder *p, const UChar *pCurSample );

  int MatchFinder_Create(CMatchFinder *p, UInt historySize,  UInt matchMaxLen, UInt keepAddBufferAfter);
  void MatchFinder_Free(CMatchFinder *p);

  void MatchFinder_Construct(CMatchFinder *p);
  void DictEnc_Construct();
  int  DictInWindow_Create(CMatchFinder *p, UInt keepSizeReserv);
  void DictEnc_FastPosInit();
  int DictEnc_Alloc();
  int DictEnc_AllocAndInit();
  void DictEnc_Init();
  int GetPosSlotFunc(int pos);
  void WriteStrMapResult(int len, int pos,const UChar * pCurBuf);


#if EXTEND_RUN
  void WriteBits(int64_t & uiDst, int64_t value, UInt numBits, UInt uiStartPos);
#else
  void WriteBits(UInt & uiDst,UInt value, UInt numBits,  UInt uiStartPos);
#endif

  UInt ReadMatchDistances(UInt *numDistancePairsRes);
  void WriteStrMapResultSimple(int len, int pos,const UChar * pCurBuf);


  UInt GetOptimumFast(UInt *backRes);
  void MovePos(UInt num);

  UInt * Hc_GetMatchesSpec(UInt lenLimit, UInt curMatch, UInt pos, const UChar *cur, UInt *son, UInt _cyclicBufferPos, 
    UInt _cyclicBufferSize, UInt cutValue, UInt *distances, UInt maxLen);

  CMatchFinder * GetMatchFinderObj(){return &m_matchFinderBase;}
  void SetMatchFinderObj(CMatchFinder mf){m_matchFinderBase = mf;}
};

class TComReOrderData
{
private:
 UInt m_uCuWidth;
 UInt m_uCuHeight;
 UInt m_uCuLen;  
 ChromaFormat m_eChForm;
public:
  void InitPara(UInt cuWidth, UInt cuHeight, UInt cuLen, ChromaFormat chForm);
  void TranposeCu(UChar * pCuBuf);
  void ReOrderDataEncYuv444(UChar * pCuBuf);
  void ReOrderDataDecYuv444(UChar * pcuBuf);
  void ReOrderDataEncYuv422(UChar * pCuBuf);
  void ReOrderDataDecYuv422(UChar * pcuBuf);
  void ReOrderDataEncYuv420(UChar * pcuBuf, UInt flag);
  void ReOrderDataDecYuv420(UChar * pcuBuf, UInt flag);
};

class TComDictEnc 
{
private:
  UInt m_uCuWidth;
  UInt m_uCuHeight;
  UInt m_uCuLen;  
  UChar * m_pCuBuf;
  
  UInt m_uPicWidth;
  UInt m_uPicHeight;
  UInt m_uPicLen;
  UInt m_uPicLenPad;

  int m_iDictCoderLevel;
  int m_iP2MLossyLevel;
  int m_iHashTableLevel;
  int m_iHashMethod;
  
  CDictEncoder * m_pDictEncoder;
  TComReOrderData * m_pReOrder;

  UInt * m_pPackSizeBuf;
  UInt m_uCuEncNumByLz;

  bool m_bIsFirstRollBack;
  bool m_bNeedResetEncoder;
  bool m_bNeedRollBack;

  UInt m_uiStartPackPos;


public:
  //create/destroy
  TComDictEnc();
  ~TComDictEnc(){};

public:
  void DictCompressCu(UInt cuIdx, UInt cuEndIdx, bool bEncOrg, TComSlice* pcSlice);

  void InitEncoder(TComSlice* pcSlice,UInt cuCnt);
#if RESET_FIX
  void ResetHash();
#endif
  void CopyCuFromPicYuv(TComPic* pcPic,UChar* pDst ,UInt cuIdx, bool bEncOrg);
  void WriteCu2RecYuv(TComDataCU* pcCU, UInt cuIdx); 
#if MV_CODING  
  void WriteBitstreamDictEnc(UInt cuIdx, UInt cuEndIdx, TEncEntropy * pEncEty, UInt isLastCuRollBack, TComPic* rpcPic);
#else
#if POS_CODING
  void WriteBitstreamDictEnc(UInt cuIdx, UInt cuEndIdx, TEncEntropy * pEncEty, UInt isLastCuRollBack, TComPic* rpcPic);
#else
  void WriteBitstreamDictEnc(UInt cuIdx, UInt cuEndIdx, TEncEntropy * pEncEty, UInt isLastCuRollBack);
#endif
#endif
  void ResetForNewPic(int cuCnt);
  void destroy();
  UInt xGetSSE();

  UInt GetCuLen(){return m_uCuLen;}

  CDictEncoder * GetDictEncoder(){return m_pDictEncoder;}
  void SetNeedRollBackVar(bool val){m_bNeedRollBack = val;}
  void SetDictCoderLevel(int i, int j, int k, int l){m_iDictCoderLevel = i;m_iP2MLossyLevel = j;   m_iHashTableLevel =k; m_iHashMethod =l;}
  int GetDictCoderLevel(){return m_iDictCoderLevel;}
#if CRC_24_to_16
  UInt * m_pCRC;
#endif 
};

class TComDictDec 
{
private:
  UInt m_uPicWidth;
  UInt m_uPicHeight;
  UInt m_uCuWidth;
  UInt m_uCuHeight;
  UInt m_uCuLen;
  UInt m_uPicLenPad;
  UChar * m_pDecBuf;
  bool m_bFirst;
  UInt m_uNumUnitInCu;
  TComReOrderData * m_pReOrder;
#if POS_CODING
  UChar m_pbFastPos[1 << 11];
#endif
public:
  //create/destroy
  TComDictDec();
  ~TComDictDec(){};

  void InitDecoder(TComSlice* pcSlice);
#if POS_CODING
  void DictDec_FastPosInit();
  int GetPosSlotFunc(int pos);
#endif
  void WriteCu2Pic(TComPic* pcPic, UInt cuIdx, UInt cuIdxDic, UChar * pDecBuf);
  void CopyCu2Dic(TComPic * pcPic, UInt cuIdx, UInt cuIdxDic, UChar * dstBase);
  void destroy();
  UInt GetCuLen(){return m_uCuLen;}
  UInt GetPicLenPad(){return m_uPicLenPad;}
  UChar * GetDictDecBuf(){return m_pDecBuf;}
  UInt GetNumUnitInCu(){return m_uNumUnitInCu;}
};

#endif
//__TCOMDICTCODER__
