/**
* Copyright 1981-2012 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.
*/

#include <stdio.h>
#include <string.h>
#include "common/fortint.h"


/* defines for FORTRAN subroutine */
#ifdef FORTRAN_NO_UNDERSCORE
#define SOFFSET soffset
#else
#define SOFFSET soffset_
#endif

#define ERROR(a,b) {perror(a);return b;}
#define GRIB 0x47524942
#define len3oct(p) ((((long)*(p))<<16) + (((long)*(p+1))<<8) + (long)*(p+2))
#define BIT1 0x80
#define BIT2 0x40
#define BIT3 0x20
#define BIT4 0x10
#define BIT5 0x08
#define BIT6 0x04
#define BIT7 0x02
#define BIT8 0x01

static int grab(unsigned char * , unsigned char * , long ,long ,long * );

fortint soffset_(
  unsigned char * buffer,
  fortint* is0,
  fortint* is1,
  fortint* is2,
  fortint* is3,
  fortint* is4,
  fortint* iedition) {
long s0, s1, s2, s3, s4, edition;
int large = 0;
int found = 0;
int code = 0;
long bytes_read = 0, advance;
unsigned char p, edit_num, flag23;
unsigned char size[3];
int section0 = 8, section1, section2, section3, section4;
long total;
unsigned char grp_7777[5];

/*  Read bytes until "GRIB" found */

    do
    {
        if( grab(buffer, &p, 1, 1, &bytes_read) != 0) return 1;
        code = ( (code << 8) + p ) & 0xFFFFFFFF;
        if (code == GRIB ) found = 1;
    } while ( ! found );
    s0 = bytes_read - 4;
    bytes_read = 4;

/*  Now find out which edition of GRIB is present (default is 1) */

    edition = 1;
    s1 = s0 + 8;
    if( (*(buffer+21-s0) == '\0') && (*(buffer+22-s0) == '\0') )
    {
        edition = -1;                            /* GRIB edition -1 */
        s1 = s0;
        section1 = 20;
        section0 = 4;
    }
    else
    {
        if( grab(buffer, size, 3, 1, &bytes_read) != 0) return 1;
        total = len3oct(size);
        if( total == 24 )
        {
/*          Move past the edition number */
            if( grab(buffer, &edit_num, 1, 1, &bytes_read) != 0) return 1;
            edition = 0;                         /* GRIB edition 0 */
            section1 = 24;
            s1 = s0 + 4;
            section0 = 4;
        }
    }

    if( edition == 1 )
    {
/*      See if it is an extra large (wave) product */
        if( total > 0x800000 ) {
            total = (total&0x7fffff) * 120;
            large = 1;
        }
/*      Move past the edition number */
        if( grab(buffer, &edit_num, 1, 1, &bytes_read) != 0) return 1;

/*      Read length of section 1 */
        if( grab(buffer, size, 3, 1, &bytes_read) != 0) return 1;
        section1 = len3oct(size);
    }

/*  Now figure out if sections 2/3 are present */

    advance = 4;
    bytes_read += advance;
    if( grab(buffer, &flag23, 1, 1, &bytes_read) != 0) return 1;
    section2 = flag23 & BIT1;
    section3 = flag23 & BIT2;

/*  Advance to end of section 1 */

    advance = section1 - (bytes_read - section0);
    bytes_read += advance;

/*  Read section 2 length if it is given*/

    if( section2 )
    {
        s2 = s0 + bytes_read;
        if( grab(buffer, size, 3, 1, &bytes_read) != 0) return 1;
        section2 = len3oct(size);
        advance = section2 - (bytes_read - section0 - section1);
        bytes_read += advance;
    }
    else
    {
        section2 = 0;
	s2 = 0;
    }

/*  Read section 3 length if it is given*/

    if( section3 )
    {
        s3 = s0 + bytes_read;
        if( grab(buffer, size, 3, 1, &bytes_read) != 0) return 1;
        section3 = len3oct(size);
        advance = section3 - (bytes_read - section0 - section1 - section2);
        bytes_read += advance;
    }
    else
    {
        section3 = 0;
	s3 = 0;
    }

/*  Read section 4 length */

    s4 = s0 + bytes_read;
    if( grab(buffer, size, 3, 1, &bytes_read) != 0) return 1;
    section4 = len3oct(size);
    if( large ) section4 = total + 3 - bytes_read - section4; 
    advance = section4 - (bytes_read - section0 - section1 - section2 - section3);
    bytes_read += advance;

/*  Check 7777 group is in the expected place */

    if( grab(buffer, grp_7777, 4, 1, &bytes_read) != 0) return 1;
    grp_7777[4] = '\0';
    if( strcmp((char *)grp_7777,"7777") != 0 )
      ERROR("7777 group not found", 15);
  

/*  Success! */
    *is0 = (fortint) s0;
    *is1 = (fortint) s1;
    *is2 = (fortint) s2;
    *is3 = (fortint) s3;
    *is4 = (fortint) s4;
    *iedition = (fortint) edition;
    return 0;
}

fortint soffset(
  unsigned char * buffer,
  fortint* is0,
  fortint* is1,
  fortint* is2,
  fortint* is3,
  fortint* is4,
  fortint* iedition) {
  return soffset_(buffer,is0,is1,is2,is3,is4,iedition);
}

static int grab(unsigned char * buffer, unsigned char * where, long size,long cnt,long * num_bytes_read)
{
long number = size*cnt;

    memcpy(where, (buffer+(*num_bytes_read)), number);
    *num_bytes_read += number;

    return 0;
}
