#ifndef INCLUDE_NAST_DEBUG_H
#define INCLUDE_NAST_DEBUG_H
//-----------------------------------------------------------------------------
//  NastDebug.h
//-----------------------------------------------------------------------------
//
//  Copyright (C) 1998 Technische Universitaet Muenchen, Germany
//                   written by Bernhard Brueck
//
//  This file is part of Nast++
//
//-----------------------------------------------------------------------------
//  CNastDebug stellt Funktionen zur Verfuegung um das Programm zu verlassen
//  und um die Contexte fuer die Ausgaben zu liefern.
//  Ausserdem werden in  NastDebug.h alle Macros definiert, 
//  die fuer eine Unterstuetzung beim Debuggen notwendig sind.
//  Die Einstellungen was verwendet werden soll stehen in NastConfig.h.
//
//  Macros:
//    NAST_ASSERT(expr)              falls !expr Abbruch 
//    NAST_ASSERT_DUMP(expr, msg)    falls !expr Abbruch und Ausgabe von msg
//    NAST_ASSERT_VALID(obj)         assertValid von obj aufrufen
//
//    NAST_WARNING(msg)              msg in den warningcontext ausgeben
//    NAST_ASSERT_WARNING(expr, msg) falls !expr Warnung ausgeben
//
//    NAST_DUMP_POSITION()           Position ins Logfile ausgeben
//    NAST_DUMP_MESSAGE(msg)         msg in den Logcontext ausgeben
//    NAST_DUMP_VARIABLE(var)        Variable in den Logcontext ausgeben
//
//    NAST_FATAL_ERROR(msg)          bricht das Programm mit einer Fehler-
//                                   meldung ab
//
//    NAST_BREAK()                   auf Benutzereingabe warten
//
//-----------------------------------------------------------------------------
//  Bei msg muss es sich dabei nicht um einen reinen String handeln,
//  sondern es kann auch eine Folge von << Operatoren sein. Das ermoglicht
//  Angaben in der Form:
//  NAST_ASSERT_DUMP( x > 10, "x = " << x << " ist zu klein");
//-----------------------------------------------------------------------------
//  Aenderungen:
//     

#include "NastConfig.h"
#include "NastDumpContext.h"
#include "NastVisualContext.h"

#include "NastVisualContextGnuplot.h"

//-----------------------------------------------------------------------------
//  CNastDebug hat die Aufgabe drei global verfuegbare DumpContexte zu
//  verwalten
//
//  dumpErrors() liefert einen DumpContext um Fehler auszugeben, die auch
//               zum Programmabbruch fuehren koennen
//               z.B. Ausgaben von ASSERT 
//
//  dumpWarnings() ist fuer Ausgaben zustaendig die evtl kritisch sein
//                 koennen, aber keinen Programmabruch erfordern
//
//  dumpLogfile()  ist fuer reine Statusinformationen
//
//  und eine Routinen fuer einen Programmabbruch zu bieten
//  mit den entsprechenden Routinen zum setzen der Dumpcontexte
//  kann jederzeit die gesamte Ausgabe auf neue Dumpcontexte gestzt werden
//-----------------------------------------------------------------------------

class CNastDebug
{
public:
    CNastDebug();
    ~CNastDebug();

    void dumpContextErrors( CNastDumpContext *pDC );
    void dumpContextWarnings( CNastDumpContext *pDC );
    void dumpContextLogfile ( CNastDumpContext *pDC );

    static CNastDumpContext& dumpContextErrors();
    static CNastDumpContext& dumpContextWarnings();
    static CNastDumpContext& dumpContextLogfile();

    static void fatalError( const char *szAssertion,
        		    const char *szFileName,
        		    int nLine,
        		    const char *szFunction,
        		    const char *szMessage);
    static void fatalExit();

private:
    static CNastDumpContext *m_pNastDumpErrors;
    static CNastDumpContext *m_pNastDumpWarnings;
    static CNastDumpContext *m_pNastDumpLogfile;
};

//----------------------------------------------------------------------------
//  Tests zur Laufzeit
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//  NAST_POSITION_IN_FILE liefert einen String mit der Position des Macros
//  im Projekt. Bei neueren GNU-Compilern kann auch die Funktion ermittelt 
//  werden, in der sich das Macro befindet. In diesem Fall liefert 
//  NAST_POSTITION_IN_FILE_PRETTY auch noch den Funktionsnamen.
//  anderen Macros benutzt.
//-----------------------------------------------------------------------------
// ab der Version gcc 2.4 kann die Funktion ermittelt werden in der 
// das NAST_ASSERT Macro expandiert wurde

#  define NAST_POSITION_IN_FILE         __FILE__ << ":" << __LINE__ << ": "

#if (!defined (__GNUC__) || __GNUC__ < 2 || __GNUC_MINOR__ < (defined (__cplusplus) ? 6 : 4))
#  define NAST_PRETTY_FUNCTION 0
#  define NAST_POSITION_IN_FILE_PRETTY  NAST_POSITION_IN_FILE
#else
#  define NAST_PRETTY_FUNCTION   __PRETTY_FUNCTION__
#  define NAST_POSITION_IN_FILE_PRETTY  NAST_POSITION_IN_FILE << ":" << __PRETTY_FUNCTION__ << ": "
#endif

