/*  lil-gp Genetic Programming System, version 1.0, 11 July 1995
 *  Copyright (C) 1995  Michigan State University
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of version 2 of the GNU General Public License as
 *  published by the Free Software Foundation.
 * 
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *  
 *  Douglas Zongker       (zongker@isl.cps.msu.edu)
 *  Dr. Bill Punch        (punch@isl.cps.msu.edu)
 *
 *  Computer Science Department
 *  A-714 Wells Hall
 *  Michigan State University
 *  East Lansing, Michigan  48824
 *  USA
 *  
 */

#include <lilgp.h>

char buffer[MAXMESSAGELENGTH];

typedef struct
{
     int id;
     char *ext;
     int reset;
     char *mode;
     int autoflush;
     
     FILE *f;
     int valid;

     char *buffer;
} output;

output streams[MAXOUTPUTSTREAMS] =
     { { OUT_SYS, ".sys", 0, "w", 1, NULL, 0 },
       { OUT_GEN, ".gen", 0, "w", 0, NULL, 0 },
       { OUT_PRG, ".prg", 0, "w", 0, NULL, 0 },
       { OUT_STT, ".stt", 0, "w", 0, NULL, 0 },
       { OUT_BST, ".bst", 1, "w", 0, NULL, 0 },
       { OUT_HIS, ".his", 0, "w", 0, NULL, 0 } };
     
int output_stream_count = SYSOUTPUTSTREAMS;
int toolate = 0;
char *global_basename = NULL;
int detail_level = DEFDETAILLEVEL;

char error_type[3][20] = { "WARNING: ", "ERROR: ", "FATAL ERROR: " };

extern int quietmode;

/* create_output_stream()
 *
 * make a new entry in the outputstream table.
 */

int create_output_stream ( int id, char *ext, int reset, char *mode,
                          int autoflush )
{
     int i;

     /* can't create a new stream if they've already been opened. */
     if ( toolate )
          return OUTPUT_TOOLATE;

     /* is the stream table full? */
     if ( output_stream_count >= MAXOUTPUTSTREAMS )
          return OUTPUT_TOOMANY;

     /* is the id or extension the same as an existing stream? */
     for ( i = 0; i < output_stream_count; ++i )
     {
          if ( id == streams[i].id )
               return OUTPUT_DUP_ID;
          if ( strcmp ( ext, streams[i].ext ) == 0 )
               return OUTPUT_DUP_EXT;
     }

     /** save stuff in the table. **/
     
     streams[output_stream_count].id = id;
     streams[output_stream_count].ext = (char *)malloc ( strlen(ext)+1 );
     strcpy ( streams[output_stream_count].ext, ext );
     streams[output_stream_count].reset = reset;
     streams[output_stream_count].mode = (char *)malloc ( strlen(mode)+1 );
     strcpy ( streams[output_stream_count].mode, mode );
     streams[output_stream_count].autoflush = autoflush;
     
     streams[output_stream_count].f = NULL;
     streams[output_stream_count].valid = 0;
     ++output_stream_count;

     return OUTPUT_OK;
     
}

/* initialize_output_streams()
 *
 * initialize the output streams.  until they are opened, anything printed
 * to one (via oputs() or oprintf()) is saved in memory.
 */

void initialize_output_streams ( void )
{
     int i;
     
     toolate = 1;
     
     for ( i = 0; i < output_stream_count; ++i )
	  streams[i].buffer = NULL;

}

/* open_output_streams()
 *
 * open files associated with each output stream.  dump anything buffered
 * in memory to the file.
 */

void open_output_streams ( void )
{
     int i;
     char *fn = NULL;
     char *basename;

     basename = get_parameter ( "output.basename" );
     
     fn = (char *)MALLOC ( strlen(basename)+50 );

     for ( i = 0; i < output_stream_count; ++i )
     {
          strcpy ( fn, basename );
          strcat ( fn, streams[i].ext );
          streams[i].f = fopen ( fn, streams[i].mode );
          if ( streams[i].f == NULL )
          {
	       error ( E_ERROR, "can't open output file \"%s\".", fn );
          }
          else
          {
               streams[i].valid = 1;
	       if ( streams[i].buffer )
		    fputs ( streams[i].buffer, streams[i].f );
	       FREE ( streams[i].buffer );
	       streams[i].buffer = NULL;
          }
     }

     global_basename = (char *)malloc ( strlen(basename)+1 );
     strcpy ( global_basename, basename );

     set_detail_level ( atoi ( get_parameter ( "output.detail" ) ) );
     if (fn)
         FREE(fn);
}

/* oputs()
 *
 * prints a string to an output stream.
 */

