#ifndef INCLUDE_NASTARRAY2D_H
#define INCLUDE_NASTARRAY2D_H

//-----------------------------------------------------------------------------
//  NastArray2d.h
//-----------------------------------------------------------------------------
//
//  Copyright (C) 1998 Technische Universitaet Muenchen, Germany
//                   written by Bernhard Brueck
//
//  This file is part of Nast++
//
//-----------------------------------------------------------------------------
//  Die Klasse stellt ein zweidimensionales Array zur Verfuehgung.
//  Die Elemente koennen ueber den operator( int,int) angesprochen werden.
//-----------------------------------------------------------------------------
//  Aenderungen:
//     

#include "NastConfig.h"
#include "NastObject.h"
#include "NastDebug.h"

template <class T> 
class CNastArray2d : public CNastObject
{
public:
    //-------------------------------------------------------------------------
    //                         Konstruktor + Destruktor
    //-------------------------------------------------------------------------
    CNastArray2d();
    CNastArray2d( int nSizeI, int nSizeJ );

    virtual ~CNastArray2d();			

    CNastArray2d( const CNastArray2d & other);	
    const CNastArray2d& operator=( const CNastArray2d &other );

    //-------------------------------------------------------------------------
    //  Zugriffsfunktionen
    //-------------------------------------------------------------------------

    void setSize( int nSizeI, int nSizeJ );    // neue Groesse festlegen
    int  sizeI() const;	                       // und abfragen 
    int  sizeJ() const;                        //

    T& operator()( int i, int j);	       // lhs 
    const T&  operator()( int i, int j) const; // rhs 

    void setAll( const T& ele);                // neuer Wert fuer alle Elemente

    //-------------------------------------------------------------------------
    //                                Debug
    //-------------------------------------------------------------------------
    //  debugInfo    gibt Information ueber den Zustand des Objekts aus
    //  assertValid  testet das Objekt auf Integritaet

    virtual void assertValid() const;
    virtual void debugDump( CNastDumpContext &dumpContext ) const;

private:
    //-------------------------------------------------------------------------
    //                           Hilfsunktionen
    //-------------------------------------------------------------------------

    void init( int nRows, int nCols );	    // Speicher belegen
    void removeAll();		            // Speicher freigeben
    void copy( const CNastArray2d &matrix); // Array kopieren
    
    T* Row( int i ) const;

    //-------------------------------------------------------------------------
    //                          Membervariablen
    //-------------------------------------------------------------------------

    T **m_arrIndex;	// Array mit Zeigern auf die Daten
    T  *m_arrData;	// die eigentlichen Daten

    int m_nSizeI;	// Groesse erste Dimension
    int m_nSizeJ;	// Groesse zweite Dimension
    int m_nSize;	// Gesamtgroesse des Arrays
};


//-------------------------------------------------------------------------
//                             Implementation
//-------------------------------------------------------------------------

//  Defaultkonstruktor
template<class T>
CNastArray2d<T>::CNastArray2d()
    :m_arrIndex(0),
     m_arrData (0),
     m_nSizeI  (0),
     m_nSizeJ  (0),
     m_nSize   (0)
{}

//  Konstruktor unter der Angabe der Groesse des Arrays
template<class T>
CNastArray2d<T>::CNastArray2d( int nSizeI, int nSizeJ )
    :m_arrIndex(0),
     m_arrData (0),
     m_nSizeI  (0),
     m_nSizeJ  (0),
     m_nSize   (0)
{
    NAST_ASSERT( nSizeI > 0 );
    NAST_ASSERT( nSizeJ > 0 );

    init( nSizeI, nSizeJ );
}

//  Destruktor
template<class T>
CNastArray2d<T>::~CNastArray2d()
{
    NAST_ASSERT_VALID( this );  
    removeAll();
}

//  Copyconstr.
template<class T>
CNastArray2d<T>::CNastArray2d( const CNastArray2d &other )
    :CNastObject( other ),
     m_arrIndex(0),
     m_arrData (0),
     m_nSizeI  (0),
     m_nSizeJ  (0),
     m_nSize   (0)
{
    NAST_ASSERT_VALID( &other );
    copy( other );
}

//  Zuweisungsoperator
template<class T> 
const CNastArray2d<T>& CNastArray2d<T>::operator=( const CNastArray2d &other )
{
    NAST_ASSERT_VALID( this );
    NAST_ASSERT_VALID( &other );

    if( this != &other )          // keine Zuweisung auf sich selbst ?
    {
        copy( other );		  // dann kann kopiert werden
    }
    
    return *this;
}


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

template<class T> 
void CNastArray2d<T>::assertValid() const
{
    NAST_ASSERT( m_arrData );
    NAST_ASSERT( m_arrIndex);
    NAST_ASSERT( m_nSizeI > 0);
    NAST_ASSERT( m_nSizeJ > 0);
    NAST_ASSERT( m_nSize == m_nSizeI * m_nSizeJ );
}

template<class T> 
void CNastArray2d<T>::debugDump( CNastDumpContext &dumpContext ) const
{
    CNastObject::debugDump( dumpContext );

    dumpContext << "\t" << "CNastArray2d\n";

    //  ii statt i um einen Bug in g++2.7.2 zu umgehen
    for( int ii = 0; ii < sizeI(); ii++)
        for( int jj = 0; jj < sizeJ(); jj++)
        {
            dumpContext << "\t\t( " << ii << ", " << jj << " ) = ";
            dumpContext << operator()( ii, jj ) << "\n";
        }
}


