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

#pragma once

#include <string>

#include "eckit/config/LocalConfiguration.h"
#include "eckit/config/Parametrisation.h"

#include "atlas/library/config.h"

namespace atlas {
namespace util {

class Metadata : public eckit::LocalConfiguration {
public:
    Metadata() : eckit::LocalConfiguration() {}

    Metadata( const eckit::LocalConfiguration& p ) : eckit::LocalConfiguration( p ) {}
    Metadata( const Metadata& p ) : eckit::LocalConfiguration( p ) {}

    using eckit::LocalConfiguration::set;

    template <typename ValueT>
    Metadata& set( const std::string& name, const ValueT& value ) {
        eckit::LocalConfiguration::set( name, value );
        return *this;
    }

    using eckit::LocalConfiguration::get;

    template <typename ValueT>
    ValueT get( const std::string& name ) const {
        ValueT value;
        if ( not eckit::LocalConfiguration::get( name, value ) )
            throw_not_found( name );
        return value;
    }

    friend std::ostream& operator<<( std::ostream& s, const Metadata& v ) {
        v.print( s );
        return s;
    }

    void broadcast();
    void broadcast( idx_t root );
    void broadcast( Metadata& );
    void broadcast( Metadata&, idx_t root );
    void broadcast( Metadata& ) const;
    void broadcast( Metadata&, idx_t root ) const;

    size_t footprint() const;

private:
    [[noreturn]] void throw_not_found( const std::string& ) const;

    Metadata( const eckit::Value& );
};

// ------------------------------------------------------------------
// C wrapper interfaces to C++ routines

extern "C" {
Metadata* atlas__Metadata__new();

void atlas__Metadata__delete( Metadata* This );
void atlas__Metadata__print( Metadata* This, std::ostream* channel );
void atlas__Metadata__json( Metadata* This, char*& json, int& size, int& allocated );
int atlas__Metadata__has( Metadata* This, const char* name );
void atlas__Metadata__set_int( Metadata* This, const char* name, int value );
void atlas__Metadata__set_long( Metadata* This, const char* name, long value );
void atlas__Metadata__set_float( Metadata* This, const char* name, float value );
void atlas__Metadata__set_double( Metadata* This, const char* name, double value );
void atlas__Metadata__set_string( Metadata* This, const char* name, const char* value );
void atlas__Metadata__set_array_int( Metadata* This, const char* name, int value[], int size );
void atlas__Metadata__set_array_long( Metadata* This, const char* name, long value[], int size );
void atlas__Metadata__set_array_float( Metadata* This, const char* name, float value[], int size );
void atlas__Metadata__set_array_double( Metadata* This, const char* name, double value[], int size );

int atlas__Metadata__get_int( Metadata* This, const char* name );
long atlas__Metadata__get_long( Metadata* This, const char* name );
float atlas__Metadata__get_float( Metadata* This, const char* name );
double atlas__Metadata__get_double( Metadata* This, const char* name );
void atlas__Metadata__get_string( Metadata* This, const char* name, char* output_str, int max_len );
void atlas__Metadata__get_array_int( Metadata* This, const char* name, int*& value, int& size, int& allocated );
void atlas__Metadata__get_array_long( Metadata* This, const char* name, long*& value, int& size, int& allocated );
void atlas__Metadata__get_array_float( Metadata* This, const char* name, float*& value, int& size, int& allocated );
void atlas__Metadata__get_array_double( Metadata* This, const char* name, double*& value, int& size, int& allocated );
}

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

}  // namespace util
}  // namespace atlas
