/*
 *  readparm.c
 *
 *  parameter file reading routines for jimslide
 *  copyright Jim Leonard 1999
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "slide.h"
/* #include "whichway.h" */


int EatString(char **sptr, char *cstr, int critical);
int EatInt(char **sptr, int *iptr, int critical);
int EatPair(char **sptr, int *i1ptr, int *i2ptr, int critical);
int EatQuotedChar(char **sptr, char *cptr, int critical);


extern int numPiece;
extern int numSpace;
extern int numType;
extern int bitsPerType;
extern int compressedPuzSize;
extern int xsize;
extern int ysize;
extern int startLoc;
extern int endLoc;
extern int totSize;
extern int concurrent;
extern int winOnly;

extern int maxNewPos;
extern int maxEntriesPerSl;

extern int directed;

extern unsigned int cutoffStep;

extern int checkFullTree;

extern int useLetters;
extern int noGraph;
extern int useLabels;

extern int numWorkFiles;
extern int bigStorage;
extern int bigmem;
extern int smallmem;


extern PieceDef *piece;


int ReadParms(int argc, char *argv[])
   {
   char defFileName[] = "puz.txt";
   char *fileName;
   FILE *infile;
   int numiPiece = 0;
   PieceDef *iPiece;
   PieceDef *curPiece;
   int pieceAlloc;
   char instring[1024];
   char *sptr;
   int pType;
   int pStep;
   int a, b, c;
   int curType;
   int numBloc = 0;
   int noConcurrent = 0;
   int numPieceWinCond = 0; /* used for determining whether concurrent solve possible */
   int line, offset1, offset2;
   int blockCount = 0;
   int parenCount;
   int blocks;
   int *intPool;
   int firstPiece;

   wallList.numWall = 0;
   winList.numWinCond = 0;


   if (argc > 1)
      fileName = argv[1];
   else
      fileName = defFileName;

   if (NULL == (infile = fopen(fileName, "r")))
      {
      printf("Can't open %s\n", fileName);
      exit(1);
      }

   pieceAlloc = 20;
   if (NULL == (iPiece = malloc(pieceAlloc * sizeof(PieceDef))))
      {
      printf("ReadParm - can't malloc initial iPiece\n");
      exit(1);
      }

   while (NULL != fgets(instring, 1020, infile))
      {
      for (sptr = instring; *sptr && *sptr != ':'; ++sptr)
         *sptr = toupper(*sptr);

      sptr = instring;

      if (EatString(&sptr, "XSIZE:", 0))
         {
         EatInt(&sptr, &xsize, 1);
         xsize += 2;
         continue;
         }

      else if (EatString(&sptr, "YSIZE:", 0))
         {
         EatInt(&sptr, &ysize, 1);
         ysize += 2;
         continue;
         }

      else if (EatString(&sptr, "PIECE:", 0))
         {
         if (numiPiece == pieceAlloc)
            {
            pieceAlloc += 20;
            if (NULL == (iPiece = (PieceDef *)realloc(iPiece, pieceAlloc * sizeof(PieceDef))))
               {
               printf("ReadParm - Can't realloc iPiece\n");
               exit(1);
               }
            }
         curPiece = &iPiece[numiPiece++];
         EatInt(&sptr, &curPiece->pieceType, 1);
         curPiece->startX = -1;
         curPiece->startY = 0;
         curPiece->moveX = 1;
         curPiece->moveY = 1;
         curPiece->curLoc = 0;
         curPiece->numBloc = 0;
         curPiece->xstep = 0;
         curPiece->ystep = 0;
 
         /* count the '(' so we can figure out how big an array to allocate */
         parenCount = 0;
         for (a = 0; sptr[a]; ++a)
            if (sptr[a] == '(')
               ++parenCount;
         if (NULL == (curPiece->xbloc = (int *) calloc(parenCount * 2, sizeof (int))))
            {
            printf("ReadParm - Can't calloc for iPiece");
            exit(1);
            }
         curPiece->ybloc = &curPiece->xbloc[parenCount];

         while (EatPair(&sptr, &curPiece->xbloc[curPiece->numBloc],
                     &curPiece->ybloc[curPiece->numBloc], 0))
            {
            ++curPiece->numBloc;
            ++blockCount;
            }
         continue;
         }

      else if (EatString(&sptr, "PUT:", 0))
         {
         EatInt(&sptr, &pType, 1);
         for (a = 0; a < numiPiece; ++a)
            if (iPiece[a].pieceType == pType)
               break;

         if (a >= numiPiece)
            {
            printf("unknown piece type to be put\n");
            exit(0);
            }
         curPiece = &iPiece[a];
         if (curPiece->startX != -1)
            {
            if (numiPiece == pieceAlloc)
               {
               pieceAlloc += 20;
               if (NULL == (iPiece = (PieceDef *) realloc(iPiece, pieceAlloc * sizeof(PieceDef))))
                  {
                  printf("ReadParm - Can't realloc iPiece in put\n");
                  exit(1);
                  }
               curPiece = &iPiece[a];
               }
            memcpy(&iPiece[numiPiece], curPiece, sizeof(PieceDef));
            curPiece = &iPiece[numiPiece++];
            }
         EatPair(&sptr, &curPiece->startX, &curPiece->startY, 1);
         if (!EatQuotedChar(&sptr, &curPiece->label, 0))
            curPiece->label = 0;
         continue;
         }

      else if (EatString(&sptr, "LOCKY:", 0))
         {
         EatInt(&sptr, &pType, 1);
         for (a = 0; a < numiPiece; ++a)
            if (iPiece[a].pieceType == pType)
               iPiece[a].moveY = 0;
         continue;
         }

      else if (EatString(&sptr, "LOCKX:", 0))
         {
         EatInt(&sptr, &pType, 1);
         for (a = 0; a < numiPiece; ++a)
            if (iPiece[a].pieceType == pType)
               iPiece[a].moveX = 0;
         continue;
         }

      else if (EatString(&sptr, "YSTEP:", 0))
         {
         EatInt(&sptr, &pType, 1);
         EatInt(&sptr, &pStep, 1);
         for (a = 0; a < numiPiece; ++a)
            if (iPiece[a].pieceType == pType)
               iPiece[a].ystep = pStep;
         continue;
         }

      else if (EatString(&sptr, "XSTEP:", 0))
         {
         EatInt(&sptr, &pType, 1);
         EatInt(&sptr, &pStep, 1);
         for (a = 0; a < numiPiece; ++a)
            if (iPiece[a].pieceType == pType)
               iPiece[a].xstep = pStep;
         continue;
         }

      else if (EatString(&sptr, "WALL:", 0))
         {
         EatPair(&sptr, &wallList.wallX[wallList.numWall],
                  &wallList.wallY[wallList.numWall], 1);
         ++wallList.numWall;
         continue;
         }

      else if (EatString(&sptr, "HWALL:", 0))
         {
         EatInt(&sptr, &line, 1);
         EatPair(&sptr, &offset1, &offset2, 1);
         if (offset1 > offset2)
            {
            a = offset1;
            offset1 = offset2;
            offset2 = a;
            }
         for (a = offset1; a <= offset2; ++a)
            {
            wallList.wallX[wallList.numWall] = a;
            wallList.wallY[wallList.numWall++] = line;
            }
         }

      else if (EatString(&sptr, "VWALL:", 0))
         {
         EatInt(&sptr, &line, 1);
         EatPair(&sptr, &offset1, &offset2, 1);
         if (offset1 > offset2)
            {
            a = offset1;
            offset1 = offset2;
            offset2 = a;
            }
         for (a = offset1; a <= offset2; ++a)
            {
            wallList.wallX[wallList.numWall] = line;
            wallList.wallY[wallList.numWall++] = a;
            }
         }

      else if (EatString(&sptr, "WIN:", 0))
         {
         EatInt(&sptr, &winList.winCond[winList.numWinCond].iPieceType, 1);
         EatPair(&sptr, &winList.winCond[winList.numWinCond].posX,
                  &winList.winCond[winList.numWinCond].posY, 1);

         if (winList.winCond[winList.numWinCond].iPieceType)
            ++numPieceWinCond;
         else
            winList.winCond[winList.numWinCond].pieceType = 0;
         ++winList.numWinCond;
         continue;
         }
      else if (EatString(&sptr, "NOCONCURRENT:", 0))
         {
         noConcurrent = 1;
         }
      else if (EatString(&sptr, "WINONLY:", 0))
         {
         winOnly = 1;
         }
      else if (EatString(&sptr, "BIGMEM:", 0))
         {
         EatInt(&sptr, &bigmem, 1);
         }
      else if (EatString(&sptr, "SMALLMEM:", 0))
         {
         EatInt(&sptr, &smallmem, 1);
         }
      else if (EatString(&sptr, "DIRECTED:", 0))
         {
         EatInt(&sptr, &directed, 1);
         }
      else if (EatString(&sptr, "CUTOFF:", 0))
         {
         EatInt(&sptr, &cutoffStep, 1);
         }
      else if (EatString(&sptr, "CHECKFULLTREE:", 0))
         {
         checkFullTree = 1;
         }
      else if (EatString(&sptr, "USELETTERS:", 0))
         {
         useLetters = 1;
         }
      else if (EatString(&sptr, "NOGRAPH:", 0))
         {
         noGraph = 1;
         }
#if 0
      else if (EatString(&sptr, "USELABELS:", 0))
         {
         useLabels = 1;
         }
#endif
      else if (EatString(&sptr, "NUMWORKFILES:", 0))
         {
         EatInt(&sptr, &numWorkFiles, 1);
         }
      else if (EatString(&sptr, "BIGSTORAGE:", 0))
         {
         bigStorage = 1;
         }
      }
   
   fclose(infile);

   if (!xsize)
      {
      printf("xsize undefined\n");
      exit(1);
      }
   if (!ysize)
      {
      printf("ysize undefined\n");
      exit(1);
      }

   numType = 0;
   numPiece = 0;
   /* STILL NEED TO CONVERT iPiece HERE */

   /* first allocate contiguous space for the pieces and their blocks */
#if 0
   if (NULL == (piece = (PieceDef *) malloc(numiPiece * sizeof(PieceDef) + blockCount * 11 * sizeof(int))))
      {
      printf("ReadParm - Can't malloc piece");
      exit(1);
      }
   intPool = (int*)(&piece[numiPiece]);
/* #else */
   if (NULL == (piece = (PieceDef *) calloc(numiPiece, sizeof(PieceDef))))
      {
      printf("ReadParm - Can't malloc piece");
      exit(1);
      }
   intPool = (int*)calloc(blockCount * 11, sizeof(int));
#else
   if (NULL == (piece = (PieceDef *) calloc(numiPiece, sizeof(PieceDef))))
      {
      printf("ReadParm - Can't malloc piece");
      exit(1);
      }

   blockCount = 0;
   for (a = 0; a < numiPiece; ++a)
      blockCount += iPiece[a].numBloc;

   intPool = (int*)calloc(blockCount * 11, sizeof(int));
#endif

   for (a = 0; a < numiPiece; ++a)
      {
      if (iPiece[a].startX == -1)
         continue;
      curType = iPiece[a].pieceType;
      ++numType;
      firstPiece = -1;
      for (b = a; b < numiPiece; ++b)
         {
         if (iPiece[b].pieceType == curType)
            {
            memcpy(&piece[numPiece], &iPiece[b], sizeof(PieceDef));
            piece[numPiece].pieceType = numType;
            blocks = piece[numPiece].numBloc;
            numBloc += blocks;
            if (firstPiece == -1)
               {
               firstPiece = numPiece;
               piece[numPiece].xbloc = intPool;
               piece[numPiece].ybloc = &intPool[blocks];
               piece[numPiece].xyoffset = &intPool[blocks * 2];
               for (c = 0; c < 4; ++c)
                  {
                  piece[numPiece].moveTo[c] = &intPool[blocks * (c + 3)];
                  piece[numPiece].moveFrom[c] = &intPool[blocks * (c + 7)];
                  }
               intPool = &intPool[blocks * 11];
               memcpy(piece[numPiece].xbloc, iPiece[b].xbloc, blocks * sizeof(int));
               memcpy(piece[numPiece].ybloc, iPiece[b].ybloc, blocks * sizeof(int));
               }
            else
               {
               piece[numPiece].xbloc = piece[firstPiece].xbloc;
               piece[numPiece].ybloc = piece[firstPiece].ybloc;
               piece[numPiece].xyoffset = piece[firstPiece].xyoffset;
               for (c = 0; c < 4; ++c)
                  {
                  piece[numPiece].moveTo[c] = piece[firstPiece].moveTo[c];
                  piece[numPiece].moveFrom[c] = piece[firstPiece].moveFrom[c];
                  }
               }
               
            ++numPiece;
            iPiece[b].startX = -1;
            }
         }
      for (b = 0; b < winList.numWinCond; ++b)
         if (winList.winCond[b].iPieceType == curType)
            winList.winCond[b].pieceType = numType;
      }

   startLoc = xsize + 1;
   endLoc = (ysize - 1) * xsize - 1;
   totSize = xsize * ysize;
   numSpace = (xsize - 2) * (ysize - 2) - numBloc - wallList.numWall;
   bitsPerType = 1;
   b = 2;
   for (a = 0; a < 8; ++a)
      {
      if (b > numType)
         break;
      b = b * 2;
      ++bitsPerType;
      }

   compressedPuzSize = (bitsPerType * (numPiece + numSpace)) / 8;
   if ((bitsPerType * (numPiece + numSpace)) % 8)
      ++compressedPuzSize;

   maxNewPos = bigmem / (compressedPuzSize + SIZE_BT_NODE_HEADER);
   maxEntriesPerSl = smallmem / (compressedPuzSize);
   
   for (a = 0; a < winList.numWinCond; ++a)
      {
      winList.winCond[a].posXY = (winList.winCond[a].posY + 1) 
               * xsize + winList.winCond[a].posX + 1;
      }


   if (numPieceWinCond == numPiece && !noConcurrent)
      concurrent = 1;
   else
      concurrent = 0;

   if (bigmem < smallmem)
      {
      printf("bigmem must be larger than smallmem.\n");
      exit(0);
      }

   if (bigStorage)
      {
      /* look at MergeTempFiles() for an explanation */
      if (bigmem < smallmem * 2 + 1024 + 2 * SIZE_SL_HEADER + SIZE_BT_HEADER)
         {
         printf ("bigmem must be greater than 2 * smallmem + %d\n",
               1024 + 2 * SIZE_SL_HEADER + SIZE_BT_HEADER);
         exit(0);
         }

      if (directed)
         {
         printf("no directed searches when using bigstorage option\n");
         exit(0);
         }
      }

   if (concurrent && directed)
      {
      printf("I don't currently support concurrent directed searches\n");
      printf("add 'NOCONCURRENT:' to the config file\n");
      exit(0);
      }

   printf("numPiece - %d\n", numPiece);
   printf("numSpace - %d\n", numSpace);
   printf("numType - %d\n", numType);
   printf("bitsPerType - %d\n", bitsPerType);
   printf("compressedPuzSize - %d\n", compressedPuzSize);
   printf("xsize - %d\n", xsize);
   printf("ysize - %d\n", ysize);
#ifdef DEBUG_INFO
   printf("startLoc - %d\n", startLoc);
   printf("endLoc - %d\n", endLoc);
   printf("totSize - %d\n", totSize);
   printf("concurrent - %d\n", concurrent);
   printf("maxNewPos - %d\n", maxNewPos);
   printf("maxEntriesPerSl - %d\n", maxEntriesPerSl);
#endif
   return 1;
   }


