/* 2/15/1993 ScianWhisselFiles.c

NEXRAD file reader */

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianArrays.h"
#include "ScianIcons.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianVisWindows.h"
#include "ScianVisObjects.h"
#include "ScianControls.h"
#include "ScianColors.h"
#include "ScianDialogs.h"
#include "ScianFiles.h"
#include "ScianFileSystem.h"
#include "ScianLists.h"
#include "ScianPictures.h"
#include "ScianErrors.h"
#include "ScianTimers.h"
#include "ScianDatasets.h"
#include "ScianFilters.h"
#include "ScianTextBoxes.h"
#include "ScianTitleBoxes.h"
#include "ScianButtons.h"
#include "ScianSliders.h"
#include "ScianScripts.h"
#include "ScianIDs.h"
#include "ScianVisContours.h"
#include "ScianStyle.h"
#include "ScianSpaces.h"
#include "ScianMethods.h"
#include "ScianObjFunctions.h"
#include "ScianWhisselFiles.h"

#include <time.h>

#ifndef ELEVATIONSKIP
#define ELEVATIONSKIP		1100
#define RADIALSKIP		1101
#define GATESKIP                1102	
#define DOREFDATASETS           1103
#define DOVELDATASETS           1104
#endif

#define TORADS (3.1415926535898 / 180.0)	/* deg -> rad conversion
						   factor */
#ifndef RELEASE

#ifdef PROTO
void skip_ray(int c, FILE *f)
#else
void skip_ray(c, f)
  int c;
  FILE *f;
#endif
{
  while(c--)
    getc(f);
}

#ifdef PROTO
static ObjPtr ReadNXRFile(ObjPtr reader, char *name)
#else
static ObjPtr ReadNXRFile(reader, name)
  ObjPtr  reader;
  char   *name;
