/*
 * (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.
 */

#include <iostream>
#include <string>

#include "atlas/field/Field.h"
#include "atlas/field/detail/FieldImpl.h"
#include "atlas/functionspace/FunctionSpace.h"

namespace atlas {

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

std::ostream& operator<<( std::ostream& os, const Field& f ) {
    os << ( *f.get() );
    return os;
}

Field::Field( const eckit::Parametrisation& config ) : Handle( Implementation::create( config ) ) {}

Field::Field( const std::string& name, array::DataType datatype, const array::ArrayShape& shape ) :
    Handle( Implementation::create( name, datatype, shape ) ) {}

Field::Field( const std::string& name, array::DataType datatype, array::ArraySpec&& spec ) :
    Handle( Implementation::create( name, datatype, std::move( spec ) ) ) {}

Field::Field( const std::string& name, array::Array* array ) : Handle( Implementation::create( name, array ) ) {}

template <typename DATATYPE>
Field::Field( const std::string& name, DATATYPE* data, const array::ArraySpec& spec ) :
    Handle( Implementation::wrap( name, data, spec ) ) {}

template <typename DATATYPE>
Field::Field( const std::string& name, DATATYPE* data, const array::ArrayShape& shape ) :
    Handle( Implementation::wrap( name, data, shape ) ) {}

/// @brief Implicit conversion to Array
Field::operator const array::Array&() const {
    return get()->array();
}
Field::operator array::Array&() {
    return get()->array();
}

const array::Array& Field::array() const {
    return get()->array();
}
array::Array& Field::array() {
    return get()->array();
}

// -- Accessors

/// @brief Access to raw data
void* Field::storage() {
    return get()->storage();
}

/// @brief Internal data type of field
array::DataType Field::datatype() const {
    return get()->datatype();
}

/// @brief Name associated to this field
const std::string& Field::name() const {
    return get()->name();
}

/// @brief Rename this field
void Field::rename( const std::string& name ) {
    get()->rename( name );
}

/// @brief Access to metadata associated to this field
const util::Metadata& Field::metadata() const {
    return get()->metadata();
}
util::Metadata& Field::metadata() {
    return get()->metadata();
}

/// @brief Resize field to given shape
void Field::resize( const array::ArrayShape& shape ) {
    get()->resize( shape );
}

void Field::insert( idx_t idx1, idx_t size1 ) {
    get()->insert( idx1, size1 );
}

/// @brief Shape of this field in Fortran style (reverse order of C style)
const std::vector<int>& Field::shapef() const {
    return get()->shapef();
}

/// @brief Strides of this field in Fortran style (reverse order of C style)
const std::vector<int>& Field::stridesf() const {
    return get()->stridesf();
}

/// @brief Shape of this field (reverse order of Fortran style)
const array::ArrayShape& Field::shape() const {
    return get()->shape();
}

/// @brief Strides of this field
const array::ArrayStrides& Field::strides() const {
    return get()->strides();
}

/// @brief Shape of this field associated to index 'i'
idx_t Field::shape( idx_t i ) const {
    return get()->shape( i );
}

/// @brief Stride of this field associated to index 'i'
idx_t Field::stride( idx_t i ) const {
    return get()->stride( i );
}

/// @brief Number of values stored in this field
size_t Field::size() const {
    return get()->size();
}

/// @brief Rank of field
idx_t Field::rank() const {
    return get()->rank();
}

/// @brief Number of bytes occupied by the values of this field
size_t Field::bytes() const {
    return get()->bytes();
}

bool Field::contiguous() const {
    return array().contiguous();
}

/// @brief Output information of field plus raw data
void Field::dump( std::ostream& os ) const {
    get()->dump( os );
}

/// Metadata that is more intrinsic to the Field, and queried often
void Field::set_levels( idx_t n ) {
    get()->set_levels( n );
}
idx_t Field::levels() const {
    return get()->levels();
}

/// Metadata that is more intrinsic to the Field, and queried often
void Field::set_variables( idx_t n ) {
    get()->set_variables( n );
}
idx_t Field::variables() const {
    return get()->variables();
}

void Field::set_functionspace( const FunctionSpace& functionspace ) {
    get()->set_functionspace( functionspace );
}
const FunctionSpace& Field::functionspace() const {
    return get()->functionspace();
}

/// @brief Return the memory footprint of the Field
size_t Field::footprint() const {
    return get()->footprint();
}

bool Field::dirty() const {
    return get()->dirty();
}

void Field::set_dirty( bool value ) const {
    return get()->set_dirty( value );
}

void Field::haloExchange( bool on_device ) const {
    get()->haloExchange( on_device );
}

void Field::adjointHaloExchange( bool on_device ) const {
    get()->adjointHaloExchange( on_device );
}

// -- Methods related to host-device synchronisation, requires gridtools_storage

void Field::updateHost() const {
    get()->updateHost();
}
void Field::updateDevice() const {
    get()->updateDevice();
}
void Field::syncHostDevice() const {
    get()->syncHostDevice();
}
bool Field::hostNeedsUpdate() const {
    return get()->hostNeedsUpdate();
}
bool Field::deviceNeedsUpdate() const {
    return get()->deviceNeedsUpdate();
}
void Field::reactivateDeviceWriteViews() const {
    get()->reactivateDeviceWriteViews();
}
void Field::reactivateHostWriteViews() const {
    get()->reactivateHostWriteViews();
}

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

template Field::Field( const std::string&, float*, const array::ArraySpec& );
template Field::Field( const std::string&, float*, const array::ArrayShape& );
template Field::Field( const std::string&, double*, const array::ArraySpec& );
template Field::Field( const std::string&, double*, const array::ArrayShape& );
template Field::Field( const std::string&, long*, const array::ArraySpec& );
template Field::Field( const std::string&, long*, const array::ArrayShape& );
template Field::Field( const std::string&, int*, const array::ArraySpec& );
template Field::Field( const std::string&, int*, const array::ArrayShape& );

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


}  // namespace atlas
