/*
 * Copyright 2005-2017 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 *
 * In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
 * virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
 */

int grib_decode_long_array(const unsigned char* p, long *bitp, long bitsPerValue,
        size_t n_vals,long* val) {
    long i=0;
    unsigned long lvalue = 0;

    if(bitsPerValue%8)
    {
        int j=0;
        for(i=0;i < n_vals;i++) {
            lvalue=0;
            for(j=0; j< bitsPerValue;j++){
                lvalue <<= 1;
                if(grib_get_bit( p, *bitp)) lvalue += 1;
                *bitp += 1;
            }
            val[i] = lvalue;
        }
    }  else  {
        int bc;
        int l = bitsPerValue/8;
        size_t o = *bitp/8;

        for(i=0;i < n_vals;i++)
        {
            lvalue  = 0;
            lvalue  <<= 8;
            lvalue |= p[o++] ;

            for ( bc=1; bc<l; bc++ )
            {
                lvalue <<= 8;
                lvalue |= p[o++] ;
            }
            val[i] = lvalue;
        }
        *bitp+=bitsPerValue*n_vals;
    }

    return 0;
}

int grib_decode_double_array(const unsigned char* p, long *bitp, long bitsPerValue,
        double reference_value,double s,double d,
        size_t n_vals,double* val) {
    long i=0;
    unsigned long lvalue = 0;
    double x;

    if(bitsPerValue%8)
    {
        int j=0;
        for(i=0;i < n_vals;i++) {
            lvalue=0;
            for(j=0; j< bitsPerValue;j++){
                lvalue <<= 1;
                if(grib_get_bit( p, *bitp)) lvalue += 1;
                *bitp += 1;
            }
            x=((lvalue*s)+reference_value)*d;
            val[i] = (double)x;
        }
    }  else  {
        int bc;
        int l = bitsPerValue/8;
        size_t o = 0;

        for(i=0;i < n_vals;i++)
        {
            lvalue  = 0;
            lvalue  <<= 8;
            lvalue |= p[o++] ;

            for ( bc=1; bc<l; bc++ )
            {
                lvalue <<= 8;
                lvalue |= p[o++] ;
            }
            x=((lvalue*s)+reference_value)*d;
            val[i] = (double)x;
        }
    }

    return 0;
}

/* code from grib_bits_fast_big_endian_simple.c */
int grib_decode_double_array_complex(const unsigned char* p, long *bitp, long nbits,double reference_value,double s,double* d,size_t size,double* val) {
    long i=0;
    long countOfLeftmostBits=0,leftmostBits=0;
    long startBit;
    long remainingBits = nbits;
    long *pp=(long*)p;
    int inited=0;
    unsigned long uval=0;

    startBit=*bitp;
    remainingBits = nbits;
    if (startBit >= max_nbits) {
        pp+=startBit/max_nbits;
        startBit %= max_nbits;
    }

    if ( (max_nbits%nbits == 0) && (*bitp%nbits == 0) ) {
        for (i=0;i<size;i++) {
            if (startBit == max_nbits) {
                startBit = 0;
                pp++;
            }

            val[i]=VALUE(*pp,startBit,remainingBits);
            val[i]= ((( (val[i]) * s)+reference_value)*d[i/2]);
            startBit+=remainingBits;
            remainingBits=nbits;
        }

    } else {

        for (i=0;i<size;i++) {
            countOfLeftmostBits = startBit + remainingBits;
            if (countOfLeftmostBits > max_nbits) {
                countOfLeftmostBits = max_nbits - startBit;
                remainingBits -= countOfLeftmostBits;
                leftmostBits=(VALUE(*pp,startBit,countOfLeftmostBits)) << remainingBits;
                startBit = 0;
                pp++;
            } else
                leftmostBits = 0;

            val[i]=leftmostBits+(VALUE(*pp,startBit,remainingBits));
            val[i]= ((( (val[i]) * s)+reference_value)*d[i/2]);
            startBit+=remainingBits;
            remainingBits=nbits;
        }
    }

    *bitp+=size*nbits;

    return GRIB_SUCCESS;
}

int grib_encode_long_array(size_t n_vals,const long* val,long bits_per_value,unsigned char* p,long *off)
{
    size_t i=0;
    unsigned long unsigned_val=0;
    unsigned char *encoded=p;
    if(bits_per_value%8){
        for(i=0;i< n_vals;i++){
            unsigned_val=val[i];
            grib_encode_unsigned_longb(encoded, unsigned_val, off , bits_per_value);
        }
    } else{
        for(i=0;i< n_vals;i++){
            int blen=0;
            blen = bits_per_value;
            unsigned_val = val[i];
            while(blen >= 8)
            {
                blen   -= 8;
                *encoded = (unsigned_val >> blen);
                encoded++;
                *off+=8;
            }
        }
    }
    return GRIB_SUCCESS;
}