//-----------------------------------------------------------------------------
//  Fuer abgeschaltete Macros wird als Ersatz NAST_EMPTY verwendet
//-----------------------------------------------------------------------------
#define NAST_EMPTY (void(0))

//-----------------------------------------------------------------------------
//  Falls NAST_USE_ASSERT definiert ist werden die beiden
//  Macros NAST_ASSERT und NAST_ASSERT_DUMP definiert.
//-----------------------------------------------------------------------------
#ifdef NAST_USE_ASSERT
#  define NAST_ASSERT(expr)                                                   \
       if(!(expr))                                                            \
           CNastDebug::fatalError( #expr,                                     \
        			   __FILE__,                                  \
        			   __LINE__,                                  \
        			   NAST_PRETTY_FUNCTION,                      \
        			   0 );                                
#  define NAST_ASSERT_DUMP(expr, szMessage)                                   \
       if(!(expr))                                                            \
       {                                                                      \
           CNastDebug::dumpContextErrors()                                    \
                  << "\n\nFolgendes Problem hat sich ergeben : \n"            \
                  << "\n" << szMessage << "\n\n";                             \
           CNastDebug::fatalError( #expr,                                     \
        			   __FILE__,                                  \
        			   __LINE__,                                  \
        			   NAST_PRETTY_FUNCTION,                      \
        			   0);                                        \
       }
#else
#  define NAST_ASSERT( expr )                 NAST_EMPTY
#  define NAST_ASSERT_DUMP( expr, szMessage ) NAST_EMPTY
#endif  //  NAST_USE_ASSERT


//-----------------------------------------------------------------------------
//  Wenn NAST_USE_ASSERT_INDEX definiert ist wird NAST_ASSERT_INDEX definiert
//  ansonsten wird ein Dummy eingefuegt.
//  Die Funktionalitaet von NAST_ASSERT_INDEX entspricht einem nornmalen 
//  NAST_ASSERT, es laesst sich aber davon getrennt an- und ausschalten.
//-----------------------------------------------------------------------------
#ifdef NAST_USE_ASSERT_INDEX
#  define NAST_ASSERT_INDEX(expr)                                             \
       if(!(expr)) CNastDebug::fatalError( #expr,                             \
          				   __FILE__,                          \
        				   __LINE__,                          \
        				   NAST_PRETTY_FUNCTION,              \
        				   "fehlerhafter Indexzugriff !");
#else
#  define NAST_ASSERT_INDEX( expr ) NAST_EMPTY
#endif  //  NAST_USE_ASSERT_INDEX


//-----------------------------------------------------------------------------
//  Wenn NAST_USE_ASSERT_VALID definiert ist wird NAST_ASSERT_VALID definiert
//  ansonsten wird ein Dummy eingefuegt.
//  NAST_ASSERT_VALID(obj) ruft die assertValid Funktion von obj auf,
//  nachdem ueberprueft wurde ob es sich dabei nicht um einen Null-Pointer
//  handelt. 
//-----------------------------------------------------------------------------
#ifdef  NAST_USE_ASSERT_VALID
#  define NAST_ASSERT_VALID(x)                                                \
       if( (x) != 0 )                                                         \
           (x)->assertValid();                                                \
       else                                                                   \
           CNastDebug::fatalError( "this == 0",                               \
        		           __FILE__,                                  \
        		           __LINE__,                                  \
        		           NAST_PRETTY_FUNCTION,                      \
        		           "Nullpointer !" );
#else
#  define NAST_ASSERT_VALID(x)  NAST_EMPTY
#endif  //  NAST_USE_ASSERT_VALID


//-----------------------------------------------------------------------------
//  falls NAST_USE_WARNING definiert ist NAST_WARNING und 
//  NAST_ASSERT_WARNING definieren
//-----------------------------------------------------------------------------
#ifdef NAST_USE_WARNING
#  define NAST_WARNING( szMessage )                                           \
    CNastDebug::dumpContextWarnings() << NAST_POSITION_IN_FILE_PRETTY         \
                                      << " Warning: "                         \
                                      << szMessage << "\n"                    

#  define NAST_ASSERT_WARNING( expr, szMessage)                               \
    if( !(expr) )                                                             \
    {                                                                         \
        CNastDebug::dumpContextWarnings() << NAST_POSITION_IN_FILE_PRETTY     \
                                          << " warning: "                     \
                                           << szMessage << "\n";              \
    }
#else
#  define NAST_WARNING( szMessage )              NAST_EMPTY
#  define NAST_ASSERT_WARNING( expr, szMessage)  NAST_EMPTY
#endif // NAST_USE_WARNINGS


//-----------------------------------------------------------------------------
//  falls NAST_USE_DUMP_POSITION definiert ist NAST_DUMP_POSITION
//  definieren. Die Ausgabe der Meldungen erfolgt ins Logfile. 
//-----------------------------------------------------------------------------
#ifdef NAST_USE_DUMP_POSITION
#  define NAST_DUMP_POSITION()                                                \
       CNastDebug::dumpContextLogfile() << NAST_POSITION_IN_FILE_PRETTY       \
                                        << "NAST_DUMP_POSITION() \n"
