// $Id$
//
// Earth System Modeling Framework
// Copyright (c) 2002-2023, University Corporation for Atmospheric Research, 
// Massachusetts Institute of Technology, Geophysical Fluid Dynamics 
// Laboratory, University of Michigan, National Centers for Environmental 
// Prediction, Los Alamos National Laboratory, Argonne National Laboratory, 
// NASA Goddard Space Flight Center.
// Licensed under the University of Illinois-NCSA License.
//
//-------------------------------------------------------------------------
// (all lines below between the !BOP and !EOP markers will be included in
//  the automated document processing.)
//-------------------------------------------------------------------------
// these lines prevent this file from being read more than once if it
// ends up being included multiple times

#ifndef ESMCI_LocalArray_H
#define ESMCI_LocalArray_H

//-----------------------------------------------------------------------------
//BOPI
// !CLASS:  ESMCI::LocalArray - uniform access to arrays from F90 and C++
//
// !DESCRIPTION:
//
// The code in this file defines the C++ LocalArray class and declares method
// signatures (prototypes).  The companion file {\tt ESMCI\_LocalArray.C}
// contains the full code (bodies) for the {\tt LocalArray} methods.
//
//EOPI
//-------------------------------------------------------------------------

#include <stdlib.h>

#include "ESMCI_Util.h"
#include "ESMCI_Macros.h"

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

namespace ESMCI {

  // classes and structs

  class LocalArray;

  // The following two numbers are sufficiently sized to accomodate 
  // the Fortran dope vector on all of the platforms we have tested.
  // The numbers are only used to allocate sufficient memory in the
  // the LocalArray structure, _not_ to do any actual memcpy() operation!
  // The Fortran dope vector is copied via a portable implementation
  // that passes pointers to the dope vectors to the Fortran side 
  // to do the actual copy.
#define ESMF_FPTR_BASE_SIZE         160
#define ESMF_FPTR_PLUS_RANK         64
  
  struct c_F90ptr {
    // Dummy structure which is at least as big as the Fortran pointer 
    // dope vector on any platform.
    unsigned char pad[ESMF_FPTR_BASE_SIZE + ESMF_MAXDIM * ESMF_FPTR_PLUS_RANK];
  };

  // this must stay in sync with the Fortran counter-part
  typedef enum{ 
    FROM_FORTRAN = 1, 
    FROM_CPLUSPLUS 
  }LocalArrayOrigin; 

  // this must stay in sync with the Fortran counter-part
  typedef enum{ 
    DATACOPY_NONE = 0,
    DATACOPY_VALUE,
    DATACOPY_REFERENCE,
    DATACOPY_ALLOC
  }DataCopyFlag;


  // class definition
  class LocalArray{

   protected:
    ESMC_TypeKind_Flag typekind;    // I1, I2, I4, I8, R4, R8
    int rank;                       // 1, 2, ..., ESMF_MAXDIM
    LocalArrayOrigin origin;        // create called from Fortran or C++?
    bool dealloc;                   // responsible for deallocation?
    bool contig;                    // optimization possible if all contiguous
    int offset[ESMF_MAXDIM];        // byte offset from base to 1st element/dim
    int lbound[ESMF_MAXDIM];        // used for fortran indexing
    int ubound[ESMF_MAXDIM];        // used for fortran indexing
    int counts[ESMF_MAXDIM];        // number of elements/dim
    int dimOff[ESMF_MAXDIM];        // Offset size per dim for computing pos.
    int lOff;                       // Offset due to lower bounds for pos. 
    int bytestride[ESMF_MAXDIM];    // byte spacing between elements/dim
    int byte_count;                 // size (in bytes) of data region
    void *base_addr;                // start of data allocation
    struct c_F90ptr f90dopev;       // opaque object which is real f90 ptr
                                    // this is memcpy'd to save and restore 
                                    // contents are not interpreted by esmf
    
  public:    
   private:
    // construct() and destruct()
    int construct(bool aflag, DataCopyFlag docopy,
      ESMC_TypeKind_Flag tk, int irank, LocalArrayOrigin oflag, bool dflag,
      const int *offsets, const int *lbounds, const int *ubounds,
      const int *icounts, void *ibase_addr, struct c_F90ptr *f90ptr);
    int destruct();

