/* mkcasastaticnc.c
 *                *
 *              Emilio          4/1/99
 *                Updated program.
 *              Emilio          2/11-24/99
 *                Wrote and debugged program.
 * COMPILE AS: gccemu mkcasastaticnc
 ******************************************************************************/

/*          STEPS
- read all the static maps: AMAZON BASIN MASK, elev, veg map, soil type map
  (need a specified order for reading the variables: mask, elev, veg, soil)
- read in the 2 soil data tables into two arrays, using a structure
- step through each pixel (do entire continent in the window, not just within the
  mask), get the soil ID at current pixel, then get the %sand and %clay for all
  depths, store in an array.
** consider writing into the attributes the entire "legend", or table giving names
   to each ID, for veg maps and soil types
*/


#include "emu.h"

const char * cpszUsage = {"usage: mkcasastaticnc \n\
.\n\
"};

/* local functions */
void Setup(void);
void ProcessAndOutput(void);

void WriteSMap(short v, char *name, char *lname, char *units, short sFillValue);
void AssignRootDepth(void);
short GetDepthNumber(int depth);
short GetProfileIndex(int soilID);
short GetSoilGroup(short pcurr);


/* local variables */
#define  ZINTERVALS   13
int errnc;
int ncID, tID, varIDsand, varIDclay;
static size_t count[] = {0,0,0}, start[] = {0,0,0};
float *SoilTypeMap, *map, *maptmp;
short **sand, **clay;
static double Zvalues[] = {50.0,100.0,150.0,200.0,250.0,300.0,
                            400.0,500.0,600.0,700.0,800.0,900.0,1000.0};

typedef struct _soilprof
{
  short   soilID;
  char    soilType[10];
  char    soilGroup[10];
  float   depthbot[ZINTERVALS];
  float   clay[ZINTERVALS];
  float   sand[ZINTERVALS];
} SOILPROF;

SOILPROF soildb[300];
short TotProfiles = 0;


int main(int argc, char *argv[])
{
  AssignString(&pszNCtitle, "EOS-Amazon Model static input data sets, Ver. 2.0");
  AssignString(&pszNChist, "Created by EMU/mkcasastaticnc.c. Feb. 24, 1999");

  Initialize(&argc, argv, NOMASK);

  Setup();
  ProcessAndOutput();

  CleanUp();

  return(0);
}

/* ================================================================= */
void Setup(void)
{
  int p, z;

  maptmp  = alloc1d_f(0,nRows*nCols-1);
  map  = alloc1d_f(0,nRows*nCols-1);
  sand = alloc2d_s(0,ZINTERVALS-1,0,nRows*nCols-1);
  clay = alloc2d_s(0,ZINTERVALS-1,0,nRows*nCols-1);

  for (p=0; p < 300; p++)
  {
    soildb[p].soilID = 0;
    for (z=0; z < ZINTERVALS; z++)
    {
      soildb[p].depthbot[z] = -999.0;
      soildb[p].clay[z]     = -999.0;
      soildb[p].sand[z]     = -999.0;
    }
  }

  /* CREATE OUTPUT NETCDF FILE */
  SetupDefaultAxes(FALSE, FALSE);
  FillAxisProp("z", "Soil Depth (bottom)", "cm", FALSE, 0.0, 1000.0, ZINTERVALS, Zvalues, TRUE);
  count[0] = 1;
  count[1] = nRows;
  count[2] = nCols;
  ncID = newNCsetup(&tID, pszOutputFile, pszNCtitle, pszNChist);
}