// ----------------------------------------------------------------------------
//                                    Hilfsfunktionen
// ----------------------------------------------------------------------------

// belegt den Speicher und initialiert die Datenstrukturen
template<class T>
void CNastArray2d<T>::init( int nSizeI, int nSizeJ )	  
{
    //  Diese Routine darf nur verwendet werden wenn keine Daten vorhanden sind
    NAST_ASSERT( m_nSizeI   == 0);
    NAST_ASSERT( m_nSizeJ   == 0);
    NAST_ASSERT( m_arrData  == 0);
    NAST_ASSERT( m_arrIndex == 0);

    // Die Groesse merken
    m_nSizeI = nSizeI;
    m_nSizeJ = nSizeJ;
    m_nSize = nSizeI * nSizeJ;

    // zusammenhaengender Block mit den Daten
    m_arrData  = new T[m_nSize];

    // Block mit Zeigern(!) auf die Daten
    m_arrIndex = new T*[m_nSizeI];

    //  Zeiger belegen
    //  ii statt i um einen Bug in g++2.7.2 zu umgehen
    for( int ii = 0; ii < nSizeI; ii++)
    {
        // ein bisschen Hardcore :-)
        m_arrIndex[ii] = m_arrData + ii * nSizeJ;
    }
}


// den gesamten Speicher wieder freigeben
template<class T>
void CNastArray2d<T>::removeAll()         
{
    NAST_ASSERT( m_arrData  );	// Speicher muss belegt sein
    NAST_ASSERT( m_arrIndex );  
    
    delete [] m_arrIndex;	
    delete [] m_arrData;

    m_arrIndex = 0;
    m_arrData  = 0;
    m_nSize  = 0;
    m_nSizeI = 0;    
    m_nSizeJ = 0;
}


// kopiert die Elemente aus einem anderen Array und kuemmert sich bei unter-
// schiedlicher Groesse um die Speicherverwaltung
template<class T>
void CNastArray2d<T>::copy( const CNastArray2d &other)
{
    // falls die Matrizen unterschiedliche Groesse haben,
    // dann muss der alte Speicher freigeben werden
    // und neuer belegt werden
    if( m_nSizeI != other.m_nSizeI || 
        m_nSizeJ != other.m_nSizeJ )
    {
        if( m_nSizeI != 0)	// nicht vom Copyconstr. aufgerufen ?
            removeAll();
        init( other.m_nSizeI, other.m_nSizeJ );
    }
    
    // und die Elemente kopieren
    for( int i=0; i < m_nSize; i++)
        m_arrData[i] = other.m_arrData[i];
}

// ----------------------------------------------------------------------------
//                         Memberfunktionen
// ----------------------------------------------------------------------------

// Groesse aendern
// nach einer Aenderung der Groesse ist der Inhalt des Arrays undefiniert
template <class T> 
inline void CNastArray2d<T>::setSize( int nSizeI , int nSizeJ )
{
    NAST_ASSERT( nSizeI >= 0);
    NAST_ASSERT( nSizeJ >= 0);

    if( m_nSizeI != nSizeI || m_nSizeJ != nSizeJ )
    {
        if( m_arrData )
            removeAll();

        init( nSizeI, nSizeJ);
    }
}

// lhs
template <class T> 
inline T& CNastArray2d<T>::operator()( int i, int j)
{					  
    NAST_ASSERT_INDEX( i >= 0 );	  // Ueberpruefung ob i und j innerhalb
    NAST_ASSERT_INDEX( i < m_nSizeI);	  // des gueltigen Bereichs liegen.
    NAST_ASSERT_INDEX( j >= 0 );	  // (nur wenn NAST_USE_ASSERT_INDEX
    NAST_ASSERT_INDEX( j < m_nSizeJ);	  //  definiert ist. )

    return m_arrIndex[i][j];
}

// rhs
template <class T> 
inline const T& CNastArray2d<T>::operator()( int i, int j) const
{
    NAST_ASSERT_INDEX( i >= 0 );	  // Ueberpruefung ob i und j innerhalb
    NAST_ASSERT_INDEX( i < m_nSizeI );	  // des gueltigen Bereichs liegen.
    NAST_ASSERT_INDEX( j >= 0 );	  // (nur wenn NAST_USE_ASSERT_INDEX
    NAST_ASSERT_INDEX( j < m_nSizeJ );	  //  definiert ist. )

    return m_arrIndex[i][j];
}

// Groesse in der ersten Dimension
template <class T> 
inline int CNastArray2d<T>::sizeI() const
{
    NAST_ASSERT_VALID( this );
    return m_nSizeI;
}

// Groesse in der zweiten Dimension
template <class T> 
inline int CNastArray2d<T>::sizeJ() const
{
    NAST_ASSERT_VALID( this );
    return m_nSizeJ ;
}

// allen Elemente einen neuen Wert zuweisen
template <class T> 
void CNastArray2d<T>::setAll( const T& ele)
{
    NAST_ASSERT_VALID( this );

    // Es kann direkt auf den Daten operiert werden, und es braucht nicht
    // das Indexarray verwendet werden
    for( int i = 0; i < m_nSize; i++)
        m_arrData[i] = ele;
}
 
#endif // INCLUDE_NASTARRAY2D_H

