//-----------------------------------------------------------------------------
// NastParameterTaggedFile.cpp
//-----------------------------------------------------------------------------

#include "NastParameterContextTaggedFile.h"
#include "NastDebug.h"

#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//-------------------------------------------------------------------------
//  maximale Zeilenlaenge 
//-------------------------------------------------------------------------
static const int nMaxLineLength = 1024;

//-------------------------------------------------------------------------
//                         Hilfsfunktionen
//-------------------------------------------------------------------------
static void stripLine( char *szLine );


#define CHAR_IGNORE1 '\t'
#define CHAR_IGNORE2 ' '
#define CHAR_COMMENT ';'
#define CHAR_QUOT    34
#define CHAR_SECTION_SEPERATOR '.'
#define CHAR_ASSIGN '='


//
//  entfernt Leerzeichen, Tabs und Kommentare aus dem String
//
static void stripLine( char *szLine )
{
    char *pch = szLine;		// pch auf Stringanfang setzen
    bool bQuot = false;		// wird true bei ein Anfuehrungszeichen

    while(1)
    {
        *szLine = *pch;
        if( *szLine == 0 )
            break;
        
        if( *pch == CHAR_COMMENT  && !bQuot ) //  Kommentar ?
        {
            *pch = 0;
            continue;
        }

        //  Anfuehrungszeichen und Whitespace ?
        if( (*pch == CHAR_IGNORE1  ||  *pch == CHAR_IGNORE2)  && !bQuot )
        {
            ++pch; 
            continue;
        }
        
        if( *pch == CHAR_QUOT )	//  Anfuehrungszeichen ?
        {
            bQuot = !bQuot;
            ++pch;
            continue;
        }

        ++pch;
        ++szLine;
    }
}


// sucht die Zeile mit der Zuweisung fuer szVariable im Bereich szSection
// und extrahiert den Wert in szValue
const char* CNastParameterContextTaggedFile::searchValue( const char *szName ) const
{
    for( int i = 0; i < m_arrTags.size(); i++)
    {
        if( strcmp( m_arrTags[i], szName ) == 0)
            return m_arrValues[i];
    }

    return 0;
}

//-----------------------------------------------------------------------------
//                    Konstruktor + Destruktor
//-----------------------------------------------------------------------------

CNastParameterContextTaggedFile::CNastParameterContextTaggedFile()
{
}

CNastParameterContextTaggedFile::CNastParameterContextTaggedFile( istream &streamIn )
{
    char szLine[nMaxLineLength]; 
    int nCountLines = 0;

    NAST_ASSERT_DUMP( streamIn.good() , "defekten Inputstream uebergeben" );
    streamIn.seekg(0);		// stream auf den Anfang zuruecksetzen
    
    while( ! streamIn.eof() )	// solange nicht Ende erreicht ist
    {
        ++nCountLines;
        streamIn.getline( szLine, nMaxLineLength ); // Zeile lesen
        stripLine(szLine);	// Leerzeichen, Kommentare usw, entfernen

        if( strlen(szLine) > 0 ) // keine Leerzeile ?
        {
            char *pAssign = strchr( szLine, CHAR_ASSIGN );  // '=' suchen

            if( pAssign )	//  '=' gefunden ?
            {
        	*pAssign = 0;	// Stringende beim '=' setzen
        	++pAssign;	// pAssign auf das nach dem = zeigen lassen
        	
        	int lengthLeft = strlen( szLine );
        	int lengthRight= strlen( pAssign );

        	// steht links und rechts vom '=' etwas ?
        	if( lengthLeft != 0 && lengthRight != 0) 
        	{
        	    char *szLeft  = new char[ lengthLeft+1  ];
        	    char *szRight = new char[ lengthRight+1 ];
        	    strcpy( szLeft, szLine );
        	    strcpy( szRight, pAssign );

        	    m_arrTags.add( szLeft );
        	    m_arrValues.add( szRight );
        	}
        	else
        	{
        	    // fehlerhafte Zeile
        	    char szError[100];
        	    sprintf( szError, "Fehler beim einlesen in Zeile: %d", nCountLines );
        	    NAST_FATAL_ERROR( szError );
        	}
            }
        }
    }
}

CNastParameterContextTaggedFile::CNastParameterContextTaggedFile( const CNastParameterContextTaggedFile & other)
    :CNastParameterContext( other )
{
    NAST_ASSERT_VALID( &other );

    //  die beiden Arrays muessen kopiert werden
    m_arrTags.setSize( other.m_arrTags.size() );
    m_arrValues.setSize( other.m_arrValues.size() );

    int i;
    for( i = 0; i < m_arrTags.size(); i++)
    {
        m_arrTags[i] = new char[ strlen( other.m_arrTags[i] )+1 ];
        strcpy( m_arrTags[i], other.m_arrTags[i] );
    }

    for( i = 0; i < m_arrValues.size(); i++)
    {
        m_arrValues[i] = new char[ strlen( other.m_arrValues[i] )+1 ];
        strcpy( m_arrValues[i], other.m_arrValues[i] );
    }
}

//  Destruktor
CNastParameterContextTaggedFile::~CNastParameterContextTaggedFile()
{
    NAST_ASSERT_VALID( this );

    int i;
    for( i = 0; i < m_arrTags.size(); i++)
        delete m_arrTags[i];

    for( i = 0; i < m_arrValues.size(); i++)
        delete m_arrValues[i];
}

