#include "Df_TurboEncoder.h"
#ifndef IS_PIPELINE
#ifndef IS_BARE

void hardware_Interleaving(
        int SeqLen,
        int* SeqLen_Out,
        int OutBlockShift_In,
        int* OutBlockShift_Out,
        int hw_Rate_In,
        int* hw_Rate_Out,
        int FSM_I_In,
        int* FSM_I_Out,
        int FSM_S_In,
        int* FSM_S_Out,
        int FSM_pNS_In[MAX_I*MAX_S],
        int FSM_pNS_Out[MAX_I*MAX_S],
        int FSM_pOS_In[MAX_I*MAX_S],
        int FSM_pOS_Out[MAX_I*MAX_S],
        int FSM_pTMl_In[MAX_S*MAX_S],
        int FSM_pTMl_Out[MAX_S*MAX_S],
        int FSM_pTMi_In[MAX_S*MAX_S],
        int FSM_pTMi_Out[MAX_S*MAX_S],
        int hw_pcSeq_In[MAX_RATE][6144+4],
        int hw_pcSeq_Out[MAX_RATE][6144+4],
        int pInpSeq [MAX_BlockLen],
        int pOutSeq [MAX_BlockLen],
        int paraTable[188][3]
        )
{
    *SeqLen_Out = SeqLen;
    *OutBlockShift_Out = OutBlockShift_In;
    *hw_Rate_Out = hw_Rate_In;
    *FSM_I_Out = FSM_I_In;
    *FSM_S_Out = FSM_S_In;
    for(int i=0; i<FSM_I_In*FSM_S_In; i++)
    {
	#ifdef IS_HLS
	#pragma HLS PIPELINE enable_flush
	DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_I*MAX_S)
	#endif
        FSM_pNS_Out[i] = FSM_pNS_In[i];
        FSM_pOS_Out[i] = FSM_pOS_In[i];
    }
    for(int i=0; i<FSM_S_In*FSM_S_In; i++)
    {
	#ifdef IS_HLS
	#pragma HLS PIPELINE enable_flush
	DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S*MAX_S)
	#endif
        FSM_pTMl_Out[i] = FSM_pTMl_In[i];
        FSM_pTMi_Out[i] = FSM_pTMi_In[i];
    }

    for(int i=0; i<3; i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    #endif
        for(int j=0; j<6144+4; j++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        #endif
            hw_pcSeq_Out[i][j] =  hw_pcSeq_In[i][j];
        }
    }
    //************************ Permutation Pattern *****************************//
    int K = SeqLen;
    int f1,f2;
    bool continueflag = 1;
    for(int idx = 187;idx>=0&&continueflag;idx--)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=188)
    #endif
        if(paraTable[idx][0]==SeqLen)
            {
            f1 = paraTable[idx][1];
            f2 = paraTable[idx][2];
            continueflag = 0;
            }
    }
    int pPi[6148];
    long long tmp;
    for(int idx=0;idx<K;idx++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_BlockLen)
    #endif
        tmp=((((long long)idx)*((long long)f1))+((((long long)idx)*((long long)idx))*((long long)f2)));
        pPi[idx]=(int)(tmp%((long long)K));
    }
    //********************* Start Interleaving ****************************//
    for(int idx=0;idx<K;idx++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_BlockLen)
    #endif
        pOutSeq[idx] = pInpSeq[pPi[idx]];
    }
}

int hardware_maxElen(
        int num,
        int p[MAX_S*MAX_S],
        int pos,
        int space)
{
    int mamm = p[0];
    for(int i=0;i<num;i++)
    {
	#ifdef IS_HLS
	#pragma HLS PIPELINE enable_flush
	DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
	#endif
        mamm = (mamm>p[i*space+pos])?mamm:p[i*space+pos];
    }
    return mamm;
}

