// ---------------------------------------------------------------------
//
// Copyright (C) 2016 - 2018 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, redistribute
// it, and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// The full text of the license can be found in the file LICENSE at
// the top level of the deal.II distribution.
//
// ---------------------------------------------------------------------

#ifndef dealii_point_wrapper_h
#define dealii_point_wrapper_h

#include <deal.II/base/config.h>
#include <deal.II/base/point.h>

#include <boost/python.hpp>

DEAL_II_NAMESPACE_OPEN

namespace python
{
  class PointWrapper
  {
  public:
    /**
     * This constructor exists only so that a std::vector of PointWrapper can be
     * created. This constructor should not be used.
     */
    PointWrapper();

    /**
     * Constructor. Use a list of double to initialize the Point.
     */
    PointWrapper(boost::python::list list);

    /**
     * Constructor. Initialize PointWrapper using a Point.
     */
    template <int dim>
    PointWrapper(const Point<dim> &p);

    /**
     * Copy constructor.
     */
    PointWrapper(const PointWrapper &other);

    /**
     * Destructor.
     */
    ~PointWrapper();

    /**
     * Return the Euclidean distance of this point to the point p, i.e., the
     * l2_norm of the difference between the vectors representing the two
     * points.
     */
    double distance(const PointWrapper &p) const;

    /**
     * Return the l2_norm of the vector connecting the origin to the point.
     */
    double norm() const;

    /**
     * Return the sum of the absolute squares of all entries.
     */
    double norm_square() const;

    /**
     * Assignment operator. The dimension of the point is changed if it is
     * different than the one of @p other.
     */
    PointWrapper &operator= (const PointWrapper &other);

    /**
     * Test for inequality of two points.
     */
    bool operator!= (const PointWrapper &p) const; 

    /**
     * Test for equality of two tensors.
     */
    bool operator== (const PointWrapper &p) const;

    /**
     * Return the scalar product of the vectors representing two points.
     */
    double operator* (const PointWrapper &p) const;

    /**
     * Add an offset to a point.
     */
    PointWrapper operator+(const PointWrapper &) const;

    /**
     * Subtract two points.
     */
    PointWrapper operator-(const PointWrapper &) const;

    /**
     * The opposite point.
     */
    PointWrapper operator-() const;

    /**
     * Divide the coordinates of the point by a factor.
     */
    PointWrapper operator/(const double factor) const;

    /**
     * Multiply the coordinates of the point by a factor.
     */
    PointWrapper operator* (const double factor) const;

    /**
     * Add another point.
     */
    PointWrapper& operator+= (const PointWrapper &p);

    /**
     * Subtract another point.
     */
    PointWrapper& operator-= (const PointWrapper &p);

    /**
     * Scale the coordinates of the point by factor.
     */
    PointWrapper& operator*= (const double factor);

    /**
     * Scale the coordinates of the point by 1/factor.
     */
    PointWrapper& operator/= (const double factor);

    /**
     * Return the first component of the Point.
     */
    double get_x() const;

    /**
     * Set the first component of the Point.
     */
    void set_x(double x);

    /**
     * Return the second component of the Point.
     */
    double get_y() const;

    /**
     * Set the second component of the Point.
     */
    void set_y(double y);

    /**
     * Return the third component of the Point.
     */
    double get_z() const;

    /**
     * Set the third component of the Point.
     */
    void set_z(double z);

    /**
     * Return the dimension of the underlying Point.
     */
    int get_dim() const;

    /**
     * Return a pointer that can be casted to the underlying Point.
     */
    void *get_point();

    /**
     * Return a constant pointer that can be casted to the underlying Point.
     */
    const void *get_point() const;

  private:
    /**
     * Delete the underlying Point and free the memory.
     */
    void clear();


    /**
     * Copy @p other PointWrapper.
     */
    void copy(const PointWrapper &other);

    /**
     * Dimension of the Point.
     */
    int dim;

    /**
     * Pointer to the underlying Point object.
     */
    void *point;
  };


  //--------------------- Inline functions ----------------------//



  inline
  int PointWrapper::get_dim() const
  {
    return dim;
  }



  inline
  void *PointWrapper::get_point()
  {
    return point;
  }



  inline
  const void *PointWrapper::get_point() const
  {
    return point;
  }
}

DEAL_II_NAMESPACE_CLOSE

#endif
