/*
<<<<<<< HEAD
 * (C) Copyright 1996- ECMWF.
=======
 * (C) Copyright 2013 ECMWF.
>>>>>>> develop
 *
 * 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 <cmath>

#include "eckit/types/FloatCompare.h"
#include "eckit/utils/MD5.h"

#include "atlas/array.h"
#include "atlas/field.h"
#include "atlas/functionspace/NodeColumns.h"
#include "atlas/grid.h"
#include "atlas/interpolation.h"
#include "atlas/mesh/Mesh.h"
#include "atlas/meshgenerator.h"
#include "atlas/option.h"
#include "atlas/util/Config.h"

#include "tests/AtlasTestEnvironment.h"

using atlas::functionspace::NodeColumns;
using atlas::util::Config;

namespace atlas {
namespace test {

class Access {
public:
    Access( const Interpolation& interpolation ) : interpolation_{interpolation} {}
    const Interpolation::Implementation::Matrix& matrix() const { return *interpolation_.get()->matrix_; }
    Interpolation interpolation_;

    std::string hash() {
        eckit::MD5 hash;
        const auto& m     = matrix();
        const auto outer  = m.outer();
        const auto index  = m.inner();
        const auto weight = m.data();

        idx_t rows = static_cast<idx_t>( m.rows() );
        hash.add( rows );
        for ( idx_t r = 0; r < rows; ++r ) {
            //v_tgt( r ) = 0.;
            for ( idx_t c = outer[r]; c < outer[r + 1]; ++c ) {
                hash.add( c );
                idx_t n = index[c];
                hash.add( n );
                //Value w = static_cast<Value>( weight[c] );
                //v_tgt( r ) += w * v_src( n );
            }
        }
        return hash.digest();
    }
};


CASE( "test_interpolation_k_nearest_neighbours" ) {
    Log::info().precision( 16 );

    Grid gridA( "O32" );
    Grid gridB( "O64" );
    Grid gridC( "O32" );

    Interpolation a( option::type( "k-nearest-neighbours" ), gridA, gridB );
    Interpolation b( option::type( "k-nearest-neighbours" ), gridB, gridC );
    ATLAS_DEBUG_VAR( Access{a}.hash() );
    ATLAS_DEBUG_VAR( Access{b}.hash() );

    // Following EXPECT are commented because they are not bit-identical across platforms.
    // This indicates that not every platform finds the same nearest neighbours!!!
    //EXPECT( Access{a}.hash() == "5ecc9d615dcf7112b5a97b38341099a5" );
    //EXPECT( Access{b}.hash() == "bf4b4214ef50387ba00a1950b95e0d93" );
}

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

CASE( "test_multiple_fs" ) {
    Grid grid1( "L90x45" );
    Grid grid2( "O8" );

    Mesh mesh1 = StructuredMeshGenerator().generate( grid1 );
    Mesh mesh2 = StructuredMeshGenerator().generate( grid2 );

    functionspace::NodeColumns fs11( mesh1, option::halo( 1 ) );
    functionspace::NodeColumns fs12( mesh1, option::halo( 2 ) );

    auto fs1 = fs11;
    functionspace::NodeColumns fs2( mesh2, option::halo( 1 ) );

    Interpolation interpolation12( Config( "type", "k-nearest-neighbours" ) | Config( "k-nearest-neighbours", 5 ), fs1,
                                   fs2 );

    auto f1 = fs1.createField<double>( Config( "name", "source" ) );
    auto f2 = fs2.createField<double>( Config( "name", "target" ) );

    auto v1 = array::make_view<double, 1>( f1 );
    v1.assign( 1. );

    interpolation12.execute( f1, f2 );
}

}  // namespace test
}  // namespace atlas

int main( int argc, char** argv ) {
    return atlas::test::run( argc, argv );
}
