/*
 * (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 "atlas/library/config.h"

#if ATLAS_HAVE_EIGEN

#define EIGEN_NO_AUTOMATIC_RESIZING
//#define EIGEN_DONT_ALIGN
//#define EIGEN_DONT_VECTORIZE

#include <Eigen/Core>
#include <Eigen/Dense>

#else

#include <cmath>
#include <iosfwd>

#endif

namespace atlas {
namespace interpolation {

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

#if ATLAS_HAVE_EIGEN

typedef Eigen::Vector3d Vector3D;

#else

class Vector3D {
public:
    Vector3D( const double* d ) {
        xyz_[0] = d[0];
        xyz_[1] = d[1];
        xyz_[2] = d[2];
    }

    Vector3D( double x, double y, double z ) {
        xyz_[0] = x;
        xyz_[1] = y;
        xyz_[2] = z;
    }

    Vector3D() {
        // Warning, data_ is uninitialised
    }

    static Vector3D Map( const double* data ) { return Vector3D( data ); }

    // Operators

    double operator[]( size_t i ) const { return xyz_[i]; }

    // Vector3D operator*(const Vector3D &) const;
    Vector3D operator-( const Vector3D& other ) const {
        return Vector3D( x() - other.x(), y() - other.y(), z() - other.z() );
    }

    Vector3D operator+( const Vector3D& other ) const {
        return Vector3D( x() + other.x(), y() + other.y(), z() + other.z() );
    }

    Vector3D operator-() const { return Vector3D( -x(), -y(), -z() ); }

    Vector3D operator/( double a ) const { return Vector3D( x() / a, y() / a, z() / a ); }

    Vector3D operator*( double a ) const { return Vector3D( x() * a, y() * a, z() * a ); }

    double norm() const { return std::sqrt( squaredNorm() ); }

    double squaredNorm() const { return x() * x() + y() * y() + z() * z(); }

    double dot( const Vector3D& other ) const { return x() * other.x() + y() * other.y() + z() * other.z(); }

    Vector3D cross( const Vector3D& other ) const {
        return Vector3D( y() * other.z() - z() * other.y(), z() * other.x() - x() * other.z(),
                         x() * other.y() - y() * other.x() );
    }

    void print( std::ostream& s ) const;

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

    double* data() { return xyz_; }

    const double* data() const { return xyz_; }

private:
    double x() const { return xyz_[0]; }
    double y() const { return xyz_[1]; }
    double z() const { return xyz_[2]; }
    double xyz_[3];
};

Vector3D operator*( double, const Vector3D& );

#endif

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

}  // namespace interpolation
}  // namespace atlas
