/*
 * 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
 **************************************/


#include "grib_api_internal.h"
#include <math.h>

/*
   This is used by make_class.pl

   START_CLASS_DEF
   CLASS      = iterator
   SUPER      = grib_iterator_class_gen
   IMPLEMENTS = destroy
   IMPLEMENTS = init;next
   MEMBERS     =   double *las
   MEMBERS     =   double *los
   END_CLASS_DEF

 */

/* START_CLASS_IMP */

/*

Don't edit anything between START_CLASS_IMP and END_CLASS_IMP
Instead edit values between START_CLASS_DEF and END_CLASS_DEF
or edit "iterator.class" and rerun ./make_class.pl

*/


static void init_class              (grib_iterator_class*);

static int init               (grib_iterator* i,grib_handle*,grib_arguments*);
static int next               (grib_iterator* i, double *lat, double *lon, double *val);
static int destroy            (grib_iterator* i);


typedef struct grib_iterator_latlon_reduced{
  grib_iterator it;
/* Members defined in gen */
	long carg;
	const char* missingValue;
/* Members defined in latlon_reduced */
	double *las;
	double *los;
} grib_iterator_latlon_reduced;

extern grib_iterator_class* grib_iterator_class_gen;

static grib_iterator_class _grib_iterator_class_latlon_reduced = {
    &grib_iterator_class_gen,                    /* super                     */
    "latlon_reduced",                    /* name                      */
    sizeof(grib_iterator_latlon_reduced),/* size of instance          */
    0,                           /* inited */
    &init_class,                 /* init_class */
    &init,                     /* constructor               */
    &destroy,                  /* destructor                */
    &next,                     /* Next Value                */
    0,                 /*  Previous Value           */
    0,                    /* Reset the counter         */
    0,                 /* has next values           */
};

grib_iterator_class* grib_iterator_class_latlon_reduced = &_grib_iterator_class_latlon_reduced;


static void init_class(grib_iterator_class* c)
{
	c->previous	=	(*(c->super))->previous;
	c->reset	=	(*(c->super))->reset;
	c->has_next	=	(*(c->super))->has_next;
}
/* END_CLASS_IMP */

static int next(grib_iterator* i, double *lat, double *lon, double *val)
{
	grib_iterator_latlon_reduced* self = (grib_iterator_latlon_reduced*)i;

	if((long)i->e >= (long)(i->nv-1))
		return 0;
	i->e++;

	*lat = self->las[i->e];
	*lon = self->los[i->e];
	*val = i->data[i->e];

	return 1;
}

static int init(grib_iterator* i,grib_handle* h,grib_arguments* args)
{
	grib_iterator_latlon_reduced* self = (grib_iterator_latlon_reduced*)i;

	int ret = GRIB_SUCCESS;
	double laf;
	double lal;
	long nlats;
	double lof,tlof;
	double lol,dimin;
	long *pl;
	size_t plsize =0 ;
	long k,j,ii;
	long nlons,plmax;
	double jdirinc = 0;
	double idirinc = 0;
	double dlon=0;
	int islocal=0;
	long nlons2 =0; /* adjusted num of longitudes */

	const char* latofirst   = grib_arguments_get_name(h,args,self->carg++);
	const char* longoffirst = grib_arguments_get_name(h,args,self->carg++);
	const char* latoflast   = grib_arguments_get_name(h,args,self->carg++);
	const char* longoflast  = grib_arguments_get_name(h,args,self->carg++);
	const char* nlats_name    = grib_arguments_get_name(h,args,self->carg++);
	const char* jdirec      = grib_arguments_get_name(h,args,self->carg++);
	const char* plac        = grib_arguments_get_name(h,args,self->carg++);

	if(( ret = grib_get_double_internal(h,latofirst,      &laf))) return ret;
	if(( ret = grib_get_double_internal(h,longoffirst,    &lof))) return ret;

	if(( ret = grib_get_double_internal(h,latoflast,     &lal))) return ret;
	if(( ret = grib_get_double_internal(h,longoflast,    &lol))) return ret;

	if(( ret = grib_get_long_internal(h,nlats_name,&nlats))) return ret;

	if(( ret = grib_get_double_internal(h,jdirec,&jdirinc))) return ret;

	plsize = nlats;
	pl  = (long*)grib_context_malloc(h->context,plsize*sizeof(long));
	grib_get_long_array_internal(h,plac,pl, &plsize);

	self->las = (double*)grib_context_malloc(h->context,i->nv*sizeof(double));
	self->los = (double*)grib_context_malloc(h->context,i->nv*sizeof(double));

	plmax=pl[0];
	for (j=0;j<nlats;j++) if (plmax<pl[j]) plmax=pl[j];
	dimin=360.0/plmax;

	if ( 360-fabs(lol-lof) < 2 * dimin ) {dlon=360; islocal=0;}
	else if (lol < lof) {
		/* handle something like 150 to -120 to treat as 150 to 240 */
		/* so that dlon is 90 (not -270) */
		dlon=lol + 360.0 - lof;
		islocal=1;
	}
	else {dlon=lol-lof; islocal=1;}

	if (laf>lal) jdirinc=-jdirinc;
	k=0;
	for (j=0;j<nlats;j++) {
		nlons=pl[j];
		tlof=lof;
		nlons2 = nlons-islocal;
		/*Sometimes there are no points on a latitude! Protect against div by zero*/
		if (nlons2<1) nlons2=1;
		idirinc=dlon/nlons2;
		for (ii=0;ii<nlons;ii++) {
			self->las[k]=laf;
			self->los[k]=tlof;
			tlof+=idirinc;
			k++;
		}
		laf+=jdirinc;
	}

	i->e = -1;
	grib_context_free(h->context,pl);

	return ret;
}

static int destroy(grib_iterator* i)
{
	grib_iterator_latlon_reduced* self = (grib_iterator_latlon_reduced*)i;
	const grib_context *c = i->h->context;

	grib_context_free(c,self->las);
	grib_context_free(c,self->los);
	return 1;
}
