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

#include "atlas/field/Field.h"
#include "atlas/field/FieldSet.h"
#include "atlas/functionspace/FunctionSpace.h"
#include "atlas/interpolation/Interpolation.h"
#include "atlas/interpolation/method/MethodFactory.h"
#include "atlas/runtime/Exception.h"

namespace atlas {

Interpolation::Interpolation( const Config& config, const FunctionSpace& source, const FunctionSpace& target ) :
    Handle( [&]() -> Implementation* {
        std::string type;
        ATLAS_ASSERT( config.get( "type", type ) );
        Implementation* impl = interpolation::MethodFactory::build( type, config );
        impl->setup( source, target );
        return impl;
    }() ) {
    std::string path;
    if ( config.get( "output", path ) ) {
        std::ofstream file( path );
        print( file );
    }
}

Interpolation::Interpolation( const Config& config, const Grid& source, const Grid& target ) :
    Handle( [&]() -> Implementation* {
        std::string type;
        ATLAS_ASSERT( config.get( "type", type ) );
        Implementation* impl = interpolation::MethodFactory::build( type, config );
        impl->setup( source, target );
        return impl;
    }() ) {
    std::string path;
    if ( config.get( "output", path ) ) {
        std::ofstream file( path );
        print( file );
    }
}

Interpolation::Interpolation( const Config& config, const FunctionSpace& source, const Field& target ) :
    Handle( [&]() -> Implementation* {
        std::string type;
        ATLAS_ASSERT( config.get( "type", type ) );
        Implementation* impl = interpolation::MethodFactory::build( type, config );
        impl->setup( source, target );
        return impl;
    }() ) {
    std::string path;
    if ( config.get( "output", path ) ) {
        std::ofstream file( path );
        print( file );
    }
}

Interpolation::Interpolation( const Interpolation::Config& config, const FunctionSpace& source,
                              const FieldSet& target ) :
    Handle( [&]() -> Implementation* {
        std::string type;
        ATLAS_ASSERT( config.get( "type", type ) );
        Implementation* impl = interpolation::MethodFactory::build( type, config );
        impl->setup( source, target );
        return impl;
    }() ) {
    std::string path;
    if ( config.get( "output", path ) ) {
        std::ofstream file( path );
        print( file );
    }
}

void Interpolation::execute( const FieldSet& source, FieldSet& target ) const {
    get()->execute( source, target );
}

void Interpolation::execute( const Field& source, Field& target ) const {
    get()->execute( source, target );
}

void Interpolation::print( std::ostream& out ) const {
    get()->print( out );
}

const FunctionSpace& Interpolation::source() const {
    return get()->source();
}

const FunctionSpace& Interpolation::target() const {
    return get()->target();
}

Interpolation::Cache Interpolation::createCache() const {
    return get()->createCache();
}

Interpolation::Interpolation( const Interpolation::Config& config, const Grid& source, const Grid& target,
                              const Interpolation::Cache& cache ) :
    Handle( [&]() -> Implementation* {
        std::string type;
        ATLAS_ASSERT( config.get( "type", type ) );
        Implementation* impl = interpolation::MethodFactory::build( type, config );
        impl->setup( source, target, cache );
        return impl;
    }() ) {}

extern "C" {
Interpolation::Implementation* atlas__Interpolation__new( const eckit::Parametrisation* config,
                                                          const functionspace::FunctionSpaceImpl* source,
                                                          const functionspace::FunctionSpaceImpl* target ) {
    Interpolation::Implementation* interpolator;
    {
        Interpolation im( *config, FunctionSpace( source ), FunctionSpace( target ) );
        interpolator = const_cast<Interpolation::Implementation*>( im.get() );
        interpolator->attach();
    }
    interpolator->detach();
    return interpolator;
}

Interpolation::Implementation* atlas__Interpolation__new_tgt_field( const eckit::Parametrisation* config,
                                                                    const functionspace::FunctionSpaceImpl* source,
                                                                    const field::FieldImpl* target ) {
    Interpolation::Implementation* interpolator;
    {
        Interpolation im( *config, FunctionSpace( source ), Field( target ) );
        interpolator = const_cast<Interpolation::Implementation*>( im.get() );
        interpolator->attach();
    }
    interpolator->detach();
    return interpolator;
}

Interpolation::Implementation* atlas__Interpolation__new_tgt_fieldset( const eckit::Parametrisation* config,
                                                                       const functionspace::FunctionSpaceImpl* source,
                                                                       const field::FieldSetImpl* target ) {
    Interpolation::Implementation* interpolator;
    {
        Interpolation im( *config, FunctionSpace( source ), FieldSet( target ) );
        interpolator = const_cast<Interpolation::Implementation*>( im.get() );
        interpolator->attach();
    }
    interpolator->detach();
    return interpolator;
}

void atlas__Interpolation__delete( Interpolation::Implementation* This ) {
    delete This;
}

void atlas__Interpolation__execute_field( Interpolation::Implementation* This, const field::FieldImpl* source,
                                          field::FieldImpl* target ) {
    Field t( target );
    This->execute( Field( source ), t );
}

void atlas__Interpolation__execute_fieldset( Interpolation::Implementation* This, const field::FieldSetImpl* source,
                                             field::FieldSetImpl* target ) {
    FieldSet t( target );
    This->execute( FieldSet( source ), t );
}

}  // extern "C"

}  // namespace atlas
