/****************************************************************************
 * This file was derived from the CNCL library by the Pablo research group.
 *
 * The complete CNCL library is available at:
 *       http://www.comnets.rwth-aachen.de/doc/cncl.html
 *
 * The CNCL copyright notice is included here:
 *
 * |_|_|_  |_|_    |_    |_|_|_  |_               C O M M U N I C A T I O N
 *|_       |_  |_  |_  |_        |_                         N E T W O R K S
 *|_       |_  |_  |_  |_        |_                               C L A S S
 * |_|_|_  |_    |_|_    |_|_|_  |_|_|_|_                     L I B R A R Y
 *
 *
 *
 * Copyright (C) 1992-1998   Communication Networks
 *                           Aachen University of Technology
 *                           D-52056 Aachen
 *                           Germany
 *                           Email: cncl-adm@comnets.rwth-aachen.de
 *
 *
 * This library is free  software; you can  redistribute it and/or modify
 * it  under   the terms  of the GNU   Library  General Public License as
 * published by the  Free Software Foundation;  either  version 2 of  the
 * License, or  (at  your option)  any  later  version.  This  library 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 Library General Public
 * Library General Public License along with  this library; if not, write
 * to the Free Software Foundation,  675  Mass Ave, Cambridge, MA  02139,
 * USA.
 ****************************************************************************/

//  -*- C++ -*-
/*****************************************************************************

 *****************************************************************************/

#include "FlFuzzyLib.h"

#include <iostream>
#include <strstream>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <new.h>

using std::endl;
using std::ends;
using std::cerr;
using std::ostream;
using std::ostrstream;


/*
 * Error handling:
 *   Default is fatal errors.
 */
FuzzyLib::ErrorType FuzzyLib::error_flag = FuzzyLib::err_fatal;

// Set/get error handler setting
FuzzyLib::ErrorType FuzzyLib::get_error() 
{
    return error_flag;
}

FuzzyLib::ErrorType FuzzyLib::set_error(ErrorType new_error)
{
    ErrorType old_error = error_flag;
    error_flag = new_error==err_default ? err_fatal : new_error;
    return old_error;
}

// Set function called for terminating program
void FuzzyLib::set_exit_handler(void (*func)())
{
    exit_handler = func;
}



/*
 * Default function for error exits
 */

void FuzzyLib::default_exit_handler()
{
    cerr << "FuzzyLib: program "
	 << ( error_flag == err_abort ? "aborted" : "terminated" )
	 << endl;

    if(error_flag == err_abort)
	abort();
    else
	exit(1);
}

/*
 * FuzzyLib exit handler
 */

void (* FuzzyLib::exit_handler)() = default_exit_handler;



/*
 * Error handler:
 *   Print out messages on cerr
 */

void FuzzyLib::error(ErrorType err,
		 const char *msg1, const char *msg2, const char *msg3,
		 const char *msg4, const char *msg5, const char *msg6)
{
    if(err == err_default)
	err = error_flag;
    
    if(err != err_ignore)
    {
	if(msg1 == NIL)
	{
	    switch(err)
	    {
	    case err_warning:
		cerr << "FuzzyLib warning: ";	break;
	    case err_info:
		cerr << "FuzzyLib: ";		break;
	    default:
		cerr << "FuzzyLib error: ";		break;
	    }
	}
	else
	    cerr << msg1;
	if(msg2)
	    cerr << msg2;
	if(msg3)
	    cerr << msg3;
	if(msg4)
	    cerr << msg4;
	if(msg5)
	    cerr << msg5;
	if(msg6)
	    cerr << msg6;
	if(str_stream)
	{
	    cerr << str_stream->str();
	    delete str_stream;
	    delete [] str_buf;
	    str_stream = NIL;
	    str_buf = NIL;
	}
	cerr << endl;
    }
    
    if(err==err_fatal || err==err_abort)
	exit_handler();
}



/*
 * Error message with default handling
 */

void FuzzyLib::error(const char *msg1, const char *msg2, const char *msg3,
		 const char *msg4, const char *msg5, const char *msg6)
{
    error(err_default, msg1, msg2, msg3, msg4, msg5, msg6);
}



/*
 * Error message with fatal handling
 */

void FuzzyLib::fatal(const char *msg1, const char *msg2, const char *msg3,
		 const char *msg4, const char *msg5, const char *msg6)
{
    error(err_fatal, msg1, msg2, msg3, msg4, msg5, msg6);
}