void Hardware_TurboEncoding_f1(
    int nBlock,
    int* SeqLen_Out,
    int* OutBlockShift_Out,
    int hw_Rate_In,
    int* hw_Rate_Out,
    int FSM_I_In,
    int* FSM_I_Out,
    int FSM_S_In,
    int* FSM_S_Out,
    int FSM_pNS_In[MAX_I*MAX_S],
    int FSM_pNS_Out[MAX_I*MAX_S],
    int FSM_pOS_In[MAX_I*MAX_S],
    int FSM_pOS_Out[MAX_I*MAX_S],
    int FSM_pTMl_In[MAX_S*MAX_S],
    int FSM_pTMl_Out[MAX_S*MAX_S],
    int FSM_pTMi_In[MAX_S*MAX_S],
    int FSM_pTMi_Out[MAX_S*MAX_S],
    int paraTable_In[188][3],
    int paraTable_Out[188][3],
    int hw_pLengthSet[MAX_NumBlock],
    int hw_piSeq[MAX_DataLength],
    int hw_pcSeq[MAX_RATE][6144+4],
    int hw_piSeq_int [MAX_BlockLen])
{
#ifdef IS_HLS
#pragma HLS INLINE off
#endif

    int iSeqLength=hw_pLengthSet[nBlock];
    int InpBlockShift=nBlock*6144;
    int OutBlockShift=nBlock*(6144+4);


    *SeqLen_Out = iSeqLength;
    *OutBlockShift_Out = OutBlockShift;
    *hw_Rate_Out = hw_Rate_In;
    *FSM_I_Out = FSM_I_In;
    *FSM_S_Out = FSM_S_In;
    for(int i=0; i<FSM_I_In*FSM_S_In; i++)
    {
	#ifdef IS_HLS
	#pragma HLS PIPELINE enable_flush
	DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_I*MAX_S)
	#endif
        FSM_pNS_Out[i] = FSM_pNS_In[i];
        FSM_pOS_Out[i] = FSM_pOS_In[i];
    }
    for(int i=0; i<FSM_S_In*FSM_S_In; i++)
    {
	#ifdef IS_HLS
	#pragma HLS PIPELINE enable_flush
	DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S*MAX_S)
	#endif
        FSM_pTMl_Out[i] = FSM_pTMl_In[i];
        FSM_pTMi_Out[i] = FSM_pTMi_In[i];
    }
    for(int ii=0; ii<188; ii++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    #endif
        for(int jj=0; jj<3; jj++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        #endif
            paraTable_Out[ii][jj] = paraTable_In[ii][jj];
        }
    }
    // Start Encoding this Block
    int cs;         //current state
    int ns;         //next state
    int isym;       //input symbol
    int osym;       //output symbol

    // rsc1
    cs=0;   //start from 0 state
    for(int i=0;i<iSeqLength;i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_NumBlock)
    #endif
        hw_pcSeq[0][i] = hw_piSeq[InpBlockShift+i];
        hw_piSeq_int[i] = hw_piSeq[InpBlockShift+i];
        isym = hw_piSeq[InpBlockShift+i];
        ns  = FSM_pNS_In[(FSM_I_In)*cs+isym];
        osym = FSM_pOS_In[(FSM_I_In)*cs+isym];
        hw_pcSeq[1][i]=osym;
        cs = ns;
    }

    // Adding termination
    int ends = cs; // ending state of Turbo saved in ends
    int maxLen = hardware_maxElen(FSM_S_In,FSM_pTMl_In,0,FSM_S_In);
    int ptaili[MAX_S];
    for(int i=0;i<maxLen;i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
    #endif
        ptaili[i]=0;
    }

    int numidx=0;
    while(cs!=0&&numidx<maxLen)//MAX_S
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
    #endif
        ptaili[numidx]=FSM_pTMi_In[cs*FSM_S_In+0];
        cs = FSM_pNS_In[(FSM_I_In)*cs+(*(ptaili+numidx))];
        numidx++;
    }
    cs = ends; //start from last state to zero-state

    int pT1[6];
    for(int i=0;i<maxLen;i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
    #endif
        isym = ptaili[i];
        ns   = FSM_pNS_In[FSM_I_In*cs+isym];
        osym = FSM_pOS_In[FSM_I_In*cs+isym];
        pT1[2*i+0]=isym;
        pT1[2*i+1]=osym;
        cs = ns;
    }

    for(int i=0;i<2;i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    #endif
        hw_pcSeq[0][iSeqLength+i] =  pT1[i*hw_Rate_In+0];
        hw_pcSeq[1][iSeqLength+i] =  pT1[i*hw_Rate_In+1];
        hw_pcSeq[2][iSeqLength+i] =  pT1[i*hw_Rate_In+2];
    }
}