#define  U_BYTEORDER     static union {unsigned long l; unsigned char c[sizeof(long)];} u_byteorder = {1}
#define  IS_BIGENDIAN()  (u_byteorder.c[sizeof(long) - 1])
#define  GRIBPACK      unsigned char
#define  Error(x, y)   fprintf(stderr, x, y)
#define  restrict

#include "encode_double_array.c"

#undef  U_BYTEORDER
#undef  IS_BIGENDIAN
#undef  GRIBPACK
#undef  Error
#undef  restrict

int grib_encode_double_array(size_t n_vals,const double* val,long bits_per_value,double reference_value,double d,double divisor,unsigned char* p,long *off)
{
    if ( fabs(d - 1) > 0 ) /* d != 1 */
    {
        size_t i=0;
        unsigned long unsigned_val=0;
        unsigned char *encoded=p;
        double x;

        if(bits_per_value%8){
            for(i=0;i< n_vals;i++){
                x=(((val[i]*d)-reference_value)*divisor)+0.5;
                unsigned_val = (unsigned long)x;
                grib_encode_unsigned_longb(encoded, unsigned_val, off , bits_per_value);
            }
        } else{
            for(i=0;i< n_vals;i++){
                int blen=0;
                blen = bits_per_value;
                x = ((((val[i]*d)-reference_value)*divisor)+0.5);
                unsigned_val = (unsigned long)x;
                while(blen >= 8)
                {
                    blen   -= 8;
                    *encoded = (unsigned_val >> blen);
                    encoded++;
                    *off+=8;
                }
            }
        }
    }
    else
    {
#if  defined  (_ARCH_PWR6)
        encode_double_array_unrolled(bits_per_value, 0, n_vals, p, val, reference_value, divisor, off);
#else
        encode_double_array_byte    (bits_per_value, 0, n_vals, p, val, reference_value, divisor, off);
#endif
    }

    return GRIB_SUCCESS;
}

/* code from grib_bits_fast_big_endian_simple.c */
int grib_encode_double_array_complex(size_t n_vals,double* val,long nbits,double reference_value,
        double* scal,double d,double divisor,unsigned char* p,long *bitp) {
    long* destination = (long*)p;
    double* v=val;
    long countOfLeftmostBits=0,startBit=0,remainingBits=0,rightmostBits=0;
    unsigned long uval=0;
    size_t i=0;

    startBit=*bitp;
    remainingBits = nbits;

    if (startBit >= max_nbits) {
        destination += startBit / max_nbits;
        startBit %= max_nbits;
    }

    if ( (max_nbits%nbits == 0) && (*bitp%nbits == 0) ) {

        for(i=0;i< n_vals;i++) {
            uval  = (unsigned long)(((((*v)*d*scal[i/2])-reference_value)*divisor)+0.5);
            if (startBit == max_nbits) {
                startBit = 0;
                destination++;
            }

            rightmostBits = VALUE(uval,max_nbits-remainingBits,remainingBits);
            *destination = ((*destination) & ~MASKVALUE(startBit,remainingBits))
                + (rightmostBits << max_nbits-(remainingBits+startBit));
            startBit+=remainingBits;
            remainingBits=nbits;

            v++;
        }

    } else {

        for(i=0;i< n_vals;i++) {
            countOfLeftmostBits = startBit + remainingBits;
            uval  = (unsigned long)(((((*v)*d*scal[i/2])-reference_value)*divisor)+0.5);
            if (countOfLeftmostBits > max_nbits) {
                countOfLeftmostBits = max_nbits - startBit;
                startBit = max_nbits - remainingBits;
                remainingBits -= countOfLeftmostBits;
                *destination = (((*destination) >> countOfLeftmostBits) << countOfLeftmostBits)
                  + (VALUE(uval,startBit,countOfLeftmostBits));
                startBit = 0;
                destination++;
            }

            rightmostBits = VALUE(uval,max_nbits-remainingBits,remainingBits);
            *destination = ((*destination) & ~MASKVALUE(startBit,remainingBits))
                + (rightmostBits << max_nbits-(remainingBits+startBit));
            startBit+=remainingBits;
            remainingBits=nbits;

            v++;
        }
    }

    *bitp+=n_vals*nbits;

    return 0;
}
