//----------------------------------------------------------------------------------------------------------------------
//
//  Declaration and implementation of the template class 'TC_Array2'                             
//
//  It implements a generic two dimensions dynamic sized array.                                  
//
//  COPY OF ITS INSTANCES IS ALLOWED AND FULLY IMPLEMENTED BY DUPLICATION.                       
//
//  This file is part of libpm library                                                           
//
//  Copyright (C) 1997 Pierre Molinaro.                                                          
//
//  e-mail : pierre@pcmolinaro.name
//
//  This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
//  Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option)
//  any later version.
//
//  This program is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; without even the implied
//  warranty of MERCHANDIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
//  more details.
//
//----------------------------------------------------------------------------------------------------------------------

#pragma once

//----------------------------------------------------------------------------------------------------------------------

#include "utilities/MF_MemoryControl.h"
#include "utilities/TF_Swap.h"

//----------------------------------------------------------------------------------------------------------------------

#include <stddef.h>

//----------------------------------------------------------------------------------------------------------------------

template <typename TYPE> class TC_Array2 ;

template <typename TYPE> void swap (TC_Array2 <TYPE> & ioOperand1,
                                    TC_Array2 <TYPE> & ioOperand2) ;

//----------------------------------------------------------------------------------------------------------------------

template <typename TYPE> class TC_Array2 {
  protected: TYPE * mArray ;
  protected: int32_t mCurrentRowCount ;
  protected: int32_t mCurrentColumnCount ;
  protected: int32_t mCapacity ;

//--- Constructors
  public: TC_Array2 (void) ;
  public: TC_Array2 (const int32_t inRowCount,
                      const int32_t inColumnCount COMMA_LOCATION_ARGS) ;

//--- Destructor
  public: virtual ~TC_Array2 (void) ;

//--- Handle copy
  public: TC_Array2 (TC_Array2 <TYPE> & inSource) ;
  public: TC_Array2 <TYPE> & operator = (TC_Array2 <TYPE> & inSource) ;

//--- Get Row and Column count
  public: inline int32_t rowCount (void) const { return mCurrentRowCount ; }
  public: inline int32_t columnCount (void) const { return mCurrentColumnCount ; }

//--- Acces
  #ifndef DO_NOT_GENERATE_CHECKINGS
    public: TYPE & operator () (const int32_t inRowIndex, const int32_t inColumnIndex COMMA_LOCATION_ARGS) ;
    public: const TYPE operator () (const int32_t inRowIndex, const int32_t inColumnIndex COMMA_LOCATION_ARGS) const ;
    protected: size_t long2size_t (const int32_t inRowIndex, const int32_t inColumnIndex COMMA_LOCATION_ARGS) const {
      MF_AssertThere (inRowIndex >= 0, "indice ligne (%ld) < 0", inRowIndex, 0) ;
      MF_AssertThere (inRowIndex < mCurrentRowCount, "indice ligne (%ld) >= nombre de lignes (%ld)", inRowIndex, mCurrentRowCount) ;
      MF_AssertThere (inColumnIndex >= 0, "indice colonne (%ld) < 0", inColumnIndex, 0) ;
      MF_AssertThere (inColumnIndex < mCurrentColumnCount, "indice ligne (%ld) >= nombre de colonnes (%ld)", inColumnIndex, mCurrentColumnCount) ;
      return (size_t) (inRowIndex * mCurrentColumnCount + inColumnIndex) ;
    }
    public: void setObjectAtIndexes (const TYPE & inObject,
                                      const int32_t inRowIndex,
                                      const int32_t inColumnIndex
                                      COMMA_LOCATION_ARGS) ;
  #endif

  #ifdef DO_NOT_GENERATE_CHECKINGS
    public: inline TYPE & operator () (const int32_t inRowIndex,
                                        const int32_t inColumnIndex) {
      return mArray [(size_t) (inRowIndex * mCurrentColumnCount + inColumnIndex)] ;
    }
    public: inline const TYPE operator () (const int32_t inRowIndex,
                                            const int32_t inColumnIndex) const {
      return mArray [(size_t) (inRowIndex * mCurrentColumnCount + inColumnIndex)] ;
    }
    public: inline void setObjectAtIndexes (const TYPE & inObject,
                                             const int32_t inRowIndex,
                                             const int32_t inColumnIndex
                                             COMMA_LOCATION_ARGS) {
      mArray [(size_t) (inRowIndex * mCurrentColumnCount + inColumnIndex)] = inObject ;
    }
  #endif

//--- Vider
  public: virtual void removeAll (void) ;

//--- Exchange
  friend void swap <TYPE> (TC_Array2 <TYPE> & ioOperand1,
                           TC_Array2 <TYPE> & ioOperand2) ;

//--- Changer la taille da la matrice
  public: void reallocArray (const int32_t inRowCount,
                              const int32_t inColumnCount COMMA_LOCATION_ARGS) ;
} ;

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

template <typename TYPE> TC_Array2 <TYPE>::TC_Array2 (void) :
mArray (NULL),
mCurrentRowCount (0),
mCurrentColumnCount (0),
mCapacity (0) {
}

//----------------------------------------------------------------------------------------------------------------------

template <typename TYPE> TC_Array2 <TYPE>::TC_Array2 (TC_Array2 <TYPE> & inSource) :
mArray (NULL),
mCurrentRowCount (0),
mCurrentColumnCount (0),
mCapacity (0) {
  *this = inSource ;
}

//----------------------------------------------------------------------------------------------------------------------

template <typename TYPE> TC_Array2 <TYPE>::TC_Array2 (const int32_t inRowCount,
                                                      const int32_t inColumnCount
                                                      COMMA_LOCATION_ARGS)  :
