/* masks.c
 *         *
 *       Emilio          12/10/2000
 *         Moved InitSiteIndex() prototype from emu.h to here, to make it a
 *         local function b/c it's used only here.  In ReadMask(), changed
 *         name of szszCurrentFile to szCurrentFile. Made some layout changes.
 *         *
 * Function file for all the C programs written to analyze binary and NetCDF
 * input and output for modelling using the Environmental Modeling Utilities,
 * or "EMU" package.
 ******************************************************************************/

#include "emu.h"


#define  MASKIDSFILE       "./maskids.cfg"
#define  MASKIDSMAXLINLEN  300

#define M 7
#define NSTACK 50

/* functions and variables defined in function.c  */
extern char * pszConfigFile;
extern int GetLine(FILE * fpin, char * pszLine, char * pszMarker, BOOL bFromStart);

/* LOCAL FUNCTIONS: Important "system" functions*/
void   ReadMask(void);

/* LOCAL FUNCTIONS: "utilities" used to support the above functions */
int    InitSiteIndex(int *ids, int maskid, int *idsLength);
void   Parse(char * pszLine, int n);
void   extractElement(char * linesegment, int n, int * pnArray);
void   indexx(unsigned short n, int arr[], unsigned short indx[]);


/* ================================================================= */

BOOL masktest(int maskid)
{
  int m=0;

  /* When MASKTYPE is "rangefileID1" and the Aggregate ID config file hasn't been *
   * read yet (ie, in the first masktest() call in ReadMask() ), MskSitesN == 0   *
   * and the 2nd if-then block will be used instead of the third                  */
  if (istype(szMaskType, "singleposval"))
    return((maskid == nRegionID) ? TRUE:FALSE);
  else if (istype(szMaskType, "rangeposval") || istype(szMaskType, "rangefileIDs")
           || (istype(szMaskType, "rangefileID1") && MskSitesN == 0))
    return((maskid > 0) ? TRUE:FALSE);
  else if (istype(szMaskType, "rangefileID1"))
  {
    for (m=0; m < Masks[0].nMaskids; m++)
      if (maskid == Masks[0].maskids[m])
        return(TRUE);

    return(FALSE);
  }
  else
    return(FALSE);
}

/* ================================================================= */

int siteindex(int *ids, int maskid)
{
  int s;

  /* look for ids[] already assigned */
  for (s=0; s < FlMskSitesN; s++)
    if (ids[s] == maskid)
      return(s);

  /* return NONE if maskid doesn't match an existing id */
  return(NONE);
}


/* ================================================================= */
/* ======================= LOCAL FUNCTIONS ========================= */

/* ================================================================= */
/* Read in the mask file, write the value selected as the mask key to
 * the global variable regionid;
 * request "-1" from varsfile if no specific key is desired;   */