//-------------------------------------------------------------------------
//                         Memberfunktionen
//-------------------------------------------------------------------------
bool 
CNastParameterContextTaggedFile::getParameter( int &parameter, 
        				       const char *szName,
        				       Req req /* = CNastParameterContext::required */) const
{
    NAST_ASSERT_VALID( this );	// Objekt in gueltigem Zustand ?
    NAST_ASSERT( szName );

    const char *szValue = searchValue( szName );
    if( szValue )		//  Parameter gefunden ?
    {
        parameter = atoi( szValue);
        return true;
    }
    
    char szTmp[100];
    switch( req )
    {

    case CNastParameterContext::optional:
        return false;
        
    case CNastParameterContext::recommended:
        sprintf( szTmp, "empfohlener Parameter %s nicht gefunden !", szName );
        NAST_WARNING( szTmp );
        return false;

    case CNastParameterContext::required:
        sprintf( szTmp, "benoetigter Parameter %s nicht gefunden !", szName );
        // Programmabbruch
        NAST_FATAL_ERROR( szTmp );
    }

    return false;
}


bool 
CNastParameterContextTaggedFile::getParameter( double &parameter, 
        				       const char *szName,
        				       Req req /*= CNastParameterContext::required */ ) const
{
    NAST_ASSERT_VALID( this );	// Objekt in gueltigem Zustand ?
    NAST_ASSERT( szName );

    const char *szValue = searchValue( szName );
    if( szValue )		//  Parameter gefunden ?
    {
        parameter = atof( szValue);
        return true;
    }

    char szTmp[100];
    switch( req )
    {

    case CNastParameterContext::optional:
        return false;
        
    case CNastParameterContext::recommended:
        sprintf( szTmp, "empfohlener Parameter %s nicht gefunden !", szName );
        NAST_WARNING( szTmp );
        return false;

    case CNastParameterContext::required:
        sprintf( szTmp, "benoetigter Parameter %s nicht gefunden !", szName );
        // Programmabbruch
        NAST_FATAL_ERROR( szTmp );
    }

    return false;
}


bool
CNastParameterContextTaggedFile::getParameter( char *parameter,            
        				       const char *szName,
        				       Req req /*= CNastParameterContext::required */ ) const
{
    NAST_ASSERT_VALID( this );	// Objekt in gueltigem Zustand ?
    NAST_ASSERT( szName );

    const char *szValue = searchValue( szName );
    if( szValue )		//  Parameter gefunden ?
    {
        parameter = strcpy( parameter, szValue);
        return true;
    }

    char szTmp[100];
    switch( req )
    {

    case CNastParameterContext::optional:
        return false;
        
    case CNastParameterContext::recommended:
        sprintf( szTmp, "empfohlener Parameter %s nicht gefunden !", szName );
        NAST_WARNING( szTmp );
        return false;

    case CNastParameterContext::required:
        sprintf( szTmp, "benoetigter Parameter %s nicht gefunden !", szName );
        // Programmabbruch
        NAST_FATAL_ERROR( szTmp );
    }

    return false;
}

CNastParameterContext*
CNastParameterContextTaggedFile::section( const char *szSection ) const
{
    NAST_ASSERT_VALID( this );
    NAST_ASSERT( szSection );

    CNastParameterContextTaggedFile *pNewContext = new CNastParameterContextTaggedFile();
    int  len = strlen( szSection );
    NAST_ASSERT( len < nMaxLineLength );

    char szSearch[nMaxLineLength]; // szSection + Trennzeichen
    strcpy( szSearch, szSection );
    szSearch[len+1] = CHAR_SECTION_SEPERATOR;
    szSearch[len+2] = '\0';
    
    for( int i = 0; i < m_arrTags.size(); i++ )
    {
        const char *szTag = m_arrTags[i];

        // beginnt der Tag mit szSearch ?
        if( strstr( szTag, szSection ) == m_arrTags[i] )
        {
            // jetzt kann kopiert werden

            const char *pTmp = szTag+len+1;
            NAST_ASSERT( strlen( pTmp) );

            char *szTagNew = new char[ strlen(pTmp)+1 ];
            strcpy( szTagNew, pTmp );
            pNewContext->m_arrTags.add( szTagNew );

            char *szValueNew = new char[ strlen(m_arrValues[i])+1 ];
            strcpy( szValueNew, m_arrValues[i] );
            pNewContext->m_arrValues.add( szValueNew );
        }
    }

    return pNewContext;
}

// ----------------------------------------------------------------------------
//                                    Debug
// ----------------------------------------------------------------------------

void 
CNastParameterContextTaggedFile::debugDump( CNastDumpContext &dumpContext ) const
{
    NAST_ASSERT_VALID( this );
    CNastParameterContext::debugDump( dumpContext );
    dumpContext << "\t"   << "CNastParameterContextTaggedFile \n";

    for( int i = 0; i < m_arrValues.size(); i++)
    {
        dumpContext << "\t[" << i << "] = \t";
        dumpContext << m_arrTags[i] << "\t";
        dumpContext << m_arrValues[i] << "\n";
    }
}


void 
CNastParameterContextTaggedFile::assertValid() const
{
    // zuerst einmal AssertValid der Basisklasse aufrufen
    CNastParameterContext::assertValid(); 

    //  die beiden Arrays muessen die gleiche Groesse haben
    NAST_ASSERT( m_arrTags.size() == m_arrValues.size() );
}