/* ================================================================= */
void ProcessAndOutput(void)
{
  int nLineArgs, i, z, soilID, depth;     int bug=0;
  float claycurr, sandcurr;
  short p=0, pcurr, sFillValue = -999;
  char line[200], *VarArgs[20];
  FILE *fin;


  /* READ MAPS AND WRITE THEM INTO NC FILE */
  WriteSMap(0, "amzmask", "Amazon Basin Mask", "ID", 0);
  WriteSMap(1, "elev30", "GTOPO30 DEM", "m", 0);
  WriteSMap(2, "landcover_d", "Defries 8km Land Cover Map", "ID", 0);
  AssignRootDepth();
  WriteSMap(-1, "rootdepth_d", "Rooting Depth from Defries Land Cover", "cm", 0);
  WriteSMap(3, "faosoiltype", "FAO Soil Type", "ID", 0);
  WriteSMap(4, "soiltype", "Soil Type, FAO+EMBRAPA", "ID", 0);
  SoilTypeMap = map;                 /* SoilTypeMap will be a float pointer */


  /* ====READ SOIL TEXTURE TABLES, FILL STRUCTURE ARRAY (BOTH INTO A SINGLE ARRAY)====  */

  if ((fin = fopen("/oc3/am_arcdir/casa/soils2/faosoiltexture.csv","r")) == NULL)
    nrerror("Couldn't open file %s for writing\n", "faosoiltexture.csv");
  (void)fgetline(line, 200, fin);  /* omit the first line (field headers) */
  while (fgetline(line, 200, fin) != 0)
  {
    nLineArgs = GetLineElements(VarArgs, line, ',');

    soilID = atoi(VarArgs[0]);
    pcurr = GetProfileIndex(soilID);
    if (pcurr != p)
    {
      p = pcurr;
      strcpy(soildb[p].soilType, VarArgs[2]);
      strcpy(soildb[p].soilGroup, VarArgs[3]);
    }

    depth    = atoi(VarArgs[1]);
    claycurr = atof(VarArgs[4]);
    sandcurr = atof(VarArgs[5]);
    z = GetDepthNumber(depth);
    soildb[p].depthbot[z] = (float)depth;
    soildb[p].clay[z]     = claycurr;
    soildb[p].sand[z]     = sandcurr;

    /* clear the allocated memory for VarArgs */
    for (i=0; i<nLineArgs; i++)
      free(VarArgs[i]);
  }
  fclose(fin);

  /* fill in the missing horizons with data from the SoilGroup, or upper horizons */
  /* THIS BLOCK WILL ONLY EXTRAPOLATE MISSING HORIZONS IN *EXISTING* PROFILES! IF THE WHOLE PROFILE
     IS MISSING (the soil type), IT DOESN'T DO ANYTHING */
  for (pcurr=0; pcurr < TotProfiles; pcurr++)
  {
    p = GetSoilGroup(pcurr);
    for (z=0; z < ZINTERVALS; z++)
      if ((int)soildb[pcurr].depthbot[z] == -999)
      {
        if ((p == pcurr) || ((int)soildb[p].depthbot[z] == -999))
        {
          /* if z == 0 when p == pcurr, we have a problem! */
          soildb[pcurr].depthbot[z] = Zvalues[z];
          soildb[pcurr].clay[z]     = soildb[p].clay[z-1];
          soildb[pcurr].sand[z]     = soildb[p].sand[z-1];
        }
        else
        {
          soildb[pcurr].depthbot[z] = Zvalues[z];
          soildb[pcurr].clay[z]     = soildb[p].clay[z];
          soildb[pcurr].sand[z]     = soildb[p].sand[z];
        }
      }
  }

  if ((fin = fopen("/oc3/am_arcdir/casa/soils2/embrapasoiltexture.csv","r")) == NULL)
    nrerror("Couldn't open file %s for writing\n", "faosoiltexture.csv");
  (void)fgetline(line, 200, fin);  /* omit the first line (field headers) */
  while (fgetline(line, 200, fin) != 0)
  {
    nLineArgs = GetLineElements(VarArgs, line, ',');

    soilID = atoi(VarArgs[0]);
    pcurr = GetProfileIndex(soilID);
    if (pcurr != p)
    {
      p = pcurr;
      strcpy(soildb[p].soilType, VarArgs[2]);
      strcpy(soildb[p].soilGroup, VarArgs[3]);
    }

    depth    = atoi(VarArgs[1]);
    claycurr = atof(VarArgs[4]);
    sandcurr = atof(VarArgs[5]);
    z = GetDepthNumber(depth);
    soildb[p].depthbot[z] = (float)depth;
    soildb[p].clay[z]     = claycurr;
    soildb[p].sand[z]     = sandcurr;

    /* clear the allocated memory for VarArgs */
    for (i=0; i<nLineArgs; i++)
      free(VarArgs[i]);
  }
  fclose(fin);

if (bDebug)
  printf("\n**bug=%d: Past read soil texture tables block; TotProfiles=%d\n", bug++,TotProfiles);


  /* ============== CREATE THE MAPS OF SAND AND CLAY FOR EACH HORIZON ============== */
  for (i = 0; i < nRows*nCols; i++)
  {
    soilID = (int) SoilTypeMap[i];
    p = GetProfileIndex(soilID);
    for (z = 0; z < ZINTERVALS; z++)
    {
      if (bDebug && (i % 20000 == 0) && (z % 3 == 0))
         printf("\n i=%d, soilID=%d, p=%d, z=%d, clay=%f", i,soilID,p,z,soildb[p].clay[z]);

      /* for profile with index "p" in soildb array, get the depth "z";
         BUT if data for that depth doesn't exist, use FillValue !!!?? (-999) */
      /* Use a scale factor of 0.01, offset of 0 */
      clay[z][i] = (soildb[p].depthbot[z] > 0.0) ? (short) floor(100.0 * soildb[p].clay[z] + 0.5) : sFillValue;
      sand[z][i] = (soildb[p].depthbot[z] > 0.0) ? (short) floor(100.0 * soildb[p].sand[z] + 0.5) : sFillValue;
    }
  }

  /* ==================== WRITE THE DATA INTO NETCDF VARIABLES ==================== */
  clay">varIDclay = DefNCvar(ncID, "clay", EMU_SHORT, "xyz", "Percent Clay, to this depth", "%");
  AddNCCompressionAttributes(ncID, varIDclay, 0.01, 0.0);
  sand">varIDsand = DefNCvar(ncID, "sand", EMU_SHORT, "xyz", "Percent Sand, to this depth", "%");
  AddNCCompressionAttributes(ncID, varIDsand, 0.01, 0.0);

  for (z=0; z < ZINTERVALS; z++)
  {
    start[0] = z;
    errnc = nc_put_vara_short(ncID, clay">varIDclay, start, count, clay[z]);
    errnc = nc_put_vara_short(ncID, sand">varIDsand, start, count, sand[z]);
  }

  errnc = nc_close(ncID);
}

/* ================================================================= */
void ProcessCommandLineArgs(int * pArgc, char * argv[])
{
}
/* ================================================================= */
void LocalCleanUp(void)
{
  free1d_f(maptmp,0,nRows*nCols-1);
  free1d_f(map,0,nRows*nCols-1);
  free2d_s(sand, 0,ZINTERVALS-1,0,nRows*nCols-1);
  free2d_s(clay, 0,ZINTERVALS-1,0,nRows*nCols-1);
}

/* ================================================================= */
/* ================================================================= */
void WriteSMap(short v, char *name, char *lname, char *units, short sFillValue)
{
  int varID;
  int i, irev, col, row;

  if (v >= 0)
  {
    ReadXY(maptmp, pstVars[v], NONE, NONE);

    for (i = 0; i < nRows*nCols; i++)
    {
      irev = getRowRevCol(i, &col, &row);
      map[irev] = maptmp[i];
    }
  }

  varID = DefNCvar(ncID, name, EMU_SHORT, "xy", lname, units);
  AddAttribute(ncID, "none", varID, "none", "_FillValue", EMU_SHORT, 1, &sFillValue);
  errnc = nc_put_var_float(ncID, varID, map);
}
/* ================================================================= */
void AssignRootDepth(void)
{
  int i, landcoverID;

  for(i=0; i < nRows*nCols; i++)
  {
    landcoverID = (int) map[i];

    switch(landcoverID)
    {
      case 0:       /* water */
        map[i] = 0.0;
        break;
      case 1:       /* Evergreen Needleleaf Forests */
        map[i] = 500.0;
        break;
      case 2:       /* Evergreen Broadleaf Forests */
        map[i] = 1000.0;
        break;
      case 3:       /* Deciduous Needleleaf Forests ???? */
        map[i] = 500.0;
        break;
      case 4:       /* Deciduous Broadleaf Forests */
        map[i] = 1000.0;
        break;
      case 5:       /* Mixed Forests */
        map[i] = 800.0;
        break;
      case 6:       /* Woodlands */
        map[i] = 700.0;
        break;
      case 7:       /* Wooded Grasslands/Shrubs */
        map[i] = 400.0;
        break;
      case 8:       /* Closed Bushlands or Shrublands */
        map[i] = 300.0;
        break;
      case 9:       /* Open Shrublands */
        map[i] = 200.0;
        break;
      case 10:      /* Grasses */
        map[i] = 200.0;
        break;
      case 11:      /* Croplands */
        map[i] = 100.0;
        break;
      case 12:      /* Bare */
        map[i] = 50.0;
        break;
      case 13:      /* Mosses and Lichens */
        map[i] = 50.0;
        break;
      default:
        map[i] = 0.0;
        break;
    }
  }
}
/* ================================================================= */
short GetDepthNumber(int depth)
{
  short z=0;

  while(depth != (int)Zvalues[z])
    z++;

  return(z);
}
/* ================================================================= */
short GetProfileIndex(int soilID)
{
  int p=0;

  /* look for soildb[].soilID already assigned */
  while (soildb[p].soilID != 0)
  {
    if (soildb[p].soilID == soilID)
      return(p);
    p++;
  }

  /* new soildb[].soilID; also calculates number of unique profiles in dataset, TotProfiles */
  if (soilID > 0)
  {
    TotProfiles++;
    soildb[p].soilID = soilID;
    return(p);
  }
  else
    return(TotProfiles);
}
/* ================================================================= */
short GetSoilGroup(short pcurr)
{
  short p=0;

  while (!istype(soildb[p].soilType, soildb[pcurr].soilGroup) && ((int)strlen(soildb[p].soilType) != 1))
    p++;

  return(p);
}