void ReadMask(void)
{
  FILE * fpc, * fpmask, * maskidsfile;
  int nsite, n, i, m;
  int *tmpSitesIDs, *tmpSitesCellcount;
  double *tmpSitesArea;
  int firstvalidindx=0, nsort;
  unsigned short *sortindx;
  char szLine[MASKIDSMAXLINLEN];
  char szMaskPath[FILENAMELEN];
  char szMaskDataType[10];
  char szCurrentFile[FILENAMELEN];
  BYTE  *pbyMask;
  SHORT *psMask;
  unsigned short *pusMask;


  if (!bQuiet)  printf("\n---- Reading Mask in ReadMask() \n");

  if ((fpc = fopen(pszConfigFile, "rt")) == NULL)
    ExitError("Couldn't open file %s for reading in ReadMask\n", pszConfigFile);

  if (GetLine(fpc, szLine, "# MASK", TRUE))
    sscanf(szLine, "%s %s %d", szMaskPath, szMaskDataType, &nRegionID);
  else
    Warning("Couldn't find \"# MASK\" in file %s in ReadMask\n", pszConfigFile);

  if (GetLine(fpc, szLine, "# MASKTYPE", TRUE))
    sscanf(szLine, "%s", szMaskType);
      /* szMaskType: singleposval OR rangeposval OR rangefileIDs OR rangefileID1 */
  else
    Warning("Couldn't find \"# MASKTYPE\" in file %s in ReadMask\n", pszConfigFile);


  strcpy(szCurrentFile, szMaskPath);
  if ((fpmask = fopen(szCurrentFile, "r")) == NULL)
    ExitError("Couldn't open pMask file %s for reading\n", szCurrentFile);

  pMask = alloc1d_l(0,nRows*nCols-1);

  /* convert input file data type to long-type */
  if (istype(szMaskDataType, "byte"))
  {
    pbyMask = alloc1d_uc(0,nRows*nCols-1);
    if (fread(pbyMask, sizeof(BYTE), nRows*nCols, fpmask) != (size_t) (nRows*nCols))
      ExitError("Could not do fread in %s \n", szCurrentFile);
    for (i=0; i < nRows*nCols; i++)
      pMask[i] = (LONG) pbyMask[i];
    free1d_uc(pbyMask, 0,nRows*nCols-1);
  }
  else if (istype(szMaskDataType, "short"))
  {
    psMask = alloc1d_s(0,nRows*nCols-1);
    if (fread(psMask, sizeof(SHORT), nRows*nCols, fpmask) != (size_t) (nRows*nCols))
      ExitError("Could not do fread in %s \n", szCurrentFile);
    for (i=0; i < nRows*nCols; i++)
      pMask[i] = (LONG) psMask[i];
    free1d_s(psMask, 0,nRows*nCols-1);
  }
  else if (istype(szMaskDataType, "ushort"))
  {
    pusMask = alloc1d_us(0,nRows*nCols-1);
    if (fread(pusMask, sizeof(unsigned short), nRows*nCols, fpmask) != (size_t) (nRows*nCols))
      ExitError("Could not do fread in %s \n", szCurrentFile);
    for (i=0; i < nRows*nCols; i++)
      pMask[i] = (LONG) pusMask[i];
    free1d_us(pusMask, 0,nRows*nCols-1);
  }
  else if (istype(szMaskDataType, "long"))
  {
    if (fread(pMask, sizeof(LONG), nRows*nCols, fpmask) != (size_t) (nRows*nCols))
      ExitError("Could not do fread in %s \n", szCurrentFile);
  }
  else
    ExitError("Mask data type %s not recognized \n", szMaskDataType);

  fclose(fpc);
  fclose(fpmask);

  /* ===== READ ALL UNIQUE IDS FROM MASK FILE INTO GLOBAL ARRAY SITEIDS ======= */

  MskSitesN=0; FlMskSitesN=0;

  /* allocate and initialize tmpSites* arrays */
  tmpSitesIDs       = alloc1d_i(1,SITESMAX);
  tmpSitesCellcount = alloc1d_i(1,SITESMAX);
  tmpSitesArea      = alloc1d_d(1,SITESMAX);
  for(n=1; n <= SITESMAX; n++)
  {
    tmpSitesIDs[n]       = 0;
    tmpSitesCellcount[n] = 0;
    tmpSitesArea[n]      = 0.0;
  }

  /* read the mask file and make initial (tmpSites*) assingments */
  for (i = 0; i < nRows*nCols; i++)
  {
    if (masktest((int)pMask[i]))
    {
      n = InitSiteIndex(&tmpSitesIDs[1], (int)pMask[i], &FlMskSitesN);

      tmpSitesCellcount[n+1]++;
      tmpSitesArea[n+1] += cellarea(i);
    }
  }

  if (bDebug)  printf(" FlMskSitesN = %d\n", FlMskSitesN);

  /* allocate and assign values to FlMskSitesIDs[], FlMskSitesCellcount[],          *
   * and FlMskSitesArea[]; also assign value to FlMskTotCellcount and FlMskTotArea  */

  /* sort tmpSitesIDs in ascending order by creating an index array */
  sortindx = alloc1d_us(1,SITESMAX);
  indexx(SITESMAX, tmpSitesIDs, sortindx);
  while (tmpSitesIDs[sortindx[firstvalidindx]] == 0)
    firstvalidindx++;

  /* now allocate and populate the sorted arrays into FlMskSites* arrays */
  FlMskSitesIDs         = alloc1d_i(0,FlMskSitesN-1);
  FlMskSitesCellcount   = alloc1d_i(0,FlMskSitesN-1);
  FlMskSitesArea        = alloc1d_d(0,FlMskSitesN-1);
  FlMskTotCellcount     = 0;
  FlMskTotArea          = 0.0;
  for (n=0; n < FlMskSitesN; n++)
  {
    nsort = sortindx[firstvalidindx+n];
    FlMskSitesIDs[n]       = tmpSitesIDs[nsort];
    FlMskSitesCellcount[n] = tmpSitesCellcount[nsort];
    FlMskSitesArea[n]      = tmpSitesArea[nsort];

    FlMskTotCellcount     += FlMskSitesCellcount[n];
    FlMskTotArea          += FlMskSitesArea[n];

    if (bDebug)
      printf(" FlMskSitesIDs[%d]=%d, FlMskSitesCellcount[%d]=%d, FlMskSitesArea[%d]=%.1f  \n",
      n,FlMskSitesIDs[n],n,FlMskSitesCellcount[n],n,FlMskSitesArea[n]/1000000.0);
  }

  free1d_i(tmpSitesIDs,1,SITESMAX);
  free1d_i(tmpSitesCellcount,1,SITESMAX);
  free1d_d(tmpSitesArea,1,SITESMAX);

  free1d_us(sortindx,1,SITESMAX);


  if (bDebug)  printf(" < alloc1d_MASK if-blocks \n");

  /* ===== populate aggregate ID Masks struct array and Msk* variables ===== */
  if (istype(szMaskType, "rangefileID"))
  {
     /* read the aggregate ID file */
     if ((maskidsfile = fopen(MASKIDSFILE, "r")) == NULL)
      nrerror("Couldn't open file %s for reading\n", MASKIDSFILE);

     /* MskSitesN = number of IDs (aggregate or otherwise) in the pMask */
     while(fgetline(szLine, MASKIDSMAXLINLEN, maskidsfile) != 0)
       MskSitesN++;
     rewind(maskidsfile);

     /* allocate space for array Masks of size MskSitesN and type MASKS */
     Masks = alloc1d_MASK(0,MskSitesN-1);

     for (n=0; n < MskSitesN; n++)
     {
        /* read individual IDs for nth Aggregate ID, then allocate space for array *
         * Masks[n].maskids; Then read IDs into that array for each Aggregate ID   */

        /* parse szLine, read and write id elements */
        (void) fgetline(szLine, MASKIDSMAXLINLEN, maskidsfile);
        Parse(szLine, n);

        if (bDebug)
        {
          printf("\nIN rangefileIDs for loop, Masks[%d].ID=%d: ", n,Masks[n].ID);
          for(m=0; m < Masks[n].nMaskids; m++)
            printf(" .maskids[%d]=%d: ", m,Masks[n].maskids[m]);
        }

        Masks[n].cellcount = 0;
        Masks[n].area      = 0.0;
        for(m=0; m < Masks[n].nMaskids; m++)
        {
          nsite = siteindex(FlMskSitesIDs, Masks[n].maskids[m]);
          Masks[n].cellcount += FlMskSitesCellcount[nsite];
          Masks[n].area      += FlMskSitesArea[nsite];
        }

     } /* END n FOR-LOOP */
     fclose(maskidsfile);
  }
  else if (istype(szMaskType, "rangeposval"))
  {
     /* allocate space for array Masks of size MskSitesN and type MASKS */
     MskSitesN  = FlMskSitesN;
     Masks      = alloc1d_MASK(0,MskSitesN-1);

     for (n=0; n < MskSitesN; n++)
     {
       Masks[n].ID          = FlMskSitesIDs[n];
       Masks[n].nMaskids    = 1;
       Masks[n].maskids     = alloc1d_i(0,0);
       Masks[n].maskids[0]  = FlMskSitesIDs[n];
       Masks[n].cellcount   = FlMskSitesCellcount[n];
       Masks[n].area        = FlMskSitesArea[n];
       if (bDebug)  printf("IN rangeposval for-loop, Masks[%d].ID=%d \n", n,Masks[n].ID);
     }
  }

  if (bDebug)  printf(" > alloc1d_MASK if-blocks; MskSitesN=%d \n", MskSitesN);

  /* allocate memory and assign values to MskSitesIDs[];   *
   * also assign value to MskTotCellcount and MskTotArea  */
  MskSitesIDs     = alloc1d_i(0,MskSitesN-1);
  MskTotCellcount = 0;
  MskTotArea      = 0.0;
  for (n=0; n < MskSitesN; n++)
  {
    IDs">MskSitesIDs[n]   = Masks[n].ID;
    MskTotCellcount += Masks[n].cellcount;
    MskTotArea      += Masks[n].area;
  }

  if (bDebug)
    printf(" > MskSitesIDs[n] for-block; MskSitesIDs[1]=%d, MskTotCellcount=%d, MskTotArea=%.1f \n",
            MskSitesIDs[1], MskTotCellcount, MskTotArea/1000000.0);
}


