#include "All.h"
#ifdef APE_BACKWARDS_COMPATIBILITY

#include "Anti-Predictor.h"

namespace APE
{

#ifdef ENABLE_COMPRESSION_MODE_NORMAL

void CAntiPredictorNormal0000To3320::AntiPredict(int * pInputArray, int * pOutputArray, int NumberOfElements)
{
    // variable declares
    int * ip, * op, * op1, * op2;
    int p, pw;
    int m;

    // short frame handling
    if (NumberOfElements < 32)
    {
        memcpy(pOutputArray, pInputArray, static_cast<size_t>(NumberOfElements) * 4);
        return;
    }

    ////////////////////////////////////////
    // order 3
    ////////////////////////////////////////
    memcpy(pOutputArray, pInputArray, 32);

    // initialize values
    m = 300;
    op = &pOutputArray[8];
    op1 = &pOutputArray[7];
    op2 = &pOutputArray[6];

    // make the first prediction
    p = (pOutputArray[7] * 3) - (pOutputArray[6] * 3) + pOutputArray[5];
    pw = (p * m) >> 12;

    // loop through the array
    for (ip = &pInputArray[8]; ip < &pInputArray[NumberOfElements]; ip++, op++, op1++, op2++)
    {
        // figure the output value
        *op = *ip + pw;

        // adjust m
        if (*ip > 0)
            m += (p > 0) ? 4 : -4;
        else if (*ip < 0)
            m += (p > 0) ? -4 : 4;

        // make the next prediction
        p = (*op * 3) - (*op1 * 3) + *op2;
        pw = (p * m) >> 12;
    }


    ///////////////////////////////////////
    // order 2
    ///////////////////////////////////////
    memcpy(pInputArray, pOutputArray, 32);
    m = 3000;

    op1 = &pInputArray[7];
    p = (*op1 * 2) - pInputArray[6];
    pw = (p * m) >> 12;

    for (op = &pInputArray[8], ip = &pOutputArray[8]; ip < &pOutputArray[NumberOfElements]; ip++, op++, op1++)
    {
        *op = *ip + pw;

        // adjust m
        if (*ip > 0)
            m += (p > 0) ? 12 : -12;
        else if (*ip < 0)
            m += (p > 0) ? -12 : 12;

        p = (*op * 2) - *op1;
        pw = (p * m) >> 12;

    }

    ///////////////////////////////////////
    // order 1
    ///////////////////////////////////////
    pOutputArray[0] = pInputArray[0];
    pOutputArray[1] = pInputArray[1] + pOutputArray[0];
    pOutputArray[2] = pInputArray[2] + pOutputArray[1];
    pOutputArray[3] = pInputArray[3] + pOutputArray[2];
    pOutputArray[4] = pInputArray[4] + pOutputArray[3];
    pOutputArray[5] = pInputArray[5] + pOutputArray[4];
    pOutputArray[6] = pInputArray[6] + pOutputArray[5];
    pOutputArray[7] = pInputArray[7] + pOutputArray[6];

    m = 3900;

    p = pOutputArray[7];
    pw = (p * m) >> 12;

    for (op = &pOutputArray[8], ip = &pInputArray[8]; ip < &pInputArray[NumberOfElements]; ip++, op++)
    {
        *op = *ip + pw;

        // adjust m
        if (*ip > 0)
            m += (p > 0) ? 1 : -1;
        else if (*ip < 0)
            m += (p > 0) ? -1 : 1;

        p = *op;
        pw = (p * m) >> 12;
    }

}

void CAntiPredictorNormal3320To3800::AntiPredict(int * pInputArray, int * pOutputArray, int NumberOfElements)
{
    // variable declares
    int q;

    // short frame handling
    if (NumberOfElements < 8)
    {
        memcpy(pOutputArray, pInputArray, static_cast<size_t>(NumberOfElements) * 4);
        return;
    }

    // make the first five samples identical in both arrays
    memcpy(pOutputArray, pInputArray, 20);

    // initialize values
    int m1 = 0;
    int m2 = 64;
    int m3 = 28;
    int OP0;

    int p3 = (3 * (pOutputArray[4] - pOutputArray[3])) + pOutputArray[2];
    int p2 = pInputArray[4] + ((pInputArray[2] - pInputArray[3]) << 3) - pInputArray[1] + pInputArray[0];
    int p1 = pOutputArray[4];

    for (q = 5; q < NumberOfElements; q++)
    {
        OP0 = pInputArray[q] + ((p1 * m1) >> 8);
        (pInputArray[q] ^ p1) > 0 ? m1++ : m1--;
        p1 = OP0;

        pInputArray[q] = OP0 + ((p2 * m2) >> 11);
        (OP0 ^ p2) > 0 ? m2++ : m2--;
        p2 = pInputArray[q] + ((pInputArray[q - 2] - pInputArray[q - 1]) << 3) - pInputArray[q - 3] + pInputArray[q - 4];

        pOutputArray[q] = pInputArray[q] + ((p3 * m3) >> 9);
        (pInputArray[q] ^ p3) > 0 ? m3++ : m3--;

        const int * pBack = &pOutputArray[q - 2];
        p3 = (3 * (pBack[2] - pBack[1])) + pBack[0];
    }

    int m4 = 370;
    int m5 = 3900;

    pOutputArray[1] = pInputArray[1] + pOutputArray[0];
    pOutputArray[2] = pInputArray[2] + pOutputArray[1];
    pOutputArray[3] = pInputArray[3] + pOutputArray[2];
    pOutputArray[4] = pInputArray[4] + pOutputArray[3];

    int p4 = (2 * pInputArray[4]) - pInputArray[3];
    int p5 = pOutputArray[4];
    int IP0, IP1;

    IP1 = pInputArray[4];
    for (q = 5; q < NumberOfElements; q++)
    {
        int * pOutputArrayQ = &pOutputArray[q];
        IP0 = *pOutputArrayQ + ((p4 * m4) >> 9);
        (*pOutputArrayQ ^ p4) > 0 ? m4++ : m4--;
        p4 = (2 * IP0) - IP1;

        *pOutputArrayQ = IP0 + ((p5 * m5) >> 12);
        (IP0 ^ p5) > 0 ? m5++ : m5--;
        p5 = *pOutputArrayQ;

        IP1 = IP0;
    }
}

void CAntiPredictorNormal3800ToCurrent::AntiPredict(int * pInputArray, int * pOutputArray, int NumberOfElements)
{
    // the frame to start prediction on
    #define FIRST_ELEMENT    4

    // short frame handling
    if (NumberOfElements < 8)
    {
        memcpy(pOutputArray, pInputArray, static_cast<size_t>(NumberOfElements) * 4);
        return;
    }

    // make the first five samples identical in both arrays
    memcpy(pOutputArray, pInputArray, FIRST_ELEMENT * 4);

    // variable declares and initializations
    int m2 = 64, m3 = 115, m4 = 64, m5 = 740, m6 = 0;
    int p4 = pInputArray[FIRST_ELEMENT - 1];
    int p3 = (pInputArray[FIRST_ELEMENT - 1] - pInputArray[FIRST_ELEMENT - 2]) << 1;
    int p2 = pInputArray[FIRST_ELEMENT - 1] + ((pInputArray[FIRST_ELEMENT - 3] - pInputArray[FIRST_ELEMENT - 2]) << 3);// - pInputArray[3] + pInputArray[2];
    int * op = &pOutputArray[FIRST_ELEMENT];
    int * ip = &pInputArray[FIRST_ELEMENT];
    int IPP2 = ip[-2];
    int p7 = 2 * ip[-1] - ip[-2];
    int opp = op[-1];

    // undo the initial prediction stuff
    for (int q = 1; q < FIRST_ELEMENT; q++)
    {
        pOutputArray[q] += pOutputArray[q - 1];
    }

    // pump the primary loop
    for (; op < &pOutputArray[NumberOfElements]; op++, ip++)
    {

        const int i = *ip;

        /////////////////////////////////////////////
        int o = i + (((p2 * m2) + (p3 * m3) + (p4 * m4)) >> 11);

        if (i > 0)
        {
            m2 -= ((p2 >> 30) & 2) - 1;
            m3 -= ((p3 >> 28) & 8) - 4;
            m4 -= ((p4 >> 28) & 8) - 4;

        }
        else if (i < 0)
        {
            m2 += ((p2 >> 30) & 2) - 1;
            m3 += ((p3 >> 28) & 8) - 4;
            m4 += ((p4 >> 28) & 8) - 4;
        }


        p2 = o + ((IPP2 - p4) << 3);
        p3 = (o - p4) << 1;
        IPP2 = p4;
        p4 = o;

        /////////////////////////////////////////////
        o += (((p7 * m5) - (opp * m6)) >> 10);

        if (p4 > 0)
        {
            m5 -= ((p7 >> 29) & 4) - 2;
            m6 += ((opp >> 30) & 2) - 1;
        }
        else if (p4 < 0)
        {
            m5 += ((p7 >> 29) & 4) - 2;
            m6 -= ((opp >> 30) & 2) - 1;
        }

        p7 = 2 * o - opp;
        opp = o;

        /////////////////////////////////////////////
        *op = o + ((op[-1] * 31) >> 5);
    }
}

#endif // #ifdef ENABLE_COMPRESSION_MODE_NORMAL

}

#endif // #ifdef APE_BACKWARDS_COMPATIBILITY