void Hardware_TurboEncoding_f2(
    int iSeqLength,
    int OutBlockShift,
    int hw_Rate,
    int hw_pcSeq_In[MAX_RATE][6144+4],
    int hw_pcSeq[MAX_RATE][MAX_NumBlock*(6144+4)],
    int FSM_I,
    int FSM_S,
    int FSM_pNS[MAX_I*MAX_S],
    int FSM_pOS[MAX_I*MAX_S],
    int FSM_pTMl[MAX_S*MAX_S],
    int FSM_pTMi[MAX_S*MAX_S],
    int piSeq2[6144])
{
#ifdef IS_HLS
#pragma HLS INLINE off
#endif

    int cs;         //current state
    int ns;         //next state
    int isym;       //input symbol
    int osym;       //output symbol

    // rsc2
    cs = 0; //start from 0 state
    for(int i=0;i<iSeqLength;i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_BlockLen)
    #endif
        isym = piSeq2[i];
        ns  = FSM_pNS[FSM_I*cs+isym];
        osym = FSM_pOS[FSM_I*cs+isym];
        hw_pcSeq[2][OutBlockShift+i]=osym;
        cs = ns;
        hw_pcSeq[0][OutBlockShift+i]=hw_pcSeq_In[0][i];
        hw_pcSeq[1][OutBlockShift+i]=hw_pcSeq_In[1][i];

    }

    // Adding termination
    int ends = cs;
    int maxLen = hardware_maxElen(FSM_S,FSM_pTMl,0,FSM_S);
    int ptail2i[MAX_S];
    for(int i=0;i<maxLen;i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
    #endif
        ptail2i[i]=0;
    }
    int numidx=0;
    while(cs!=0&&numidx<maxLen)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
    #endif
        ptail2i[numidx]=FSM_pTMi[cs*FSM_S+0];
        cs = FSM_pNS[FSM_I*cs+ptail2i[numidx]];
        numidx++;
    }
    cs = ends; //start from last state to zero-state
    int pT2[6];
    for(int i=0;i<maxLen;i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
    #endif
        isym = ptail2i[i];
        ns  = FSM_pNS[FSM_I*cs+isym];
        osym = FSM_pOS[FSM_I*cs+isym];
        pT2[2*i+0]=isym;
        pT2[2*i+1]=osym;
        cs = ns;
    }

    for(int i=0;i<2;i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    #endif
        hw_pcSeq[0][OutBlockShift+iSeqLength+i] =  hw_pcSeq_In[0][iSeqLength+i];
        hw_pcSeq[1][OutBlockShift+iSeqLength+i] =  hw_pcSeq_In[1][iSeqLength+i];
        hw_pcSeq[2][OutBlockShift+iSeqLength+i] =  hw_pcSeq_In[2][iSeqLength+i];
    }

    for(int i=0;i<2;i++)
    {
    #ifdef IS_HLS
    #pragma HLS PIPELINE enable_flush
    #endif
        hw_pcSeq[0][OutBlockShift+iSeqLength+2+i] =  pT2[i*hw_Rate+0];
        hw_pcSeq[1][OutBlockShift+iSeqLength+2+i] =  pT2[i*hw_Rate+1];
        hw_pcSeq[2][OutBlockShift+iSeqLength+2+i] =  pT2[i*hw_Rate+2];
    }
}