mArray (NULL),
mCurrentRowCount (0),
mCurrentColumnCount (0),
mCapacity (0){
  reallocArray (inRowCount, inColumnCount COMMA_THERE) ;
}

//----------------------------------------------------------------------------------------------------------------------

template <typename TYPE> TC_Array2 <TYPE> & TC_Array2 <TYPE>::operator = (TC_Array2 <TYPE> & inSource) {
  removeAll () ;
  const int32_t tailleSource = inSource.mCurrentRowCount * inSource.mCurrentColumnCount ;
  if (tailleSource == 0) {
    mCurrentRowCount = inSource.mCurrentRowCount ;
    mCurrentColumnCount = inSource.mCurrentColumnCount ;
  }else{
    try{
      macroMyNewArray (mArray, TYPE, tailleSource) ;
      for (int32_t i=0 ; i<tailleSource ; i++) {
        mArray [(size_t) i] = inSource.mArray [(size_t) i] ;
      }
      mCurrentRowCount = inSource.mCurrentRowCount ;
      mCurrentColumnCount = inSource.mCurrentColumnCount ;
    }catch (...) {
      macroMyDeleteArray (mArray) ;
      throw ;
    }
  }
  return * this ;
}

//----------------------------------------------------------------------------------------------------------------------

template <typename TYPE> TC_Array2 <TYPE>::~TC_Array2 (void) {
  removeAll () ;
}

//----------------------------------------------------------------------------------------------------------------------

template <typename TYPE> void TC_Array2<TYPE>::removeAll (void) {
  macroMyDeleteArray (mArray) ;
  mCurrentRowCount = 0 ;
  mCurrentColumnCount = 0 ;
  mCapacity = 0 ;
}

//----------------------------------------------------------------------------------------------------------------------

template <typename TYPE> void TC_Array2<TYPE>::reallocArray (const int32_t inRowCount,
                                                             const int32_t inColumnCount COMMA_LOCATION_ARGS) {
  if ((inRowCount != mCurrentRowCount) || (inColumnCount != mCurrentColumnCount)) {
    MF_AssertThere (inRowCount >= 0, "inRowCount (%ld) < 0", inRowCount, 0) ;
    MF_AssertThere (inColumnCount >= 0, "inColumnCount (%ld) < 0", inColumnCount, 0) ;
    const int32_t newNeededSize = inRowCount * inColumnCount ;
    int32_t newCapacity = (mCapacity > 32) ? mCapacity : 32 ;
    while (newCapacity < newNeededSize) {
      newCapacity <<= 1 ;
    }
    TYPE * newArray = NULL ;
    macroMyNewArray (newArray, TYPE, (size_t) newCapacity) ;
    const int32_t maxLigne = (mCurrentRowCount < inRowCount) ? mCurrentRowCount : inRowCount ;
    const int32_t maxColonne = (mCurrentColumnCount < inColumnCount) ? mCurrentColumnCount : inColumnCount ;
    for (int32_t i=0 ; i<maxLigne ; i++) {
      for (int32_t j=0 ; j<maxColonne ; j++) {
        newArray [(size_t) (i * inColumnCount + j)] = mArray [(size_t) (i * mCurrentColumnCount + j)] ;
      }
    }
    macroMyDeleteArray (mArray) ;
    mArray = newArray ; newArray = NULL ;
    mCurrentColumnCount = inColumnCount ;
    mCurrentRowCount = inRowCount ;
    mCapacity = newCapacity ;
  }
}

//----------------------------------------------------------------------------------------------------------------------

#ifndef DO_NOT_GENERATE_CHECKINGS
  template <typename TYPE> TYPE & TC_Array2 <TYPE>::operator () (const int32_t inRowIndex,
                                                                 const int32_t inColumnIndex
                                                                 COMMA_LOCATION_ARGS) {
    return mArray [long2size_t (inRowIndex, inColumnIndex COMMA_THERE)] ;
  }
#endif

//----------------------------------------------------------------------------------------------------------------------

#ifndef DO_NOT_GENERATE_CHECKINGS
  template <typename TYPE> const TYPE TC_Array2 <TYPE>::operator () (const int32_t inRowIndex,
                                                                     const int32_t inColumnIndex
                                                                     COMMA_LOCATION_ARGS) const {
    return mArray [long2size_t (inRowIndex, inColumnIndex COMMA_THERE)] ;
  }
#endif

//----------------------------------------------------------------------------------------------------------------------

#ifndef DO_NOT_GENERATE_CHECKINGS
  template <typename TYPE> void TC_Array2 <TYPE>::setObjectAtIndexes (const TYPE & inObject,
                                                                      const int32_t inRowIndex,
                                                                      const int32_t inColumnIndex
                                                                      COMMA_LOCATION_ARGS) {
    mArray [long2size_t (inRowIndex, inColumnIndex COMMA_THERE)] = inObject ;
  }
#endif

//----------------------------------------------------------------------------------------------------------------------

template <typename TYPE> void swap (TC_Array2 <TYPE> & ioOperand1,
                                    TC_Array2 <TYPE> & ioOperand2) {
  swap (ioOperand1.mArray, ioOperand2.mArray) ;
  swap (ioOperand1.mCurrentRowCount, ioOperand2.mCurrentRowCount) ;
  swap (ioOperand1.mCurrentColumnCount, ioOperand2.mCurrentColumnCount) ;
  swap (ioOperand1.mCapacity, ioOperand2.mCapacity) ;
}

//----------------------------------------------------------------------------------------------------------------------