/*
 * Copyright 2005-2018 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.
 */

/***************************************************************************
 *   Enrico Fucile  - 19.06.2007                                           *
 *                                                                         *
 ***************************************************************************/
/**
 * decode an array of n_vals values from a octet-stream
 *
 * @param p input bitstream, for technical reasons put into octets
 * @param bitp current position in the bitstream
 * @param bitsPerValue number of bits needed to build a number (e.g. 8=byte, 16=short, 32=int, but also other sizes allowed)
 * @param n_vals number of values to decode
 * @param val output, values encoded as 32/64bit numbers
 */
int grib_decode_long_array(const unsigned char* p, long *bitp, long bitsPerValue,
        size_t n_vals,long* val)
{
    unsigned long mask = BIT_MASK(bitsPerValue);

    /* pi: position of bitp in p[]. >>3 == /8 */
    long pi = *bitp / 8;
    size_t i;
    /* number of useful bits in current byte */
    int usefulBitsInByte = 8-(*bitp & 7);
    for (i = 0; i < n_vals; i++) {
        /* read at least enough bits (byte by byte) from input */
        long bitsToRead = bitsPerValue;
        long ret = 0;
        while (bitsToRead > 0) {
            ret <<= 8;
            /*   ret += p[pi];         */
            /*   Assert( (ret & p[pi]) == 0 ); */
            ret = ret | p[pi];
            pi++;
            bitsToRead -= usefulBitsInByte;
            usefulBitsInByte = 8;
        }
        *bitp += bitsPerValue;
        /*fprintf(stderr, "%d %d %d %d %d\n", bitsPerValue, *bitp, pi, ret, bitsToRead);*/
        /* bitsToRead might now be negative (too many bits read) */
        /* remove those which are too much */
        ret >>= -1*bitsToRead;
        /* remove leading bits (from previous value) */
        ret &= mask;
        val[i] = ret;

        usefulBitsInByte = -1*bitsToRead; /* prepare for next round */
        if (usefulBitsInByte > 0) {
            pi--; /* reread the current byte */
        } else {
            usefulBitsInByte = 8; /* start with next full byte */
        }
    }
    return 0;
}

/* #define GRIB_HK_DEBUG 1 */
/**
 * decode an array of n_vals values from an octet-bitstream to double-representation
 *
 * @param p input bitstream, for technical reasons put into octets
 * @param bitp current position in the bitstream
 * @param bitsPerValue number of bits needed to build a number (e.g. 8=byte, 16=short, 32=int, but also other sizes allowed)
 * @param n_vals number of values to decode
 * @param val output, values encoded as 32/64bit numbers
 */
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 0
    /* slow reference code */
    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;
    }
}
#endif
unsigned long mask = BIT_MASK(bitsPerValue);

/* pi: positin of bitp in p[]. >>3 == /8 */
long pi = *bitp / 8;
/* some bits might of the current byte at pi might be used */
/* by the previous number usefulBitsInByte gives remaining unused bits */
/* number of useful bits in current byte */
int usefulBitsInByte = 8-(*bitp & 7);
for(i=0;i < n_vals;i++) {
    /* value read as long */
    long bitsToRead = 0;
    lvalue = 0;
    bitsToRead = bitsPerValue;
    /* read one byte after the other to lvalue until >= bitsPerValue are read */
    while (bitsToRead > 0) {
        lvalue <<= 8;
        lvalue += p[pi];
        pi++;
        bitsToRead -= usefulBitsInByte;
        usefulBitsInByte = 8;
    }
    *bitp += bitsPerValue;
    /* bitsToRead is now <= 0, remove the last bits */
    lvalue >>= -1*bitsToRead;
    /* set leading bits to 0 - removing bits used for previous number */
    lvalue &= mask;

    usefulBitsInByte = -1*bitsToRead; /* prepare for next round */
    if (usefulBitsInByte > 0) {
        pi--; /* reread the current byte */
    } else {
        usefulBitsInByte = 8; /* start with next full byte */
    }
    /* scaling and move value to output */
    x=((lvalue*s)+reference_value)*d;
    val[i] = (double)x; 
}
return 0;
}

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) {
    return GRIB_NOT_IMPLEMENTED;
}

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;
}

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)
{
    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;
            }
        }
    }
    return GRIB_SUCCESS;
}

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) {
    return GRIB_NOT_IMPLEMENTED;
}