void Hardware_TurboEncoding(
    int hw_NumBlock,
    int hw_Rate,
    int hw_pLengthSet[MAX_NumBlock],
    int hw_piSeq[MAX_DataLength],
    int hw_pcSeq[MAX_RATE][MAX_NumBlock*(6144+4)],
    int FSM_I,
    int FSM_S,
    int FSM_pNS[MAX_I*MAX_S],
    int FSM_pOS[MAX_I*MAX_S],
    int FSM_pTMl[MAX_S*MAX_S],
    int FSM_pTMi[MAX_S*MAX_S],
    int paraTable[188][3])
{


	for(int nBlock=0;nBlock<hw_NumBlock;nBlock++)
    {
    #ifdef IS_HLS
#pragma HLS DATAFLOW
    DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_NumBlock)
    #endif
        int iSeqLength_1, iSeqLength_2;
        int OutBlockShift_1, OutBlockShift_2;
        int hw_Rate_1, hw_Rate_2;
        int FSM_I_1, FSM_I_2;
        int FSM_S_1, FSM_S_2;
        int FSM_pNS_1[MAX_I*MAX_S], FSM_pNS_2[MAX_I*MAX_S];
        int FSM_pOS_1[MAX_I*MAX_S], FSM_pOS_2[MAX_I*MAX_S];
        int FSM_pTMl_1[MAX_S*MAX_S], FSM_pTMl_2[MAX_S*MAX_S];
        int FSM_pTMi_1[MAX_S*MAX_S], FSM_pTMi_2[MAX_S*MAX_S];
        int hw_pcSeq_1[MAX_RATE][6144+4];
        int hw_pcSeq_2[MAX_RATE][6144+4];

        int piSeq2[6144];
        int hw_piSeq_int [MAX_BlockLen];

        int paraTable_1[188][3];
         Hardware_TurboEncoding_f1(
                nBlock,
                &iSeqLength_1,
                &OutBlockShift_1,
                hw_Rate,
                &hw_Rate_1,
                FSM_I,
                &FSM_I_1,
                FSM_S,
                &FSM_S_1,
                FSM_pNS,
                FSM_pNS_1,
                FSM_pOS,
                FSM_pOS_1,
                FSM_pTMl,
                FSM_pTMl_1,
                FSM_pTMi,
                FSM_pTMi_1,
                paraTable,
                paraTable_1,
                hw_pLengthSet,
                hw_piSeq,
                hw_pcSeq_1,
                hw_piSeq_int);


        hardware_Interleaving(
                iSeqLength_1,
                &iSeqLength_2,
                OutBlockShift_1,
                &OutBlockShift_2,
                hw_Rate_1,
                &hw_Rate_2,
                FSM_I_1,
                &FSM_I_2,
                FSM_S_1,
                &FSM_S_2,
                FSM_pNS_1,
                FSM_pNS_2,
                FSM_pOS_1,
                FSM_pOS_2,
                FSM_pTMl_1,
                FSM_pTMl_2,
                FSM_pTMi_1,
                FSM_pTMi_2,
                hw_pcSeq_1,
                hw_pcSeq_2,
                hw_piSeq_int,
                piSeq2,
                paraTable_1
                );

        Hardware_TurboEncoding_f2(
            iSeqLength_2,
            OutBlockShift_2,
            hw_Rate_2,
            hw_pcSeq_2,
            hw_pcSeq,
            FSM_I_2,
            FSM_S_2,
            FSM_pNS_2,
            FSM_pOS_2,
            FSM_pTMl_2,
            FSM_pTMi_2,
            piSeq2);



/*
        int iSeqLength=hw_pLengthSet[nBlock];
        int InpBlockShift=nBlock*6144;
        int OutBlockShift=nBlock*(6144+4);

        // Start Encoding this Block
        int cs;         //current state
        int ns;         //next state
        int isym;       //input symbol
        int osym;       //output symbol

        for(int i=0;i<iSeqLength;i++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_BlockLen)
        #endif
            hw_pcSeq[0][OutBlockShift+i] = hw_piSeq[InpBlockShift+i];
        }

        // rsc1
        cs=0;   //start from 0 state
        for(int i=0;i<iSeqLength;i++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_NumBlock)
        #endif
            isym = hw_piSeq[InpBlockShift+i];
            ns  = FSM_pNS[(FSM_I)*cs+isym];
            osym = FSM_pOS[(FSM_I)*cs+isym];
            hw_pcSeq[1][OutBlockShift+i]=osym;
            cs = ns;
        }

        // Adding termination
        int ends = cs; // ending state of Turbo saved in ends
        int maxLen = hardware_maxElen(FSM_S,FSM_pTMl,0,FSM_S);
        int ptaili[MAX_S];
        for(int i=0;i<maxLen;i++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
        #endif
            ptaili[i]=0;
        }

        int numidx=0;
        while(cs!=0&&numidx<maxLen)//MAX_S
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
        #endif
            ptaili[numidx]=FSM_pTMi[cs*FSM_S+0];
            cs = FSM_pNS[(FSM_I)*cs+(*(ptaili+numidx))];
            numidx++;
        }
        cs = ends; //start from last state to zero-state

        int pT1[6];
        for(int i=0;i<maxLen;i++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
        #endif
            isym = ptaili[i];
            ns   = FSM_pNS[FSM_I*cs+isym];
            osym = FSM_pOS[FSM_I*cs+isym];
            pT1[2*i+0]=isym;
            pT1[2*i+1]=osym;
            cs = ns;
        }

        for(int i=0;i<2;i++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        #endif
            hw_pcSeq[0][OutBlockShift+iSeqLength+i] =  pT1[i*hw_Rate+0];
            hw_pcSeq[1][OutBlockShift+iSeqLength+i] =  pT1[i*hw_Rate+1];
            hw_pcSeq[2][OutBlockShift+iSeqLength+i] =  pT1[i*hw_Rate+2];
        }
        cs=-1;
        ns=-1;
        isym=-1;
        osym=-1;

        int piSeq2[6144];
        hardware_Interleaving(
                iSeqLength,
                InpBlockShift,
                hw_piSeq,
                piSeq2,
                paraTable
                );
        //EInter.Interleaving(iSeqLength,(piSeq+InpBlockShift),piSeq2);

        // rsc2
        cs = 0; //start from 0 state
        for(int i=0;i<iSeqLength;i++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_BlockLen)
        #endif
                isym = piSeq2[i];
                ns  = FSM_pNS[FSM_I*cs+isym];
                osym = FSM_pOS[FSM_I*cs+isym];
                hw_pcSeq[2][OutBlockShift+i]=osym;
                cs = ns;
        }

        // Adding termination
        ends = cs;
        maxLen = hardware_maxElen(FSM_S,FSM_pTMl,0,FSM_S);
        int ptail2i[MAX_S];
        for(int i=0;i<maxLen;i++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
        #endif
            ptail2i[i]=0;
        }
        numidx=0;
        while(cs!=0&&numidx<maxLen)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
        #endif
            ptail2i[numidx]=FSM_pTMi[cs*FSM_S+0];
            cs = FSM_pNS[FSM_I*cs+ptail2i[numidx]];
            numidx++;
        }
        cs = ends; //start from last state to zero-state
        int pT2[6];
        for(int i=0;i<maxLen;i++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        DO_PRAGMA(HLS LOOP_TRIPCOUNT max=MAX_S)
        #endif
            isym = ptail2i[i];
            ns  = FSM_pNS[FSM_I*cs+isym];
            osym = FSM_pOS[FSM_I*cs+isym];
            pT2[2*i+0]=isym;
            pT2[2*i+1]=osym;
            cs = ns;
        }

        for(int i=0;i<2;i++)
        {
        #ifdef IS_HLS
        #pragma HLS PIPELINE enable_flush
        #endif
            hw_pcSeq[0][OutBlockShift+iSeqLength+2+i] =  pT2[i*hw_Rate+0];
            hw_pcSeq[1][OutBlockShift+iSeqLength+2+i] =  pT2[i*hw_Rate+1];
            hw_pcSeq[2][OutBlockShift+iSeqLength+2+i] =  pT2[i*hw_Rate+2];
        }
                */
    }
}

#endif
#endif
