/* Some standard headers */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <float.h>

/* Interface for the PROJ library of geographic projections */
#include <proj_api.h>

/* Interface definition of the OPERA coder/decoder */
#include "desc.h"
#include "bufr.h"
#include "bitio.h"
#include "rlenc.h"

#include "radar.h"

static radar_data_t radar_data;

static int ccitt_len;
static char ccitt_str[251];
static decode_ccitt_str(varfl val, int ind) {
  ccitt_str[ccitt_len++] = val;
  return 1;
}

static int our_callback (varfl val, int ind) {
    radar_data_t * b = &radar_data;
    static int ncorners = 0;

    /* do nothing if data modification descriptor or replication descriptor */
    if (ind == _desc_special) {
      if (des[ind]->el->d.f == 2) {
	return 1;
      }
    }

    /* sequence descriptor */
    if (des[ind]->id == SEQDESC) {
      /* get descriptor */
      dd *d = &(des[ind]->seq->d);
      varfl *vv;

      /* open array for values */
      bufrval_t* v = bufr_open_val_array ();
      if (v == NULL) return 0;
      
      /* decode sequence to global array */
      if (!bufr_parse_out (des[ind]->seq->del, 0, des[ind]->seq->nel - 1,
			   bufr_val_to_global, 0)) {
	bufr_close_val_array ();
	return 0;
      }
      vv = v->vals;

      /* WMO block and station number */
      if  (bufr_check_fxy (d, 3,1,1)) {   
	b->wmoblock = vv[0];
	b->wmostat = vv[1];
      } else if (bufr_check_fxy (d, 3,1,11)) { 
	b->meta.year = vv[0];       /* Date */
	b->meta.month = vv[1];
	b->meta.day = vv[2];
      }	else if (bufr_check_fxy (d, 3,1,13)) { 
	b->meta.hour = vv[0];       /* Time */
	b->meta.min = vv[1];
	b->meta.sec = vv[2];
      } else if (bufr_check_fxy (d, 3,13,9)) { 
	/* Reflectivity scale */
	int j;
	int i = 0;
	b->img.scale.vals[0] = vv[i++];
	b->img.scale.nvals = vv[i++] + 1;  /* number of scale values */ 
	assert(b->img.scale.nvals < 256);
	for (j = 1; j < b->img.scale.nvals; j++) {
	  b->img.scale.vals[j] = vv[i++];
	}
      } else if (bufr_check_fxy (d, 3,1,23) && (ncorners == 0)) {
	++ncorners;
	b->img.nw.lat = vv[0];
	b->img.nw.lon = vv[1];
      } else if (bufr_check_fxy (d, 3,1,23) && (ncorners == 1)) {
	++ncorners;
	b->img.ne.lat = vv[0];
	b->img.ne.lon = vv[1];
      } else if (bufr_check_fxy (d, 3,1,23) && (ncorners == 2)) {
	++ncorners;
	b->img.se.lat = vv[0];
	b->img.se.lon = vv[1];
      } else if (bufr_check_fxy (d, 3,1,23) && (ncorners == 3)) {
	++ncorners;
	b->img.sw.lat = vv[0];
	b->img.sw.lon = vv[1];
      } else if (bufr_check_fxy (d, 3,21,250)) {
	/*radars list for composite*/
	int i = 0, j;    
	b->nradar_list = vv[i++];
	if (b->nradar_list > 0) {
	  b->radar_list = calloc(b->nradar_list, sizeof(meta_radar_compo_t));
	  for (j = 0; j < b->nradar_list; ++j) {
	    b->radar_list[j].wmoblock = vv[i++];
	    b->radar_list[j].wmostat = vv[i++];
	    b->radar_list[j].indqual = vv[i++];
	    b->radar_list[j].presence = vv[i++];
	  }
	}
      } else if (bufr_check_fxy (d, 3,21,193)) {
	unsigned short * img = NULL;
	int nvals, ncols, nrows, status = rldec_to_mem (vv, &img, &nvals, &nrows, &ncols);
	if (status == 0) {
	  fprintf(stderr,"Error rldec_to_mem\n");
	}
	/*fprintf(stderr,"ncols = %d nrows = %d nvals = %d\n", ncols, nrows, nvals);*/
	if ((ncols != b->img.ncols) || (nrows != b->img.nrows)) {
	  fprintf(stderr,"Error consistency for ncols or nrows\n");
	}
	b->img.data = img;
      } else {
	fprintf (stderr,
		 "Unknown sequence descriptor %d %d %d\n", d->f, d->x, d->y);
      }
      /* close the global value array */
      bufr_close_val_array ();
    }
    /* element descriptor */
    else if (des[ind]->id == ELDESC) {
      dd *d = &(des[ind]->el->d);

      if (bufr_check_fxy (d, 0,1,99)) {
	ccitt_len = 0;
	bufr_parse_out (d,0,0,decode_ccitt_str,0);
	/*fprintf (stderr, "Unique product definition: %s\n", ccitt_str);*/
	return 1;
      } else if (bufr_check_fxy (d, 0,25,61)) {
	ccitt_len = 0;
	bufr_parse_out (d,0,0,decode_ccitt_str,0);
	/*fprintf (stderr, "Software identification and version number: %s\n", ccitt_str);*/
	return 1;
      } else if (bufr_check_fxy (d, 0,29,205)) {
	ccitt_len = 0;
	bufr_parse_out (d,0,0,decode_ccitt_str,0);
	/*fprintf (stderr, "Projection: %s\n", ccitt_str);*/
	return 1;
      } else if (bufr_check_fxy (d, 0,5,1)) {
	b->proj.stdpar1 = val;
      } else if (bufr_check_fxy (d, 0,5,33)) {
	b->img.psizex = val;
      } else if (bufr_check_fxy (d, 0,6,33)) {
	b->img.psizey = val;
      } else if (bufr_check_fxy (d, 0,7,2)) {
	b->meta.radar_height = val;
      } else if (bufr_check_fxy (d, 0,29,1)) {
	b->proj.type = val;
      } else if (bufr_check_fxy (d, 0,29,2)) {
	b->img.grid = val;
      } else if (bufr_check_fxy (d, 0,29,195)) {
	/* False Easting */
	b->proj.xoff = val;
      } else if (bufr_check_fxy (d, 0,29,196)) {
	/* False Northing */
	b->proj.yoff = val;
      } else if (bufr_check_fxy(d,0,29,199)) {
	/* Semi-major axis or rotation ellipsoid */
	b->proj.majax = val;
      } else if (bufr_check_fxy(d,0,29,200)) {
	/* Semi-minor axis or rotation ellipsoid */
	b->proj.minax = val;
      } else if (bufr_check_fxy (d, 0,29,203)) {
	/* Longitude Origin */
	b->proj.orig.lon = val;
      } else if (bufr_check_fxy (d, 0,29,204)) {
	/* Latitude Origin */
	b->proj.orig.lat = val;
      } else if (bufr_check_fxy (d, 0,30,21)) {
	b->img.ncols = val;
      } else if (bufr_check_fxy (d, 0,30,22)) {
	b->img.nrows = val;
      } else if (bufr_check_fxy (d, 0,30,31)) {
	b->img.type = val;
      }
      else 
	fprintf (stderr,
		 "Unknown element descriptor %d %d %d\n", d->f, d->x, d->y);
    }
    return 1;
}