/* ================================================================= */
/* ================================================================= */

int InitSiteIndex(int *ids, int maskid, int *idsLength)
{
  int sindex=0;

  /* look for ids[] already assigned */
  while (ids[sindex] != 0)
  {
    if (ids[sindex] == maskid)
      return(sindex);
    sindex++;
  }

  /* new ids[]; also calculates number of unique IDs in mask file, FlMskSitesN */
  (*idsLength)++;
  ids[sindex] = maskid;
  return(sindex);
}
/* ================================================================= */

void Parse(char * pszLine, int n)
{
  char * pCh;
  char s[MASKIDSMAXLINLEN], s1[MASKIDSMAXLINLEN];
  int m, array[SITESMAX];

  Masks[n].nMaskids = 0;

  strcpy(s, pszLine);
  pCh = strchr(s, ':'); /* check s for :, no error checking   */
  *pCh = '\0';          /* clip the string s at :     */

  sscanf(s, "%d", &Masks[n].ID);

  strcpy(s1, pszLine);
  pCh = strchr(s1, ':');  /* find : again       */
  pCh++;                  /* skip to next char      */
  strcpy(s, pCh);         /* copy string into s from that point */

  while ((pCh = strchr(s, ',')) != NULL)
  {
    *pCh = '\0';
    pCh++;

    extractElement(s, n, array);
    strcpy(s, pCh);        /* copy string into s from that point  */
  }

  /* get the last entry - note that this will fail if there's no entry. */
  extractElement(s, n, array);

  /* now  allocate space for Masks[n].maskids array,
   * then write array elements from  array[i] to  Masks[n].maskids[i] */
  Masks[n].maskids = alloc1d_i(0,Masks[n].nMaskids - 1);
  for (m=0; m < Masks[n].nMaskids; m++)
    Masks[n].maskids[m] = array[m];
}