/*
 * Error message with warning handling
 */

void FuzzyLib::warning(const char *msg1, const char *msg2, const char *msg3,
		   const char *msg4, const char *msg5, const char *msg6)
{
    error(err_warning, msg1, msg2, msg3, msg4, msg5, msg6);
}



/*
 * (Non-)Error message with info handling
 */

void FuzzyLib::info(const char *msg1, const char *msg2, const char *msg3,
		const char *msg4, const char *msg5, const char *msg6)
{
    error(err_info, msg1, msg2, msg3, msg4, msg5, msg6);
}



/*
 * strstream for additional output with error(), ...
 */
char       *FuzzyLib::str_buf = NIL;
ostrstream *FuzzyLib::str_stream = NIL;


ostream& FuzzyLib::msg()
{
    if(str_buf)
	delete [] str_buf;
    if(str_stream)
	delete str_stream;
    str_buf    = new char [STR_BUF_SIZE];
    // This results in a proper EOS '\0' even if `ends' isn't
    // pushed into the stream.
    memset(str_buf, 0, STR_BUF_SIZE);
    str_stream = new ostrstream(str_buf, STR_BUF_SIZE);
    
    return *str_stream;
}

void FuzzyLib::clear()
{
    delete [] str_buf;
    str_buf = NIL;
    delete str_stream;
    str_stream = NIL;
}

#ifndef FuzzyLib_NO_MATHERR			// matherr() may not be available

/*
 * matherr() handling
 */
extern "C"
#ifdef LIBGPP_24			// libg++ <=2.4 uses libm_execption
int matherr(struct libm_exception *x)
#elif __LINUX__
int matherr(struct __exception *x)
#elif __SUNPRO_CC && EXCP_REDFN_TO_MATHEXCP
int matherr(struct math_exception *x)
#else
int matherr(struct exception *x)
#endif
{
    char buffer[128];

    ostrstream strm(buffer, sizeof(buffer));
    const char *err;
    int args = 1;
    
    if(!strcmp(x->name, "atan2")  ||  !strcmp(x->name, "copysign")  ||
       !strcmp(x->name, "drem")   ||  !strcmp(x->name, "fmod")      ||
       !strcmp(x->name, "frexp")  ||  !strcmp(x->name, "hypot")     ||
       !strcmp(x->name, "ldexp")  ||  !strcmp(x->name, "modf")      ||
       !strcmp(x->name, "pow")    ||  !strcmp(x->name, "scalb")     ||
       !strcmp(x->name, "yn")                                         )
	args = 2;
    
    switch(x->type)
    {
    case DOMAIN:
	err = "DOMAIN";
	break;
    case SING:
	err = "SING";
	break;
    case OVERFLOW:
	err = "OVERFLOW";
	break;
    case UNDERFLOW:
	if(!strcmp(x->name, "exp"))	// exp() underflow = 0 is o.k.
	    return 0;
	err = "UNDERFLOW";
	break;
#ifdef TLOSS
    case TLOSS:
	err = "TLOSS";
	break;
#endif
#ifdef PLOSS
    case PLOSS:
	err = "PLOSS";
	break;
#endif
    default:
	err = "unknown";
	break;
    }

    if(args == 2)
	strm << x->name << "(" << x->arg1 << ", " << x->arg2 << "): "
	     << err << " error" << ends;
    else
	strm << x->name << "(" << x->arg1 << "): "
	     << err << " error" << ends;

    FuzzyLib::error(NIL, "matherr: ", buffer);
    
    return 1;
}

#endif /**!FuzzyLib_NO_MATHERR**/



/*
 * new handler called when memory is exhausted
 */
void FuzzyLib::new_handler()
{
    fatal(NIL, "memory exhausted");
    /**NOT REACHED**/
}



/*
 * FuzzyLib initialization, called once
 */

Fl_bool FuzzyLib::initialized = FALSE;

int FuzzyLib::verbose = 0;

void FuzzyLib::initialize()
{
  char* p;
    /* Install new handler */
  set_new_handler(FuzzyLib::new_handler);
    
  initialized = TRUE;
  
  if (( p = getenv("MAESTRO_DEBUG")) == NULL) {
    verbose = 0;
  } else {
    verbose = atoi(p);
  }


}



/*
 * Global FuzzyLib object to enforce initialization even if no FuzzyLib object
 * is created elsewhere.
 */
static FuzzyLib dummy;