   public:
    // create() and destroy()
    static LocalArray *create(ESMC_TypeKind_Flag tk, int rank,
      LocalArrayOrigin oflag, int *rc = NULL);
    static LocalArray *create(ESMC_TypeKind_Flag tk, int rank, const int *counts,
      void *base_addr = NULL, DataCopyFlag docopy = DATACOPY_REFERENCE,
      int *rc = NULL);
    static LocalArray *create(ESMC_TypeKind_Flag dk, int rank, const int *counts,
      const int *lbounds, const int *ubounds, void *base_addr = NULL, 
      DataCopyFlag docopy = DATACOPY_REFERENCE, int *rc = NULL);
    static LocalArray *create(const LocalArray *larrayIn, DataCopyFlag copyflag,
      const int *lbounds, const int *ubounds, int *rc);
    static int destroy(LocalArray *array);

    // standard methods
    int print(const char *options = NULL)const;
    int write(const char *options, const char *filename)const;
    int validate(const char *options)const;

    // simple set/get methods
    void setRank(int rank){ this->rank = rank; }
    int getRank()const{ return rank; }
    void setTypeKind(ESMC_TypeKind_Flag typekind){ this->typekind = typekind; }
    ESMC_TypeKind_Flag getTypeKind()const{ return typekind; }
    void setBaseAddr(void *base_addr){ this->base_addr = base_addr; }
    void *getBaseAddr()const{ return base_addr; } 
    void setOrigin(LocalArrayOrigin origin){ this->origin = origin; } 
    LocalArrayOrigin getOrigin()const{ return origin; }
    void setByteCount(int byte_count){ this->byte_count = byte_count; }
    int getByteCount()const{ return byte_count; }
    void setDealloc(bool dealloc){ this->dealloc = dealloc; }
    bool getDealloc()const{ return dealloc; }
    const int *getCounts()const{ return counts; }
    const int *getLbounds()const{ return lbound; }
    const int *getUbounds()const{ return ubound; }
    
    // combo set method
    int setInfo(struct c_F90ptr *fptr, void *base, const int *counts, 
      const int *lbounds, const int *ubounds, const int *offsets,
      const bool *cflag, const bool *dflag);

    // set/get the Fortran dope vector
    int setFortranDopev(struct c_F90ptr *p);
    int getFortranDopev(struct c_F90ptr *p)const;
    
    // force the base address in Fortran ptr dope vector *if* mismatch detected
    int forceFortranPtr(void *base){
      void **f90dopev_cast = (void **)(&f90dopev);
      // The rationale for this call is as follows:
      // - some Fortran compilers will make temporary copies of the actual
      //   data array.
      // - such copies can be detected by comparing the address of the first
      //   array element against the address of the first element of the
      //   original data array, stored in the LocalArray object.
      // - if/when a temporary data copy has been detected this routine tries
      //   to force the base address in the stored f90 ptr dope vector to the
      //   correct base address stored in the LocalArray. The assumption is made
      //   that the base address is that of the first element and is stored 
      //   without offset at the start of the dope vector.
      // - since the details about the f90 ptr dope vector are compiler
      //   dependent the above assumption of where to force the base address
      //   (and with what offset in the address) may turn out to be incorrect.
      //   However, this routine is a last resort in those cases where a 
      //   temporary copy has been detected and it tests out fine on XLF and
      //   lahey.
      if (base != base_addr){
        // Only force base_addr into dope vector if the first element according
        // to dope vector is located somewhere else, i.e. a temporary copy.
        *f90dopev_cast = base_addr;
      }
      return ESMF_SUCCESS;
    }

    // Get Data from a position in the LocalArray
    template <class TYPE> int getData(int *index, TYPE *data);

    // Get Data from a position in the LocalArray w/o internal error checking
    template <class TYPE> void getDataInternal(int *index, TYPE *data);

    // Set Data to a position in the LocalArray
    template <class TYPE> int setData(int *index, TYPE data);

    // Set data to a position in the LocalArray w/o internal error checking
    template <class TYPE> void setDataInternal(int *index, TYPE data);

    // portably copy Fortran dope vector
    static int tkrPtrCopy(void *dst, void *src, ESMC_TypeKind_Flag typekind,
      int rank);
  
  };  // class LocalArray

} // namespace ESMCI

#endif  // ESMCI_LocalArray_H