/* ================================================================= */

void extractElement(char * linesegment, int n, int * pnArray)
{
  char * pOper;
  char s[NAMELEN], s1[NAMELEN];
  int i, m;
  int nID, nFirstID, nLastID;


  strcpy(s, linesegment);

  if ((pOper = strchr(s, '-')) != NULL)  /* handle "-" operator */
  {
    *pOper = '\0';
    pOper++;

    sscanf(s, "%d", &nFirstID);
    strcpy(s1, pOper);
    sscanf(s1, "%d", &nLastID);

    /* make id assignments to aggregate mask */
    for (m=0; m < FlMskSitesN; m++)
      if (FlMskSitesIDs[m] <= nLastID)
      {
        if (FlMskSitesIDs[m] >= nFirstID)
        {
          pnArray[Masks[n].nMaskids] = FlMskSitesIDs[m];
          Masks[n].nMaskids++;
        }
      }
      else
        break;
  }
  else if ((pOper = strchr(s, '<')) != NULL) /* handle "<" operator (less or equal) */
  {
    *pOper = '\0';
    pOper++;

    strcpy(s1, pOper);
    sscanf(s1, "%d", &nLastID);

    /* make id assignments to aggregate mask */
    for (m=0; m < FlMskSitesN; m++)
      if (FlMskSitesIDs[m] <= nLastID)
      {
        pnArray[Masks[n].nMaskids] = FlMskSitesIDs[m];
        Masks[n].nMaskids++;
      }
      else
        break;
  }
  else if ((pOper = strchr(s, '>')) != NULL) /* handle ">" operator (greater or equal) */
  {
    *pOper = '\0';
    pOper++;

    strcpy(s1, pOper);
    sscanf(s1, "%d", &nFirstID);

    /* make id assignments to aggregate mask */
    for (m=0; m < FlMskSitesN; m++)
      if (FlMskSitesIDs[m] >= nFirstID)
      {
        pnArray[Masks[n].nMaskids] = FlMskSitesIDs[m];
        Masks[n].nMaskids++;
      }
  }
  else   /* no special operator present, just a single number */
  {
    sscanf(s, "%d", &nID);
    pnArray[Masks[n].nMaskids] = nID;
    Masks[n].nMaskids++;
  }
}

