/*
 * Implementation of class PgmReader
 *
 * $Date$
 * $Revision$
 *
 */

#include "WImage/ImageArray.hh"
#include "WImage/PgmReader.hh"
#include "WImage/debug.h"
#include <cstdio>

#if !(defined __WATCOMC__ && __WATCOMC__ < 1230)
using namespace std;
#endif

static int uncomment (FILE *fhandle, int ch);
static int next (FILE* fhandle);

int
PgmReader::readfmt (void)
{
  
  FILE * fhandle = NULL;
  int ch = (int)' ';
  int ftype;
  int xsize = 0, ysize = 0, maxg = 0;
  long fp;
  enum states { s_type, s_size, s_read, s_done, s_error };
  int state = s_type;
  

  if (m_fname == NULL)
    {
      fhandle = stdin;
    }
  else 
    {
      /*
       * Never fails since checked before.
       */
      fhandle = fopen (m_fname, "rb");
    }

  while (state < s_done)
    {
      switch (state)
        {
        case s_type: 
          /* 
           * Look for type indicator 
           */
          while ((ch != (int)'P') && (ch != (int)'#')) 
            { 
              if ((ch = fgetc (fhandle)) == EOF)
                {
                  state = s_error;
                  break;
                }
            }
          
          if (uncomment (fhandle, ch) < 0)
            {
              state = s_error;
              break;
            }

          if (ch != (int)'P')
            {
              state = s_error;
              break;
            }
          
          /* 
           * get type, 5 or 6
           */
          if ((ftype = fgetc (fhandle)) == EOF)
            {
              state = s_error;
              break;
            }
          if (ftype != (int)'5')
            {
              state = s_error;
              break;
            }
          state = s_size;
          break;
  
        case s_size:
          /* 
           * Look for x size, y size, max grey level...
           */
          xsize = next (fhandle);       
          ysize = next (fhandle);
          maxg  = next (fhandle);
          /*
           * Do some consistency checks...
           */
          if ((xsize <= 0) || (ysize <= 0) || (maxg <= 0))
            {
              state = s_error;
              break;
            } 
          m_pixels->resize (ysize, xsize);
          state = s_read;
          break;
          
        case s_read:
          fp = -1l*(long)xsize*(long)ysize;
          /*
           * This does not help if the image is smaller than the header!
           */
          if (fseek (fhandle, fp, SEEK_END) < 0)
            {
              state = s_error;
              break;
            }
          /*
           * Now read row-wise...
           */
          /*
           * may be overwritten in loop!
           */
          state = s_done;
          for (int i = 0; i < ysize; i++)
            {
              if (fread (&m_pixels->array () [i*xsize], sizeof (pixel),
                         xsize, fhandle) != (size_t)xsize)
                {
                  state = s_error;
                  break;
                }
            }
          break;

        default: 
          break;
        }
    }

  if (fclose (fhandle) == EOF)
    {
      return -1;
    }

  return state == s_error? -2: 0;
}

static int 
next (FILE* fhandle)
{
  int tmp;
  int ch;

  do 
    { 
      if ((ch = fgetc (fhandle)) == EOF)
        {
          return ch;
        }
    } 
  while ((ch <= (int)' ') && (ch != (int)'#'));

  ch = uncomment (fhandle, ch);

  (void)ungetc ((char)ch, fhandle);

  if (fscanf (fhandle, "%d", &tmp) != 1) 
    {
      return EOF;
    }

  return tmp;
}

static int 
uncomment (FILE *fhandle, int ch)
{
  /*
   * Skip comments...
   */
  while ((ch == (int)'#')) 
    {
      while (ch != (int)'\n') 
        { 
          if ((ch = fgetc (fhandle)) == EOF)
            {
              return ch;
            }
        }
      while (ch <  (int)' ' ) 
        { 
          if ((ch = fgetc (fhandle)) == EOF)
            {
              return ch;
            }
        }
    }
  
  return ch;
}