int EatString(char **sptr, char *cstr, int critical)
   {
   char *cptr = *sptr;
   int len = strlen(cstr);


   for (cptr = *sptr; *cptr && (*cptr <= ' '); ++cptr);
   
   if (!strncmp(cstr, cptr, len))
      {
      *sptr = cptr + len;
      return 1;
      }
   if (critical)
      {
      printf("Expected %s\n", cstr);
      exit(1);
      }
   return 0;
   }

int EatInt(char **sptr, int *iptr, int critical)
   {
   char *cptr;
   int val = 0;

   for (cptr = *sptr; *cptr && (*cptr <= ' '); ++cptr);

   *iptr = atoi(cptr);
   if (*cptr == '-')
      ++cptr;

   if (!isdigit(*cptr))
      {
      if (critical)
         {
         printf("Expected a numeric at %s\n", cptr);
         exit(1);
         }
      else
         return 0;
      }

   for (; isdigit(*cptr); ++cptr);
   *sptr = cptr;
   return 1;
   }

int EatQuotedChar(char **sptr, char *cptr, int critical)
   {
   char *tptr = *sptr;
   if (EatString(&tptr, "'", critical))
      {
      *cptr = *(tptr++);
      if (EatString(&tptr, "'", critical))
         {
         *sptr = tptr;
         return 1;
         }
      }
   return 0;
   }

int EatPair(char **sptr, int *i1ptr, int *i2ptr, int critical)
   {
   char *tptr = *sptr;

   if (EatString(&tptr, "(", critical))
      if (EatInt(&tptr, i1ptr, critical))
         if (EatString(&tptr, ",", critical))
            if (EatInt(&tptr, i2ptr, critical))
               if (EatString(&tptr, ")", critical))
                  {
                  *sptr = tptr;
                  return 1;
                  }
   return 0;
   }