void oputs ( int streamid, int detail, const char *string )
{
     int i, j;

     if ( streamid == OUT_SYS && !quietmode )
     {
          printf ( string );
          fflush ( stdout );
     }

     if ( detail_level < detail )
          return;
     
     for ( i = 0; i < output_stream_count; ++i )
     {
          if ( streamid == streams[i].id )
          {
               if ( streams[i].valid )
               {
                    fputs ( string, streams[i].f );
                    if ( streams[i].autoflush )
                         fflush ( streams[i].f );
                    break;
               }
	       else
	       {
		    if ( streams[i].buffer )
		    {
			 j = strlen ( streams[i].buffer ) +
			      strlen ( string ) + 1;
			 streams[i].buffer = (char *)REALLOC ( streams[i].buffer, j );
			 strcat ( streams[i].buffer, string );
		    }
		    else
		    {
			 streams[i].buffer = (char *)MALLOC ( strlen ( string ) + 1 );
			 strcpy ( streams[i].buffer, string );
		    }
	       }
          }
     }
}

/* oprintf()
 *
 * prints a formatted string to an output stream.
 */

void oprintf ( int streamid, int detail, const char *format, ... )
{
     va_list ap;

     if ( detail_level < detail )
          return;
     
     va_start ( ap, format );
     vsprintf ( buffer, format, ap );
     va_end ( ap );

     oputs ( streamid, detail, buffer );
}

/* output_filehandle()
 *
 * returns the filehandle associated with a given stream.
 */

FILE *output_filehandle ( int streamid )
{
     int i;
     for ( i = 0; i < output_stream_count; ++i )
          if ( streamid == streams[i].id )
               if ( streams[i].valid )
                    return ( streams[i].f );
     return NULL;
}

/* output_stream_close()
 *
 * closes an output stream, if it has been marked as resettable.
 */

void output_stream_close ( int streamid )
{
     int i;
     for ( i = 0; i < output_stream_count; ++i )
          if ( streamid == streams[i].id )
               if ( streams[i].reset )
                    if ( streams[i].valid )
                    {
                         fclose ( streams[i].f );
                         streams[i].valid = 0;
                         break;
                    }
}

/* output_stream_open()
 *
 * reopens a resettable output stream.
 */

void output_stream_open ( int streamid )
{
     char *fn = NULL;
     int i;
     
     for ( i = 0; i < output_stream_count; ++i )
          if ( streamid == streams[i].id )
               if ( streams[i].reset )
                    if ( !streams[i].valid )
                    {
                         fn = (char *)MALLOC ( strlen(global_basename)+50 );
                         
                         strcpy ( fn, global_basename );
                         strcat ( fn, streams[i].ext );
                         streams[i].f = fopen ( fn, streams[i].mode );
                         if ( streams[i].f == NULL )
                         {
                              /* an error. */
                         }
                         else
                              streams[i].valid = 1;
                    }
     if (fn)
        FREE(fn);
}

/* output_stream_flush()
 *
 * flushes all the resettable output streams.
 */

void output_stream_flush ( int streamid )
{
     int i;
     
     for ( i = 0; i < output_stream_count; ++i )
          if ( streamid == streams[i].id )
               if ( streams[i].reset )
                    if ( streams[i].valid )
                         fflush ( streams[i].f );
}

/* close_output_streams()
 *
 * closes all the output streams.
 */

void close_output_streams ( void )
{
     int i;

     for ( i = 0; i < output_stream_count; ++i )
     {
          if ( streams[i].valid )
          {
               fclose ( streams[i].f );
               streams[i].valid = 0;
          }
     }

     for ( i = SYSOUTPUTSTREAMS; i < MAXOUTPUTSTREAMS; ++i )
     {
          free ( streams[i].ext );
          free ( streams[i].mode );
     }
     
     free ( global_basename );
}

/* flush_output_streams()
 *
 * flushes all the output streams.
 */

void flush_output_streams ( void )
{
     int i;
     for ( i = 0; i < output_stream_count; ++i )
          if ( streams[i].valid )
               fflush ( streams[i].f );
}

/* error()
 *
 * prints an error message to the system output stream, with a given
 * level of severity.  If the error is fatal, then the error is also
 * printed to stderr and the program exits with status 1.
 */

void error ( int severity, char *format, ... )
{
     va_list ap;

     va_start ( ap, format );
     vsprintf ( buffer, format, ap );
     va_end ( ap );
     strcat ( buffer, "\n" );

     oputs ( OUT_SYS, 0, error_type[severity] );
     oputs ( OUT_SYS, 0, buffer );

     if ( severity == E_FATAL_ERROR )
     {
          fprintf ( stderr, "exiting due to fatal error.\n" );
          exit(1);
     }

}

/* set_detail_level()
 *
 * sets the detail level to a given value.
 */

void set_detail_level ( int newlevel )
{
     if ( newlevel >= MINDETAILLEVEL &&
         newlevel <= MAXDETAILLEVEL )
          detail_level = newlevel;
}

/* test_detail_level()
 *
 * returns 1 if the argument is less than or equal to the current
 * detail level.
 */

int test_detail_level ( int detail )
{
     if ( detail_level < detail )
          return 0;
     return 1;
}
