/*
 * (C) Copyright 2013 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

/// @file Connectivity.h
/// @author Willem Deconinck
/// @date October 2015
/// @details
/// This file contains Connectivity classes
///   - IrregularConnectivity
///   - BlockConnectivity
///   - MultiBlockConnectivity
///
/// It is important to note that connectivity access functions are
/// inlined for optimal performance. The connectivity itself is internally
/// stored with base 1, to be compatible with Fortran access.
/// C++ access operators however convert the resulting connectivity to base 0.

#pragma once

#include <array>
#include <cstring>
#include <initializer_list>

#include "atlas/array.h"
#include "atlas/array/ArrayView.h"
#include "atlas/array/DataType.h"
#include "atlas/array/IndexView.h"
#include "atlas/array/SVector.h"
#include "atlas/array/Vector.h"
#include "atlas/array_fwd.h"
#include "atlas/library/config.h"
#include "atlas/util/Object.h"

namespace eckit {
class Stream;
}

namespace atlas {
namespace mesh {

constexpr size_t MAX_STRING_SIZE() {
    return 60;
}

template <typename ConnectivityImpl>
class ConnectivityInterface : public util::Object, DOXYGEN_HIDE( public ConnectivityImpl ) {
    using ConnectivityImpl::ConnectivityImpl;
    using util::Object::Object;
};

// Classes defined in this file:
class IrregularConnectivityImpl;
class BlockConnectivityImpl;
class MultiBlockConnectivityImpl;

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

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#if ATLAS_HAVE_FORTRAN
#define INDEX_DEREF Index
#define INDEX_REF Index
#define FROM_FORTRAN -1
#define TO_FORTRAN +1
#else
#define INDEX_DEREF *
#define INDEX_REF idx_t&
#define FROM_FORTRAN
#define TO_FORTRAN
#endif
#endif

/// @brief Irregular Connectivity Table
/// @author Willem Deconinck
///
/// Container for irregular connectivity tables. This is e.g. the case
/// for a node-to-X connectivity.
///   connectivity = [
///     1   2   3   4   5   6       # node 1
///     7   8                       # node 2
///     9  10  11  12               # node 3
///    13  14  15                   # node 4
///    16  17  18  19  20  21       # node 5
///   ]
/// There are 2 modes of construction:
///   - It wraps existing raw data
///   - It owns the connectivity data
///
/// In case ATLAS_HAVE_FORTRAN is defined (which is usually the case),
/// the raw data will be stored with base 1 for Fortran interoperability.
/// The operator(row,col) will then do the conversion to base 0.
///
/// In the first mode of construction, the connectivity table cannot be resized.
/// In the second mode of construction, resizing is possible

namespace detail {
// FortranIndex:
// Helper class that does +1 and -1 operations on stored values

class ConnectivityIndex {
public:
    enum
    {
        BASE = 1
    };

public:
    ATLAS_HOST_DEVICE ConnectivityIndex( const ConnectivityIndex& other ) { set( other.get() ); }
    ATLAS_HOST_DEVICE ConnectivityIndex( idx_t* idx ) : idx_( idx ) {}
    ATLAS_HOST_DEVICE void set( const idx_t& value ) { *( idx_ ) = value + BASE; }
    ATLAS_HOST_DEVICE idx_t get() const { return *(idx_)-BASE; }
    ATLAS_HOST_DEVICE void operator=( const idx_t& value ) { set( value ); }
    ATLAS_HOST_DEVICE ConnectivityIndex& operator+( const idx_t& value ) {
        *( idx_ ) += value;
        return *this;
    }
    ATLAS_HOST_DEVICE ConnectivityIndex& operator-( const idx_t& value ) {
        *( idx_ ) -= value;
        return *this;
    }
    ATLAS_HOST_DEVICE ConnectivityIndex& operator--() {
        --( *( idx_ ) );
        return *this;
    }
    ATLAS_HOST_DEVICE ConnectivityIndex& operator++() {
        ++( *( idx_ ) );
        return *this;
    }

    // implicit conversion
    ATLAS_HOST_DEVICE operator idx_t() const { return get(); }

private:
    idx_t* idx_;
};
}  // namespace detail

class ConnectivityRow {
#if ATLAS_HAVE_FORTRAN
    using Index = detail::ConnectivityIndex;
#else
    using Index = idx_t&;
#endif
public:
    ATLAS_HOST_DEVICE
    ConnectivityRow( idx_t* data, idx_t size ) : data_( data ), size_( size ) {}

    template <typename Int>
    ATLAS_HOST_DEVICE idx_t operator()( Int i ) const {
        return data_[i] FROM_FORTRAN;
    }

    template <typename Int>
    ATLAS_HOST_DEVICE INDEX_REF operator()( Int i ) {
        return INDEX_DEREF( data_ + i );
    }

    ATLAS_HOST_DEVICE
    idx_t size() const { return size_; }

private:
    idx_t* data_;
    idx_t size_;
};

class IrregularConnectivityImpl {
public:
    typedef ConnectivityRow Row;

public:
    //-- Constructors

    /// @brief Construct connectivity table that needs resizing a-posteriori
    /// Data is owned
    IrregularConnectivityImpl( const std::string& name = "" );

    /// @brief Construct connectivity table wrapping existing raw data.
    /// No resizing can be performed as data is not owned.
    IrregularConnectivityImpl( idx_t values[], idx_t rows, idx_t displs[], idx_t counts[] );

#ifdef __CUDACC__
    /// @brief Copy ctr (only to be used when calling a cuda kernel)
    // This ctr has to be defined in the header, since __CUDACC__ will identify
    // whether
    // it is compiled it for a GPU kernel
    IrregularConnectivityImpl( const IrregularConnectivityImpl& other ) :
        owns_( false ),
        values_( other.values_ ),
        displs_( other.displs_ ),
        counts_( other.counts_ ),
        missing_value_( other.missing_value_ ),
        rows_( other.rows_ ),
        maxcols_( other.maxcols_ ),
        mincols_( other.mincols_ ),
        ctxt_( nullptr ) {}
#endif

    /// @brief Construct a mesh from a Stream (serialization)
    explicit IrregularConnectivityImpl( eckit::Stream& );

    virtual ~IrregularConnectivityImpl();

    //-- Accessors

    /// @brief Name associated to this Connetivity
    const std::string name() const { return std::string( name_ ); }

    /// @brief Rename this Connectivity
    void rename( const std::string& name ) {
        strncpy( name_, name.c_str(), std::max( name.size(), MAX_STRING_SIZE() ) );
    }

    /// @brief Number of rows in the connectivity table
    ATLAS_HOST_DEVICE
    idx_t rows() const { return rows_; }

    /// @brief Number of columns for specified row in the connectivity table
    ATLAS_HOST_DEVICE
    idx_t cols( idx_t row_idx ) const { return counts_[row_idx]; }

    /// @brief Maximum value for number of columns over all rows
    ATLAS_HOST_DEVICE
    idx_t maxcols() const { return maxcols_; }

    /// @brief Minimum value for number of columns over all rows
    ATLAS_HOST_DEVICE
    idx_t mincols() const { return mincols_; }

    /// @brief Access to connectivity table elements for given row and column
    /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined.
    ATLAS_HOST_DEVICE
    idx_t operator()( idx_t row_idx, idx_t col_idx ) const;

    idx_t size() const { return values_.size(); }

    ATLAS_HOST_DEVICE
    idx_t missing_value() const { return missing_value_; }

    ATLAS_HOST_DEVICE
    Row row( idx_t row_idx ) const;

    // -- Modifiers

    /// @brief Modify row with given values. Values must be given with base 0
    void set( idx_t row_idx, const idx_t column_values[] );

    /// @brief Modify (row,col) with given value. Value must be given with base 0
    void set( idx_t row_idx, idx_t col_idx, const idx_t value );

    /// @brief Resize connectivity
    /// @note Can only be used when data is owned.
    virtual void resize( idx_t old_size, idx_t size, bool initialize, const idx_t values[], bool fortran_array );

    /// @brief Resize connectivity, and add given rows
    /// @note Can only be used when data is owned.
    virtual void add( idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false );

    /// @brief Resize connectivity, and add given rows with missing values
    /// @note Can only be used when data is owned.
    virtual void add( idx_t rows, idx_t cols );

    /// @brief Resize connectivity, and add given rows with missing values
    /// @note Can only be used when data is owned.
    virtual void add( idx_t rows, const idx_t cols[] );

    /// @brief Resize connectivity, and copy from a BlockConnectivity
    virtual void add( const BlockConnectivityImpl& block );

    /// @brief Resize connectivity, and insert given rows
    /// @note Can only be used when data is owned.
    virtual void insert( idx_t position, idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false );

    /// @brief Resize connectivity, and insert given rows with missing values
    /// @note Can only be used when data is owned.
    virtual void insert( idx_t position, idx_t rows, idx_t cols );

    /// @brief Resize connectivity, and insert given rows with missing values
    /// @note Can only be used when data is owned.
    virtual void insert( idx_t position, idx_t rows, const idx_t cols[] );

    virtual void clear();

    virtual size_t footprint() const;

    idx_t displs( const idx_t row ) const { return displs_[row]; }

    void dump( std::ostream& os ) const;
    friend std::ostream& operator<<( std::ostream& os, const IrregularConnectivityImpl& p ) {
        p.dump( os );
        return os;
    }

    virtual void encode( eckit::Stream& s ) const { encode_( s ); }
    virtual void decode( eckit::Stream& s ) { decode_( s ); }

protected:
    bool owns() { return owns_; }
    const idx_t* displs() const { return displs_.data(); }
    const idx_t* counts() const { return counts_.data(); }

    /// @brief Serialization to Stream
    void encode_( eckit::Stream& ) const;

    /// @brief Serialization from Stream
    void decode_( eckit::Stream& );

    friend eckit::Stream& operator<<( eckit::Stream& s, const IrregularConnectivityImpl& x ) {
        x.encode( s );
        return s;
    }

    friend eckit::Stream& operator>>( eckit::Stream& s, IrregularConnectivityImpl& x ) {
        x.decode( s );
        return s;
    }

private:
    void on_delete();
    void on_update();

private:
    char name_[MAX_STRING_SIZE()];
    bool owns_;

protected:
    array::SVector<idx_t> values_;
    array::SVector<idx_t> displs_;
    array::SVector<idx_t> counts_;

    idx_t missing_value_;
    idx_t rows_;
    idx_t maxcols_;
    idx_t mincols_;

public:
    typedef void* ctxt_t;
    typedef void ( *callback_t )( ctxt_t );

private:
    friend class ConnectivityPrivateAccess;
    ctxt_t ctxt_;
    callback_t callback_update_;
    callback_t callback_delete_;
};

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

/// @brief Connectivity contiguously composed of multiple BlockConnectivityImpl
/// @author Willem Deconinck
///
/// Container for connectivity tables that are layed out in memory as multiple
/// BlockConnectivities stitched together.
/// This is e.g. the case for a element-node connectivity, with element-types
/// grouped together:
/// @code{.sh}
///  connectivity = [
///     # triangles  (block 0)
///         1   2   3
///         4   5   6
///         7   8   9
///        10  11  12
///     # quadrilaterals (block 1)
///        13  14  15  16
///        17  18  19  20
///        21  22  23  24
///  ]
/// @endcode
///
/// This class can also be interpreted as a atlas::IrregularConnectivity without
/// distinction between blocks
///
/// There are 2 modes of construction:
///   - It wraps existing raw data
///   - It owns the connectivity data
///
/// In case ATLAS_HAVE_FORTRAN is defined (which is usually the case),
/// the raw data will be stored with base 1 for Fortran interoperability.
/// The operator(row,col) will then do the conversion to base 0.
///
/// In the first mode of construction, the connectivity table cannot be resized.
/// In the second mode of construction, resizing is possible
class MultiBlockConnectivityImpl : public IrregularConnectivityImpl {
public:
    //-- Constructors

    /// @brief Construct connectivity table that needs resizing a-posteriori
    /// Data is owned
    MultiBlockConnectivityImpl( const std::string& name = "" );

    /// @brief Construct a mesh from a Stream (serialization)
    explicit MultiBlockConnectivityImpl( eckit::Stream& );

    virtual ~MultiBlockConnectivityImpl();

    //-- Accessors

    /// @brief Number of blocks
    ATLAS_HOST_DEVICE
    idx_t blocks() const { return blocks_; }

    /// @brief Access to a block connectivity
    ATLAS_HOST_DEVICE
    const BlockConnectivityImpl& block( idx_t block_idx ) const { return block_[block_idx]; }
    ATLAS_HOST_DEVICE
    BlockConnectivityImpl& block( idx_t block_idx ) { return block_[block_idx]; }

    /// @brief Access to connectivity table elements for given row and column
    /// The row_idx counts up from 0, from block 0, as in IrregularConnectivity
    /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined.
    ATLAS_HOST_DEVICE
    idx_t operator()( idx_t row_idx, idx_t col_idx ) const;

    /// @brief Access to connectivity table elements for given row and column
    /// The block_row_idx counts up from zero for every block_idx.
    /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined.
    ATLAS_HOST_DEVICE
    idx_t operator()( idx_t block_idx, idx_t block_row_idx, idx_t block_col_idx ) const;

    //-- Modifiers

    /// @brief Resize connectivity, and add given rows as a new block
    /// @note Can only be used when data is owned.
    virtual void add( idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false );

    /// @brief Resize connectivity, and copy from a BlockConnectivity to a new
    /// block
    /// @note Can only be used when data is owned.
    virtual void add( const BlockConnectivityImpl& );

    /// @brief Resize connectivity, and add given rows with missing values
    /// @note Can only be used when data is owned.
    virtual void add( idx_t rows, idx_t cols );

    /// @brief Resize connectivity, and add given rows with missing values
    /// @note Can only be used when data is owned.
    virtual void add( idx_t rows, const idx_t cols[] );

    /// @brief Resize connectivity, and insert given rows
    /// @note Can only be used when data is owned.
    virtual void insert( idx_t position, idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false );

    /// @brief Resize connectivity, and insert given rows with missing values
    /// @note Can only be used when data is owned.
    virtual void insert( idx_t position, idx_t rows, idx_t cols );

    /// @brief Resize connectivity, and insert given rows with missing values
    /// @note Can only be used when data is owned.
    virtual void insert( idx_t position, idx_t rows, const idx_t cols[] );

    virtual void clear();

    virtual size_t footprint() const;

    virtual void encode( eckit::Stream& s ) const {
        IrregularConnectivityImpl::encode_( s );
        encode_( s );
    }
    virtual void decode( eckit::Stream& s ) {
        IrregularConnectivityImpl::decode_( s );
        decode_( s );
    }

protected:
    /// @brief Serialization to Stream
    void encode_( eckit::Stream& ) const;

    /// @brief Serialization from Stream
    void decode_( eckit::Stream& );

    friend eckit::Stream& operator<<( eckit::Stream& s, const MultiBlockConnectivityImpl& x ) {
        x.encode( s );
        return s;
    }

    friend eckit::Stream& operator>>( eckit::Stream& s, MultiBlockConnectivityImpl& x ) {
        x.decode( s );
        return s;
    }

private:
    void rebuild_block_connectivity();

private:
    idx_t blocks_;
    array::SVector<idx_t> block_displs_;
    array::SVector<idx_t> block_cols_;

    array::SVector<BlockConnectivityImpl> block_;
};

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

/// @brief Block Connectivity table
/// @author Willem Deconinck
/// Container for connectivity tables that are layed out in memory as a block.
/// Every row has the same
/// number of columns.
///
/// There are 2 modes of construction:
///   - It wraps existing raw data
///   - It owns the connectivity data
///
/// In case ATLAS_HAVE_FORTRAN is defined (which is usually the case),
/// the raw data will be stored with base 1 for Fortran interoperability.
/// The operator(row,col) will then do the conversion to base 0.
///
/// In the first mode of construction, the connectivity table cannot be resized.
/// In the second mode of construction, resizing is possible
class BlockConnectivityImpl {
private:
    friend class IrregularConnectivityImpl;
    friend class MultiBlockConnectivityImpl;
    BlockConnectivityImpl( idx_t rows, idx_t cols, idx_t values[], bool dummy );

public:
    //-- Constructors

    /// @brief Construct connectivity table that needs resizing a-posteriori
    /// Data is owned
    BlockConnectivityImpl();
    BlockConnectivityImpl( idx_t rows, idx_t cols, const std::initializer_list<idx_t>& );

    /// @brief Construct connectivity table wrapping existing raw data.
    /// No resizing can be performed as data is not owned.
    BlockConnectivityImpl( idx_t rows, idx_t cols, idx_t values[] );

#ifdef __CUDACC__
    /// @brief Copy ctr (only to be used when calling a cuda kernel)
    // This ctr has to be defined in the header, since __CUDACC__ will identify
    // whether it is compiled it for a GPU kernel
    BlockConnectivityImpl( const BlockConnectivityImpl& other ) :
        owns_( false ),
        values_( other.values_ ),
        rows_( other.rows_ ),
        cols_( other.cols_ ),
        missing_value_( other.missing_value_ ) {}
#endif

    BlockConnectivityImpl( BlockConnectivityImpl&& other );

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

    /// @brief Construct a mesh from a Stream (serialization)
    explicit BlockConnectivityImpl( eckit::Stream& );

    /// @brief Destructor
    ~BlockConnectivityImpl();

    ATLAS_HOST_DEVICE
    idx_t index( idx_t i, idx_t j ) const;

    //-- Accessors

    /// @brief Access to connectivity table elements for given row and column
    /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined.
    ATLAS_HOST_DEVICE
    idx_t operator()( idx_t row_idx, idx_t col_idx ) const;

    /// @brief Number of rows
    ATLAS_HOST_DEVICE
    idx_t rows() const { return rows_; }

    /// @brief Number of columns
    ATLAS_HOST_DEVICE
    idx_t cols() const { return cols_; }

    /// @brief Access to raw data.
    /// Note that the connectivity base is 1 in case ATLAS_HAVE_FORTRAN is
    /// defined.
    ATLAS_HOST_DEVICE
    const idx_t* data() const { return values_.data(); }
    ATLAS_HOST_DEVICE
    idx_t* data() { return values_.data(); }

    ATLAS_HOST_DEVICE
    idx_t missing_value() const { return missing_value_; }

    size_t footprint() const;

    //-- Modifiers

    /// @brief Modify row with given values. Values must be given with base 0
    ATLAS_HOST_DEVICE
    void set( idx_t row_idx, const idx_t column_values[] );

    /// @brief Modify (row,col) with given value. Value must be given with base 0
    ATLAS_HOST_DEVICE
    void set( idx_t row_idx, idx_t col_idx, const idx_t value );

    /// @brief Resize connectivity, and add given rows
    /// @note Can only be used when data is owned.
    void add( idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false );

    bool owns() const { return owns_; }


    friend std::ostream& operator<<( std::ostream& out, const BlockConnectivityImpl& x ) {
        x.print( out );
        return out;
    }


protected:
    /// @brief Wrap existing and set owns_ = false
    void rebuild( idx_t rows, idx_t cols, idx_t values[] );


    /// @brief Serialization to Stream
    void encode( eckit::Stream& ) const;

    /// @brief Serialization from Stream
    void decode( eckit::Stream& );

    friend eckit::Stream& operator<<( eckit::Stream& s, const BlockConnectivityImpl& x ) {
        x.encode( s );
        return s;
    }

    friend eckit::Stream& operator>>( eckit::Stream& s, BlockConnectivityImpl& x ) {
        x.decode( s );
        return s;
    }

    void print( std::ostream& ) const;

private:
    bool owns_;
    array::SVector<idx_t> values_;

    idx_t rows_;
    idx_t cols_;
    idx_t missing_value_;
};

using IrregularConnectivity  = ConnectivityInterface<IrregularConnectivityImpl>;
using MultiBlockConnectivity = ConnectivityInterface<MultiBlockConnectivityImpl>;
using BlockConnectivity      = BlockConnectivityImpl;
using Connectivity           = IrregularConnectivity;

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

inline idx_t IrregularConnectivityImpl::operator()( idx_t row_idx, idx_t col_idx ) const {
    assert( counts_[row_idx] > ( col_idx ) );
    return values_[displs_[row_idx] + col_idx] FROM_FORTRAN;
}

inline void IrregularConnectivityImpl::set( idx_t row_idx, const idx_t column_values[] ) {
    const idx_t N = counts_[row_idx];
    for ( idx_t n = 0; n < N; ++n ) {
        values_[displs_[row_idx] + n] = column_values[n] TO_FORTRAN;
    }
}

inline void IrregularConnectivityImpl::set( idx_t row_idx, idx_t col_idx, const idx_t value ) {
    assert( col_idx < counts_[row_idx] );
    values_[displs_[row_idx] + col_idx] = value TO_FORTRAN;
}

inline IrregularConnectivityImpl::Row IrregularConnectivityImpl::row( idx_t row_idx ) const {
    return IrregularConnectivityImpl::Row( const_cast<idx_t*>( values_.data() ) + displs_( row_idx ),
                                           counts_( row_idx ) );
}

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

inline idx_t MultiBlockConnectivityImpl::operator()( idx_t row_idx, idx_t col_idx ) const {
    return IrregularConnectivityImpl::operator()( row_idx, col_idx );
}

inline idx_t MultiBlockConnectivityImpl::operator()( idx_t block_idx, idx_t block_row_idx, idx_t block_col_idx ) const {
    return block( block_idx )( block_row_idx, block_col_idx );
}

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

inline idx_t BlockConnectivityImpl::operator()( idx_t row_idx, idx_t col_idx ) const {
    return values_[index( row_idx, col_idx )] FROM_FORTRAN;
}

inline void BlockConnectivityImpl::set( idx_t row_idx, const idx_t column_values[] ) {
    for ( idx_t n = 0; n < cols_; ++n ) {
        values_[index( row_idx, n )] = column_values[n] TO_FORTRAN;
    }
}

inline void BlockConnectivityImpl::set( idx_t row_idx, idx_t col_idx, const idx_t value ) {
    values_[index( row_idx, col_idx )] = value TO_FORTRAN;
}

inline idx_t BlockConnectivityImpl::index( idx_t i, idx_t j ) const {
    return i * cols_ + j;
}

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

extern "C" {
Connectivity* atlas__Connectivity__create();
MultiBlockConnectivity* atlas__MultiBlockConnectivity__create();
const char* atlas__Connectivity__name( Connectivity* This );
void atlas__Connectivity__rename( Connectivity* This, const char* name );
void atlas__Connectivity__delete( Connectivity* This );
void atlas__Connectivity__displs( Connectivity* This, idx_t*& displs, idx_t& size );
void atlas__Connectivity__counts( Connectivity* This, idx_t*& counts, idx_t& size );
void atlas__Connectivity__values( Connectivity* This, idx_t*& values, idx_t& size );
idx_t atlas__Connectivity__rows( const Connectivity* This );
void atlas__Connectivity__add_values( Connectivity* This, idx_t rows, idx_t cols, idx_t values[] );
void atlas__Connectivity__add_missing( Connectivity* This, idx_t rows, idx_t cols );
idx_t atlas__Connectivity__missing_value( const Connectivity* This );

idx_t atlas__MultiBlockConnectivity__blocks( const MultiBlockConnectivity* This );
BlockConnectivityImpl* atlas__MultiBlockConnectivity__block( MultiBlockConnectivity* This, idx_t block_idx );

idx_t atlas__BlockConnectivity__rows( const BlockConnectivityImpl* This );
idx_t atlas__BlockConnectivity__cols( const BlockConnectivityImpl* This );
idx_t atlas__BlockConnectivity__missing_value( const BlockConnectivityImpl* This );
void atlas__BlockConnectivity__data( BlockConnectivityImpl* This, idx_t*& data, idx_t& rows, idx_t& cols );
void atlas__BlockConnectivity__delete( BlockConnectivityImpl* This );
}

#undef FROM_FORTRAN
#undef TO_FORTRAN
#undef INDEX_DEREF

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

}  // namespace mesh

namespace array {

// TODO HACK
// template<> inline DataType::kind_t
// DataType::kind<mesh::BlockConnectivityImpl*>()   { return KIND_INT32;    }
}

}  // namespace atlas