/* ================================================================== */
/* indexes an array arr[1..n], ie, outputs the array indx[1..n] such that
   arr[indx[j]] is in ascending order for j=1,2,...,N. The input quantities
   n and arr are not changed            */

void indexx(unsigned short n, int arr[], unsigned short indx[])
{
  unsigned short i,indxt,ir=n,j,k,l=1;
  int jstack=0,*istack;
  int a;

  istack=alloc1d_i(1,NSTACK);
  for (j=1;j<=n;j++) indx[j]=j;
  for (;;) {
    if (ir-l < M) {
      for (j=l+1;j<=ir;j++) {
        indxt=indx[j];
        a=arr[indxt];
        for (i=j-1;i>=1;i--) {
          if (arr[indx[i]] <= a) break;
          indx[i+1]=indx[i];
        }
        indx[i+1]=indxt;
      }
      if (jstack == 0) break;
      ir=istack[jstack--];
      l=istack[jstack--];
    } else {
      k=(l+ir) >> 1;
      SWAP(indx[k],indx[l+1]);
      if (arr[indx[l+1]] > arr[indx[ir]]) {
        SWAP(indx[l+1],indx[ir])
      }
      if (arr[indx[l]] > arr[indx[ir]]) {
        SWAP(indx[l],indx[ir])
      }
      if (arr[indx[l+1]] > arr[indx[l]]) {
        SWAP(indx[l+1],indx[l])
      }
      i=l+1;
      j=ir;
      indxt=indx[l];
      a=arr[indxt];
      for (;;) {
        do i++; while (arr[indx[i]] < a);
        do j--; while (arr[indx[j]] > a);
        if (j < i) break;
        SWAP(indx[i],indx[j])
      }
      indx[l]=indx[j];
      indx[j]=indxt;
      jstack += 2;
      if (jstack > NSTACK) nrerror("NSTACK too small in indexx.","");
      if (ir-i+1 >= j-l) {
        istack[jstack]=ir;
        istack[jstack-1]=i;
        ir=j-1;
      } else {
        istack[jstack]=j-1;
        istack[jstack-1]=l;
        l=i;
      }
    }
  }
  free1d_i(istack,1,NSTACK);
}