#define fill_desc(ff,xx,yy) {\
        dd.f=ff; dd.x=xx; dd.y=yy; \
        bufr_desc_to_array (descs, dd, nd);}
#define fill_v(val) bufr_val_to_array (vals, val, &nv);

int create_msg (dd *descs, int *nd, varfl **vals, radar_data_t *d) {
    dd dd;
    int nv = 0;

    fill_desc(3,1,1);           /* WMO block and station number */
    fill_v(d->wmoblock);
    fill_v(d->wmostat);
    fill_desc(3,1,11);
    fill_v(d->meta.year);       /* Date */
    fill_v(d->meta.month);
    fill_v(d->meta.day);
    fill_desc(3,1,12);  
    fill_v(d->meta.hour);       /* Time */
    fill_v(d->meta.min);
    fill_desc(3,1,23);        
    fill_v(d->img.nw.lat);      /* Lat. / lon. of NW corner */
    fill_v(d->img.nw.lon);
    fill_desc(3,1,23); 
    fill_v(d->img.ne.lat);      /* Lat. / lon. of NE corner */
    fill_v(d->img.ne.lon);
    fill_desc(3,1,23); 
    fill_v(d->img.se.lat);      /* Lat. / lon. of SE corner */
    fill_v(d->img.se.lon);
    fill_desc(3,1,23); 
    fill_v(d->img.sw.lat);      /* Lat. / lon. of SW corner */
    fill_v(d->img.sw.lon);
    fill_desc(0,29,1);
    fill_v(d->proj.type);             /* Projection type */
    fill_desc(0,5,2);
    fill_v(d->proj.stdpar1);          /* Eeference latitude */
    fill_desc(0,5,33);
    fill_v(d->img.psizex);            /* Pixel size along x coordinate */
    fill_desc(0,6,33);
    fill_v(d->img.psizey);            /* Pixel size along y coordinate */
    fill_desc(0,30,21);
    fill_v(d->img.ncols);             /* Number of (column ) pixels per row */
    fill_desc(0,30,22);
    fill_v(d->img.nrows);             /* Number of (rows) pixels per column */   
    fill_desc(0,30,31);
    fill_v(d->img.type);        /* Image type */
    fill_desc(0,30,32);
    fill_v(1);                  /* Combination with other data */
    fill_desc(0,29,2);
    fill_v(d->img.grid);        /* Co-ordinate grid */
    fill_desc(3,13,9);
    {
      int j; 
      fill_v(d->img.scale.vals[0]);   /* Reflectivity scale */
      fill_v(d->img.scale.nvals-1);
      for (j = 1; j < d->img.scale.nvals; j++) {
	fill_v(d->img.scale.vals[j]);
      }
    }
    fill_desc(3,21,250)
    fill_v(d->nradar_list);     /* Radars list */
    {
      int j;
      for (j = 0; j < d->nradar_list; ++j) {
	fill_v(d->radar_list[j].wmoblock);
	fill_v(d->radar_list[j].wmostat);
	fill_v(d->radar_list[j].indqual);
	fill_v(d->radar_list[j].presence);
      }
    }
    fill_desc(3,21,7);
    fill_v(7);
    fill_v(1);
    fill_v(0);
    fill_v(1);
    fill_v(3);
    fill_v(2);
    fill_v(0.00004);
    fill_v(3);
    /* run length encode our bitmap */
    fill_desc(3,21,193);
    if (!rlenc_from_mem(d->img.data, d->img.nrows, d->img.ncols, vals, &nv)) {
      fprintf (stderr, "Error rlenc_from_mem\n");
    }
    return 1;
}