#else
#  define NAST_DUMP_POSITION() NAST_EMPTY
#endif  //  NAST_USE_DUMP_POSITION


//-----------------------------------------------------------------------------
//  falls NAST_USE_DUMP_MESSAGE definiert ist NAST_DUMP_POSITION
//  definieren. Die Ausgabe der Meldungen erfolgt ins Logfile. 
//-----------------------------------------------------------------------------
#ifdef NAST_USE_DUMP_MESSAGE
#  define NAST_DUMP_MESSAGE(msg)                                              \
    CNastDebug::dumpContextLogfile() << NAST_POSITION_IN_FILE_PRETTY          \
                                     << msg <<  "\n"
#else
#  define NAST_DUMP_MESSAGE(msg) NAST_EMPTY
#endif  // NAST_USE_DUMP_MESSAGE


//-----------------------------------------------------------------------------
//  falls NAST_USE_DUMP_VARIABLE definiert ist NAST_DUMP_VARIABLE
//  definieren. Die Ausgabe der Variablen erfolgt ins Logfile. 
//-----------------------------------------------------------------------------
#ifdef NAST_USE_DUMP_VARIABLE
#  define NAST_DUMP_VARIABLE(var)  \
       CNastDebug::dumpContextLogfile() << NAST_POSITION_IN_FILE              \
                                        << #var << " = " << var << "\n"
#else
#  define NAST_DUMP_VARIABLE(var) NAST_EMPTY
#endif  // NAST_USE_DUMP_VARIABLE


//-----------------------------------------------------------------------------
//  falls NAST_USE_DUMP_VISUAL definiert ist
//  NAST_DUMP_VISUAL              fuer CNastObjekte
//  NAST_DUMP_VISUAL_INTERVALL    fuer doubles
//  definieren. Die Ausgabe erfolge in den aktuellen VisualContext
//-----------------------------------------------------------------------------
#ifdef NAST_USE_DUMP_VISUAL
#  define NAST_DUMP_VISUAL_INTERVALL( y, nSize )                              \
   {                                                                         \
        NAST_ASSERT( nSize > 0 );                                             \
        static int nCalled = 0;                                               \
        static CNastArray<double> arrX;\
        static CNastArray<double> arrY;                                 \
        static NAST_DUMP_VISUAL_CONTEXT visContext(#y);                       \
        if( nCalled < nSize)                                                  \
        {                                                                     \
            arrX.add(nCalled);                                                \
            arrY.add(y);                                                      \
        }                                                                     \
        else                                                                  \
        {                                                                     \
            for( int i = 0; i < nSize-1; i++)                                 \
            {                                                                 \
                arrX[i] = arrX[i+1];                                          \
                arrY[i] = arrY[i+1];                                          \
            }                                                                 \
            arrX[nSize-1] = nCalled;                                          \
            arrY[nSize-1] = y;                                                \
        }                                                                     \
        visContext.show(arrX, arrY);                                          \
        nCalled++;                                                            \
    }
#  define NAST_DUMP_VISUAL(obj)                                               \
    {                                                                         \
        static NAST_DUMP_VISUAL_CONTEXT visContext(__FILE__"  "#obj);         \
        visContext.show(obj);                                                 \
    }
#else
#  define NAST_DUMP_VISUAL_INTERVALL( y, nCount ) NAST_EMPTY
#  define NAST_DUMP_VISUAL(obj)                   NAST_EMPTY
#endif


//-----------------------------------------------------------------------------
//  Programmabbruch mit Fehlermeldung
//-----------------------------------------------------------------------------

#define NAST_FATAL_ERROR(szString)                                            \
    CNastDebug::fatalError( 0,                                                \
        		    __FILE__,                                         \
        		    __LINE__,                                         \
        		    NAST_PRETTY_FUNCTION,                             \
        		    szString ) 

//-----------------------------------------------------------------------------
//  Warten auf eine Eingabe durch den Benutzer
//-----------------------------------------------------------------------------
#ifdef NAST_USE_BREAK
#  include <iostream.h>
#  include <stdlib.h>
#  define NAST_BREAK()                                                         \
   {                                                                          \
       static int nCount = 1;                                                 \
       if( --nCount == 0)                                                     \
       {                                                                      \
           char szBuff[10];                                                   \
           cout << NAST_POSITION_IN_FILE << "(-2 BREAK, -1 RUN, n Steps)\n";  \
           cout.flush();                                                      \
           cin.getline(szBuff,10);                                            \
           int i = atoi(szBuff);                                              \
           if( i == 0) i = 1;                                                 \
           if( i == -2) NAST_FATAL_ERROR("Break" );                           \
           nCount = i;                                                        \
       }                                                                      \
   }                                                                          
#else
#  define NAST_BREAK() NAST_EMPTY
#endif

#endif // INCLUDE_NAST_DEBUG_H