#endif
{
  struct NXRhead header;	/* NEXRAD/SciAn file header */
  FILE   *infile;

  char    formname[TEMPSTRSIZE];
  char    refname[TEMPSTRSIZE];
  char    velname[TEMPSTRSIZE];
  char   *tc;

  int     els = 0;	/* Number of elevations */
  int     i, j, k;	/* Index variables */
  int     ii, ji, ki;

  float   x, y, z, len, inc;	/* Used in grid-positioning calculations */
  float   sinz, cosz, cosr, sinr;
  float *cosa, *sina, *h, *s;
  float ae = (4.0 * 6371.0) / 3.0; /* Effective Radius of the Earth */

  float ref_value_array[256];	/* Array of byte-to-float values for
				   compressed dataset format */
  float vel_value_array[256];

  ObjPtr  RefForm = NULL;	/* Dataforms for Reflectivity */
  ObjPtr  VelForm = NULL;	/* and Velocity */
  ObjPtr  form = NULL;	/* Temp form pointer */

  ObjPtr  RefDataSet, VelDataSet;	/* Datasets for reflec. and vel. */

  long    dims[3];
  real    bounds[6];	/* Used to define parameters of datasets */
  long    indices[3];	/* and dataforms */

  float   val;			/* Conversion vars */
  unsigned char uchar;
  int     elcount, ncount, count;
  int sum;

  int elskip = 0, radskip = 0, gateskip = 3;
  int radmod, gatemod, elmod;
  int doref = 1, dovel = 0;

  ObjPtr var;

  var = GetIntVar("ReadNXRFile", reader, DOREFDATASETS);
  if (!var)
    doref = 1;
  else
    doref = GetInt(var);

  var = GetIntVar("ReadNXRFile", reader, DOVELDATASETS);
  if (!var)
    dovel = 1;
  else
    dovel = GetInt(var);

  var = GetRealVar("ReadNXRFile", reader, ELEVATIONSKIP);
  if (!var)
    elskip = 0;
  else
    elskip = (int)GetReal(var);

  var = GetRealVar("ReadNXRFile", reader, RADIALSKIP);
  if (!var)
    radskip = 0;
  else
    radskip = (int)GetReal(var);

  var = GetRealVar("ReadNXRFile", reader, GATESKIP);
  if (!var)
    gateskip = 0;
  else
    gateskip = (int)GetReal(var);

  elmod = elskip + 1;
  radmod = radskip + 1;
  gatemod = gateskip + 1;

  /* Derive a dataset name from the file name */

  strcpy(formname, name);
  tc = formname;
  while (*tc && (*tc != '@') && (*tc != '.'))
    tc++;
  *tc = '\0';
  strcpy(refname, formname);
  strcpy(velname, formname);

  strcat(formname, ".form");
  strcat(refname, ".dBZ");
  strcat(velname, ".m/s");

  /* Open the file and verify that it contains appropriate data */

  if ((infile = fopen(name, "r")) == NULL) {
    FileFormatError("ReadNXRFile", "File not accessible");
    return NULLOBJ;
  }
  if (fread(&header, sizeof(header), 1, infile) != 1) {
    FileFormatError("ReadNXRFile", "Error reading file header");
    fclose(infile);
    return NULLOBJ;
  }
  if (strncmp(header.ident, FVERSION, 6) != 0) {
    FileFormatError("ReadNXRFile", "File is not a NEXRAD file");
    fclose(infile);
    return NULLOBJ;
  }
  if (strncmp(header.ident, FVERSION, 7) != 0) {
    FileFormatError("ReadNXRFile", 
		    "File and reader versions are incompatible");
    fclose(infile);
    return NULLOBJ;
  }

  /* Count the number of elevations to be read. */

  fprintf(stderr, "Els=%d\n", header.num_els);

  if (elskip) {
    for (i=0,els=0; i < header.num_els; i++) {
      if (!(i % elmod) || (i==header.num_els-1))
	els++;
    }
  } else
    els = header.num_els;

  /* Establish the grid point dimensions. */

  dims[0] = els;
  dims[1] = (int)fceil((double)N_RADS / (double)radmod);
  dims[2] = (int)fceil((double)N_REFGATES / (double)gatemod);

  /* 
    Build a grid for placing data.  Use the reflectivity gate spacing
    for both reflectivity and velocity data for the purposes of data
    reduction (velocity data is 4x as dense, but covers half the distance).
  */

  form = NewDataset(formname, 3, dims, 3);
  SetCurField(FIELD1, form);

  /* Precalculate the sin/cos of all the radials since these
     will be the same for each elevation angle (saves a few
     thousand sin/cos calls). */

  cosa = (float *)malloc(N_RADS * sizeof(float));
  sina = (float *)malloc(N_RADS * sizeof(float));

  for (j=0; j < N_RADS; j++) {
    /* Convert from radial number to sin/cos of the degree
       measure of that radial.  Radials are numbered clockwise
       starting 0.5 degrees east of north. */
    cosa[j] = (float) cos((double) (90.0 - (j + 0.5)) * TORADS);
    sina[j] = (float) sin((double) (90.0 - (j + 0.5)) * TORADS);
  }
  
  h = (float *)malloc(N_REFGATES * sizeof(float));
  s = (float *)malloc(N_REFGATES * sizeof(float));

  inc = (float) header.ref_gatespace / 1000.0;

  for (i=0, ii=0; i < header.num_els; i++) {	/* elevation loop */

    fprintf(stderr,"Doing loop %d\n", i);
    /* Are we skipping this elevation? */
    if ((i % elmod) && (i<header.num_els-1)) {
      continue;
    }

    indices[0] = ii++;

    /* Cache the sin/cos values for this elevation angle. */

    sinz = sin((double) (header.elevs[i]*TORADS));
    cosz = cos((double) (header.elevs[i]*TORADS));

    /* Establish heights and distances for any given ray in
       the current elevation scan. */

    for (k=0, len=inc/2.0; k < N_REFGATES; k++, len += inc) {
      /* Given the distance of the gate (len), h is the height
	 above the surface of the earth accounting for curvature. */
      h[k] = sqrt((double) (len*len + ae*ae + 2.0*len*ae*sinz)) - ae;
      
      /* s is distance along the surface of the earth to the point
	 underneath the grid point accounting for the curvature. */ 
      s[k] = ae * asin((double)((len*cosz)/(ae + h[k]))); 
    }

    if (i == 0)	{		/* Set x and y bounds from the smallest
				   elevation angle.  */
      bounds[0] = -s[N_REFGATES-1];
      bounds[1] = s[N_REFGATES-1];
      bounds[2] = -s[N_REFGATES-1];
      bounds[3] = s[N_REFGATES-1];
      bounds[4] = h[0];
    } else if (i == header.num_els-1) {	/* Set z max from the greatest
					   elevation angle. */
      bounds[5] = h[N_REFGATES-1];
    }

    for (j=0, ji=0; j < N_RADS; j++) { /* radial loop */

      if (!(j % radmod)) {
	indices[1] = ji++;

	cosr = cosa[j];
	sinr = sina[j];

	for (k=0, ki=0; k < N_REFGATES; k++) {
	  if (!(k % gatemod)) {
	    z = h[k];
	    x = s[k] * cosr;
	    y = s[k] * sinr;

	    indices[2] = ki++;
	  
	    PutFieldComponent(FIELD1, 0, indices, x);
	    PutFieldComponent(FIELD1, 1, indices, y);
	    PutFieldComponent(FIELD1, 2, indices, z);
	  }
	}
      }
    }
  }

  free(sina);
  free(cosa);
  free(h);
  free(s);

  /* Now that the grid has been created, turn it into a dataform. */

  RefForm = NewCurvilinearDataForm(formname, 3, dims, bounds, form);

  /* Now setup the complete datasets. */

  if (doref) {
    for (i=2; i < 256; i++) {
      ref_value_array[i] = (float)(i - 2) / 2.0 - 32.0;
    }
    ref_value_array[0] = ref_value_array[1] = missingData;

    RefDataSet = NewCompressedDataset(refname, 3, dims, 0, ref_value_array);
    SetDatasetForm(RefDataSet, RefForm);
  }
  if (dovel) {
    if (header.vel_res == 2) {
      for (i=2; i < 256; i++) {
	vel_value_array[i] = (float)(i - 2) / 2.0 - 63.5;
      }
    } else {
      for (i=2; i < 256; i++) {
	vel_value_array[i] = (float)(i - 129);
      }
    }
    vel_value_array[0] = vel_value_array[1] = missingData;

    VelDataSet = NewCompressedDataset(velname, 3, dims, 0, vel_value_array);
    SetDatasetForm(VelDataSet, RefForm);
  }

  /* Read and convert data: for each elevation, reflectivity is first, 
     then velocity data. */

  for (i=0, ii=0; i < header.num_els; i++) {
    if ((i % elmod) && (i < header.num_els-1)) {
      skip_ray(N_RADS * (N_REFGATES + N_VELGATES), infile);
      continue;
    }

    indices[0] = ii++;

    if (doref) {		/* Creating Reflectivity datasets? */
      fprintf(stderr, "Doing ref elev %d\n", i);
      SetCurField(FIELD1, RefDataSet);
      for (j=0, ji=0; j < N_RADS; j++) {
	if (j % radmod)
	  skip_ray(N_REFGATES, infile);
	else {
	  indices[1] = ji++;
	  if (feof(infile))
	    goto READERROR;
	  for (k=0, ki=0; k < N_REFGATES; k++) {
	    uchar = (unsigned char) getc(infile);
	    if (!(k % gatemod)) {
	      indices[2] = ki++;
      /* val = (uchar <= 1) ? missingData : (uchar - 2) / 2.0 - 32.0; */
	      PutCompressedFieldComponent(FIELD1, 0, indices, uchar);
	    }
	  }
	}
      }
    } else {
      skip_ray(N_RADS * N_REFGATES, infile);
    }

    if (dovel) {		/* Creating Velocity datasets? */
      /* Read velocity scan using one of two resolution values */
      fprintf(stderr, "Doing vel elev %d\n", i);
      SetCurField(FIELD1, VelDataSet);
      for (j=0, ji=0; j < N_RADS; j++) {
	if (j % radmod) {
	  skip_ray(N_VELGATES, infile);
	} else {
	  elcount = 0;
	  sum = 0;
	  ncount = 0;
	  indices[1] = ji++;
	  if (feof(infile))
	    goto READERROR;
	  for (k=0, ki=0; k < N_VELGATES; k++) {
	    elcount += header.vel_gatespace;
	    uchar = (unsigned char) getc(infile);
	    if (uchar > 1) {
	      sum += uchar;
	      ncount++;
	    }
	    if (elcount >= header.ref_gatespace * gatemod) {
	      elcount %= header.ref_gatespace * gatemod;
	      indices[2] = ki++;
	      if (ncount)
		uchar = (unsigned char)((float)sum / (float)ncount + 0.5);
	      else
		uchar = 0;
	      PutCompressedFieldComponent(FIELD1, 0, indices, uchar);
	      sum = 0;
	      ncount = 0;
	    }
	  }
	  for (; ki < dims[2]; ki++) {
	    indices[2] = ki;
	    PutCompressedFieldComponent(FIELD1, 0, indices, (unsigned char)0);
	  }
	}
      }
    } else {    
      /* Skip velocity scan */
      skip_ray(N_RADS * N_VELGATES, infile);
    }		/* end if (do velocity dataset) */
  }	/* end elevation loop */

  fclose(infile);

  /* The UNIX concept of time (i.e., seconds since 1/1/70 UTC) is
     used to express the time of the radar picture.  SciAn wants
     seconds since midnight, so convert appropriately. */
  {
    struct tm the_time;
    time_t clocktime, result_time;
    extern long _timezone, _altzone, _daylight;

    clocktime = (time_t) header.file_time;
    memcpy((void *)&the_time, (void *)gmtime(&clocktime),
	   sizeof(struct tm));
    /* Reset to midnight of the current day... */
    the_time.tm_sec = 0;
    the_time.tm_min = 0;
    the_time.tm_hour = 0;
    /* 
      Obtain the clock time at midnight. The SG documentation says
      mktime() converts from the local timezone domain, so subtract
      the difference in seconds between the local time zone and GMT
      so that the result will be GMT.
    */
    result_time = mktime(&the_time);
    if (_daylight)
      result_time -= _altzone;
    else
      result_time -= _timezone;
    val = (float)(clocktime - result_time);
  }

  val += 3600.0;

  if (doref)
    RegisterTimeDataset(RefDataSet, val);
  if (dovel)
    RegisterTimeDataset(VelDataSet, val);

  return NULLOBJ;

READERROR:
  FileFormatError("ReadNXRFile", "Premature EOF!");
  fclose(infile);
  return NULLOBJ;
}