void bufr_encoding(sect_1_t *s1, radar_data_t *src_data, bufr_t *bufr_msg, char *table_dir, char *output) {
    dd descs[MAX_DESCS];  /* array of data descriptors, must be large enough
                             to hold all required descriptors */
    int nd = 0;           /* current number of descriptors in descs */
    varfl* vals = NULL;   /* array of data values */
    int ok;

   /* first let's create our source message */

    create_msg (descs, &nd, &vals, &radar_data);

    s1->vmtab = 11;
    s1->vltab = 4;
    s1->subcent = s1->gencent = 255;
    _bufr_edition = 2;
    /*fprintf (stderr, "Output file header:\n");
      header_dump(s1);*/

    /* read supported data descriptors from tables */
    ok = (read_tables (table_dir, s1->vmtab, s1->vltab, s1->subcent, s1->gencent) >= 0);

    /* encode our data to a data-descriptor- and data-section */

    if (ok) ok = bufr_encode_sections34 (descs, nd, vals, bufr_msg);

    /* encode section 0, 1, 2, 5 */

    if (ok) ok = bufr_encode_sections0125 (s1, bufr_msg);

    /******* Save coded data */
    if (ok) ok = bufr_write_file(bufr_msg, output);
    
    if (vals != NULL)
        free (vals);

    if (!ok) exit (EXIT_FAILURE);
}

/*===========================================================================
                                  Main program
  ===========================================================================*/
int main (int argc, char *argv[])
{
  char *usage = "usage: dwd [-v] [-d tabdir] input_file output_file\n";
  char *version = "dwd V1.0, 16-07-2013\n";
  
  char *table_dir = NULL;     /* directory for BUFR tables */

  sect_1_t s1;
  bufr_t msg;

  /******* check command line parameter */

  while (argc > 1 && *argv[1] == '-') {
    if (*(argv[1] + 1) == 'v')
      fprintf (stderr, "%s", version);
    else if (*(argv[1] + 1) == 'd') {
      if (argc < 2) {
        fprintf (stderr, "Missing parameter for -d\n\n%s", usage);
        exit (EXIT_FAILURE);
      }
      table_dir = argv[2];
      argc--;
      argv++;
    } else {
        fprintf (stderr, "Invalid parameter %s\n\n%s", argv[1], usage);
        exit (EXIT_FAILURE);
    }
    argc--;
    argv++;
  }

  /******* Get input- and output-filenames from the command-line */
  if (argc != 3) {
    fprintf (stderr, "%s", usage);
    exit (EXIT_FAILURE);
  }

  if (!bufr_read_file(&msg, argv[1])) {
        fprintf (stderr, "FATAL: Unable to read the BUFR-message in %s!\n", argv[1]);
        exit (EXIT_FAILURE);
  }

  /******* decode section 1 */
  /*fprintf (stderr, "Input file header:\n");
  header_dump(&s1);*/
  if (!bufr_decode_sections01(&s1, &msg)) {
    fprintf (stderr, "FATAL: Unable to decode section 1\n");
    exit (EXIT_FAILURE);
  }

  /* read descriptor tables */
  if (read_tables (table_dir, s1.vmtab, s1.vltab, s1.subcent, s1.gencent)) {
    fprintf (stderr, "FATAL: Unable to read tables\n");
    exit (EXIT_FAILURE);
  }

  /* decode data descriptor and data-section now */
  {
    int ok, desch, ndescs;
    dd* dds = NULL;

    /* open bitstreams for section 3 and 4 */
    desch = bufr_open_descsec_r(&msg, NULL);
    ok = (desch >= 0);
    if (ok) ok = (bufr_open_datasect_r(&msg) >= 0);

    /* calculate number of data descriptors  */
    ndescs = bufr_get_ndescs (&msg);

    /* allocate memory and read data descriptors from bitstream */
    if (ok) ok = bufr_in_descsec (&dds, ndescs, desch);

    /* output data to our global data structure */
    if (ok) ok = bufr_parse_out (dds, 0, ndescs - 1, our_callback, 1);

    /* close bitstreams and free descriptor array */
    if (dds != (dd*) NULL)
        free (dds);
    bufr_close_descsec_r (desch);
    bufr_close_datasect_r ();
    free_descs();
  }

  /* recode data */
  bufr_encoding(&s1, &radar_data, &msg, table_dir, argv[2]);

  exit (EXIT_SUCCESS);
}

