//
// markIV: software to download CCD data from a MarkIV ISA card and parse
//         two files containing FITS style header information, with the intent
//         of creating two FITS files containing appropriate information
// Copyright (C) 2001  Robert S. Creager
//
// E-mail: robert@carolinecreager.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// $Name: Downfits-1_1 $
//
// $Log: parse.cpp,v $
// Revision 1.0  2002/01/06 18:12:19  robc
// First CVS entry
//

#include <stdlib.h>
#include <stdio.h>
#include <iostream.h>
#include <fitsio.h>
#include "tom.h"

// List of reserved keywords to be filled out within the program using
// below data
SPStringList reserved;

// Defining the reserved words which will be ignored in the input files
char* char_reserved[ ] = {
   "SIMPLE",
   "BITPIX",
   "NAXIS",
   "NAXIS1",
   "NAXIS2",
   "EXTEND",
   "BZERO",
   "BSCALE",
//   "DATE",
// Now in the plate_? file names   "FILTER",
   "END"
   };

// Some strings we'll be using for parsing the input files
// Note that unlike 'real' regular expressions, the patterns inside []
// are order dependent with Regexp...
SPString str_space = "[ \t]*";
SPString str_all = "(.*)";
SPString str_key = "^([/_a-z1-9-]+)" + str_space + "=";
SPString str_string = str_space + "'(.*)'" + str_all ;
SPString str_digit = str_space + "([E0-9.-]+)" + str_all;
SPString str_comment = str_space + "/" + str_space + "(.*)" + str_space;
SPString str_comment_key = "^COMMENT" + str_space + "(.*)";

// A couple of pre-compiled regexp's we'll use
Regexp reg_keyDigit( str_key + str_digit, Regexp::nocase );
Regexp reg_keyString( str_key + str_string, Regexp::nocase );
Regexp reg_comment( str_comment_key );

void
parse_main( fitsfile* fptr[ MAX_CCD ], char plate_id[ MAX_CCD ], int num_ccds )
   {
   // Now, we'll start the section which reads in the parameter files
   // and fills out the fits header
   SPString plateFN[ MAX_CCD ], constantFN;
   ifstream plate[ MAX_CCD ];
   int itemp;


   // Simple do while( 0 ) loop to allow for nice exits in case of a problem
   do
      {
      for ( itemp = 0; itemp < num_ccds; ++itemp )
         {
         plateFN[ itemp ] = "plate_";
         plateFN[ itemp ] += tolower( plate_id[ itemp ] );
         plate[ itemp ].open( plateFN[ itemp ] );
         if ( !plate[ itemp ] )
            {
            cerr << "Error: cannot open " << plateFN[ itemp ] << " for reading" << endl;
            itemp = 99;
            break;
            }
         }
      if ( itemp == 99 )
         {
         break;
         }

      // Try to open the files for parsing
      constantFN = "constant";
      ifstream constant( constantFN );
      if ( !constant )
         {
         cerr << "Error: cannot open " << constantFN << " for reading" << endl;
         break;
         }

      // Lets build up the list of reserved words so someone doesn't accidently
      // change the 'default' ones
      for ( int temp = 0;
            temp < sizeof( char_reserved ) / sizeof( char_reserved[ 0 ] ); ++temp )
         {
         reserved.push( char_reserved[ temp ] );
         }

      // Now, go add the keywords found in the input files into the fits file
      for ( itemp = 0; itemp < num_ccds; ++itemp )
         {
         add_keywords( fptr[ itemp ], constant );
         add_keywords( fptr[ itemp ], plate[ itemp ] );
         plate[ itemp ].close( );
         // Well, for some reason, either seekg doesn't work, or I don't know
         // how to use it, so we close and re-open the files to get us
         // started at the beginning again...
         constant.close( );
         constant.open( constantFN );
         }
      constant.close( );
      }
   while( 0 );
   }

void
add_keywords( fitsfile* fptr, ifstream& file )
   {
   SPString fline;
   SPStringList used;
   int matchCount;
   double value;
   int itemp;

   // Now, go read the entire file, working with lines that interest us
   while( file >> fline )
      {
      // Be entirely too tolerant of errors, by re-setting the status
      // every time
      int status = 0;

      // Note that we want to declare these values within the loop, as
      // they will be deleted and re-instantiated each time through, preventing
      // duplicate comments - I actually determined this before I had the
      // problem - amazing...
      SPStringList pc;
      SPString commentString;
      SPStringList matches;

      // This match is a key/digit set
      if ( (matchCount = fline.m( reg_keyDigit, matches )) )
         {
         // Check to make sure this keyword hasn't been used before
         if (  (used.grep( "^" + matches[ 1 ] + "$" ) ) )
            {
            cout << "Warning: " << matches[ 1 ] << " has multiple entries.";
            cout << "  Using latest value" << endl;
            }
         // and that it's not a reserved word
         else if ( reserved.grep( "^" + matches[ 1 ] + "$" ) )
            {
            cout << "Warning: " << matches[ 1 ] << " is a reserved word.";
            cout << "  Not entering." << endl;
            continue;
            }
         // convert the string into a double value
         value = atof( matches[ 2 ] );
         // If we have end-of-line-data, look for a comment to use
         if ( matchCount == 4 )
            {
            if ( matches[ 3 ].m( str_comment, pc ) )
               {
               commentString = pc[ 1 ];
               }
            }
         // Write out the key/value pair we found
         fits_update_key( fptr, TDOUBLE, matches[ 1 ].c_str( ), &value,
                         commentString.c_str( ), &status );
         fits_report_error( stderr, status );
         // Save the keyword we just used to make sure we don't double up
         used.push( matches[ 1 ] );
         }
      // This is a key/string set
      else if ( (matchCount = fline.m( reg_keyString, matches ) ) )
         {
         // Check to make sure this keyword hasn't been used before
         if (  (used.grep( "^" + matches[ 1 ] + "$" ) ) )
            {
            cout << "Warning: " << matches[ 1 ] << " has multiple entries.";
            cout << "  Using latest value" << endl;
            }
         // and that it's not a reserved word
         else if ( reserved.grep( "^" + matches[ 1 ] + "$" ) )
            {
            cout << "Warning: " << matches[ 1 ] << " is a reserved word.";
            cout << "  Not entering." << endl;
            continue;
            }
         // Trimming the leading spaces of the string - just 'cause
         matches[ 2 ].s( "^ ", ""  );
         // Trimming the trailing spaces of the string - just 'cause
         matches[ 2 ].s( str_space + "$", ""  );
         // If we have end-of-line-data, look for a comment to use
         if ( matchCount == 4 )
            {
            if ( matches[ 3 ].m( str_comment, pc ) )
               {
               commentString = pc[ 1 ];
               }
            }
         // Write out the key/string pair we found
         fits_update_key( fptr, TSTRING,
                          matches[ 1 ].c_str( ), matches[ 2 ].c_str( ),
                          commentString.c_str( ), &status );
         fits_report_error( stderr, status );
         // Save the keyword we just used to make sure we don't double up
         used.push( matches[ 1 ] );
         }
      // This is a COMMENT key
      else if ( (matchCount = fline.m( reg_comment, matches ) ) )
         {
         fits_write_comment( fptr, matches[ 1 ].c_str( ), &status );
         fits_report_error( stderr, status );
         }

      }

   return;
   }