#endif

#ifdef PROTO
void    KillWhisselFiles(void)
#else
void    KillWhisselFiles()
#endif
{
}

#ifndef RELEASE
#define NXR_ITEMWIDTH 225
#define NXR_MIDDLE_SEP 45
#define NXR_SMALLER_ITEM 180

#ifdef PROTO
static ObjPtr    AddNXRControls(ObjPtr FileReader, ObjPtr PanelContents)
#else
static ObjPtr    AddNXRControls(FileReader, PanelContents)
  ObjPtr  FileReader, PanelContents;
#endif
{
  ObjPtr  checkbox, textbox, var;
  int     left, right, bottom, top;

  bottom = left = MAJORBORDER;

  top = bottom + CHECKBOXHEIGHT;
  right = left + NXR_ITEMWIDTH;

  /* If DOREFDATASETS has not been created yet, create it and set
     it to TRUE. */

  var = GetIntVar("AddNXRControls", FileReader, DOREFDATASETS);
  if (!var)
    SetVar(FileReader, DOREFDATASETS, ObjTrue);
  checkbox = NewCheckBox(left, right, bottom, top,
			 "Create Reflectivity Datasets",
			 GetPredicate(FileReader, DOREFDATASETS));
  PrefixList(PanelContents, checkbox);
  SetVar(checkbox, PARENT, PanelContents);
  SetVar(checkbox, HELPSTRING, NewString("This option determines whether or \
not reflectivity datasets are generated."));
  AssocDirectControlWithVar(checkbox, FileReader, DOREFDATASETS);

  left = right + NXR_MIDDLE_SEP;
  right = left + NXR_ITEMWIDTH;

  /* If DOVELDATASETS has not been created yet, create it and set
     it to TRUE. */

  var = GetIntVar("AddNXRControls", FileReader, DOVELDATASETS);
  if (!var)
    SetVar(FileReader, DOREFDATASETS, ObjTrue);
  checkbox = NewCheckBox(left, right, bottom, top,
			 "Create Velocity Datasets",
			 GetPredicate(FileReader, DOVELDATASETS));
  PrefixList(PanelContents, checkbox);
  SetVar(checkbox, PARENT, PanelContents);
  SetVar(checkbox, HELPSTRING, NewString("This option determines whether or \
not Doppler velocity datasets are generated."));
  AssocDirectControlWithVar(checkbox, FileReader, DOVELDATASETS);

  left = MAJORBORDER;
  right = left + NXR_SMALLER_ITEM;
  bottom = top + MINORBORDER;
  top = bottom + EDITBOXHEIGHT;

  /* Elevation Skip Factors */
  textbox = NewTextBox(left, right, bottom, top,
		     0, "Elevation Skip Text", "Elevation Skip Factor:");
  SetVar(textbox, PARENT, PanelContents);
  PrefixList(PanelContents, textbox);

  left = right + NXR_MIDDLE_SEP;
  right = left + NXR_ITEMWIDTH;
  
  var = GetRealVar("AddNXRControls", FileReader, ELEVATIONSKIP);
  if (!var)
    SetVar(FileReader, ELEVATIONSKIP, NewReal(0.0));
  textbox = NewTextBox(left, right, bottom, top,
		       EDITABLE + WITH_PIT + ONE_LINE,
		       "Elevation Skip", "0");
  SetVar(textbox, PARENT, PanelContents);
  PrefixList(PanelContents, textbox);
  SetTextAlign(textbox, RIGHTALIGN);
  AssocTextRealControlWithVar(textbox, FileReader, ELEVATIONSKIP, 
			      0, plusInf, TR_NE_TOP);
  SetVar(textbox, HELPSTRING,
	 NewString("This value indicates the number of elevation \
scans to skip.  The bottom and top elevation angles are always used, \
regardless of this value."));

  left = MAJORBORDER;
  right = left + NXR_SMALLER_ITEM;
  bottom = top + MINORBORDER;
  top = bottom + EDITBOXHEIGHT;

  /* Radial Skip Factors */
  textbox = NewTextBox(left, right, bottom, top,
		       0, "Radial Skip Text", "Radial Skip Factor:");
  SetVar(textbox, PARENT, PanelContents);
  PrefixList(PanelContents, textbox);

  left = right + NXR_MIDDLE_SEP;
  right = left + NXR_ITEMWIDTH;

  var = GetRealVar("AddNXRControls", FileReader, RADIALSKIP);
  if (!var)
    SetVar(FileReader, RADIALSKIP, NewReal(0.0));
  textbox = NewTextBox(left, right, bottom, top,
		       EDITABLE + WITH_PIT + ONE_LINE,
		       "Radial Skip", "0");
  SetVar(textbox, PARENT, PanelContents);
  PrefixList(PanelContents, textbox);
  SetTextAlign(textbox, RIGHTALIGN);
  AssocTextRealControlWithVar(textbox, FileReader, RADIALSKIP, 
			      0, plusInf, TR_NE_TOP);
  SetVar(textbox, HELPSTRING,
	 NewString("This value indicates the number of radials (beams) \
skipped."));

  left = MAJORBORDER;
  right = left + NXR_SMALLER_ITEM;
  bottom = top + MINORBORDER;
  top = bottom + EDITBOXHEIGHT;

  /* Gate Skip Factors */
  textbox = NewTextBox(left, right, bottom, top,
		       0, "Gate Skip Text", "Gate Skip Factor:");
  SetVar(textbox, PARENT, PanelContents);
  PrefixList(PanelContents, textbox);

  left = right + NXR_MIDDLE_SEP;
  right = left + NXR_ITEMWIDTH;

  var = GetRealVar("AddNXRControls", FileReader, GATESKIP);
  if (!var)
    SetVar(FileReader, GATESKIP, NewReal(0.0));
  textbox = NewTextBox(left, right, bottom, top,
		       EDITABLE + WITH_PIT + ONE_LINE,
		       "Gate Skip", "0");
  SetVar(textbox, PARENT, PanelContents);
  PrefixList(PanelContents, textbox);
  SetTextAlign(textbox, RIGHTALIGN);
  AssocTextRealControlWithVar(textbox, FileReader, GATESKIP, 
			      0, plusInf, TR_NE_TOP);
  SetVar(textbox, HELPSTRING,
	 NewString("This value indicates the number of gates \
to skip during file reading."));

  return NULLOBJ;
}

#endif

#ifdef PROTO
void    InitWhisselFiles(void)
#else
void    InitWhisselFiles()
#endif
{
#ifndef RELEASE
  ObjPtr  fileReader;

  fileReader = NewFileReader("NXR");
  SetVar(fileReader, EXTENSION, NewString("nxr"));
  SetVar(fileReader, HELPSTRING,
	 NewString("This file reader ingests NEXRAD (NEXt generation RADar) \
volume scans which have been pre-processed by a tape-to-disk file \
converter.  Since NEXRAD volume scans can be quite large, file reader \
options have been provided to reduce the amount of data processed."));
  SetMethod(fileReader, READALL, ReadNXRFile);
  SetMethod(fileReader, ADDCONTROLS, AddNXRControls);

  SetVar(fileReader, DOREFDATASETS, ObjTrue);
  SetVar(fileReader, DOVELDATASETS, ObjTrue);
  SetVar(fileReader, ELEVATIONSKIP, NewReal(0.0));
  SetVar(fileReader, RADIALSKIP, NewReal(0.0));
  SetVar(fileReader, GATESKIP, NewReal(0.0));

  /*EMP added save setting stuff*/
  AddSnapVar(fileReader, DOREFDATASETS);
  AddSnapVar(fileReader, DOVELDATASETS);
  AddSnapVar(fileReader, ELEVATIONSKIP);
  AddSnapVar(fileReader, RADIALSKIP);
  AddSnapVar(fileReader, GATESKIP);
  ApplySavedSettings(fileReader);

#endif
}
