#ifndef MASK_H
#define MASK_H

/*
 *  mask.h
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "nest.h"
#include "topology_names.h"
#include "position.h"
#include "dictdatum.h"
#include "dictutils.h"
#include "exceptions.h"
#include "topologymodule.h"

namespace nest
{
  class TopologyModule;

  /**
   * Abstract base class for masks with unspecified dimension.
   */
  class AbstractMask
  {
  public:
    /**
     * Virtual destructor
     */
    virtual ~AbstractMask()
      {}

    /**
     * @returns true if point is inside mask.
     */
    virtual bool inside(const std::vector<double_t> &) const = 0;

    /**
     * @returns a dictionary with the definition for this mask.
     */
    virtual DictionaryDatum get_dict() const
      { throw KernelException("Can not convert mask to dict"); }

    /**
     * Create the intersection of this mask with another. Masks must have
     * the same dimension
     * @returns a new dynamically allocated mask.
     */
    virtual AbstractMask * intersect_mask(const AbstractMask & other) const = 0;

    /**
     * Create the union of this mask with another. Masks must have the same
     * dimension.
     * @returns a new dynamically allocated mask.
     */
    virtual AbstractMask * union_mask(const AbstractMask & other) const = 0;

    /**
     * Create the difference of this mask and another. Masks must have the
     * same dimension.
     * @returns a new dynamically allocated mask.
     */
    virtual AbstractMask * minus_mask(const AbstractMask & other) const = 0;

  };

  typedef lockPTRDatum<AbstractMask, &TopologyModule::MaskType> MaskDatum;

  /**
   * Abstract base class for masks with given dimension.
   */
  template<int D>
  class Mask : public AbstractMask
  {
  public:
    ~Mask()
      {}

    /**
     * @returns true if point is inside mask.
     */
    virtual bool inside(const Position<D> &) const = 0;

    /**
     * @returns true if point is inside mask.
     */
    bool inside(const std::vector<double_t> &pt) const;

    /**
     * @returns true if the whole box is inside the mask.
     * @note a return value of false is not a guarantee that the whole box
     * is not inside the mask.
     */
    virtual bool inside(const Box<D> &) const = 0;

    /**
     * @returns true if the whole box is outside the mask.
     * @note a return value of false is not a guarantee that the whole box
     * is not outside the mask.
     */
    virtual bool outside(const Box<D> &b) const;

    /**
     * The whole mask is inside (i.e., false everywhere outside) the bounding box.
     * @returns bounding box
     */
    virtual Box<D> get_bbox() const = 0;

    /**
     * Clone method.
     * @returns dynamically allocated copy of mask object
     */
    virtual Mask * clone() const = 0;

    AbstractMask * intersect_mask(const AbstractMask & other) const;
    AbstractMask * union_mask(const AbstractMask & other) const;
    AbstractMask * minus_mask(const AbstractMask & other) const;
  };

  /**
   * Mask which covers all of space
   */
  template<int D>
  class AllMask : public Mask<D>
  {
  public:

    ~AllMask()
      {}

    using Mask<D>::inside;

    /**
     * @returns true always for this mask.
     */
    bool inside(const Position<D> &) const
      { return true; }

    /**
     * @returns true always for this mask
     */
    bool inside(const Box<D> &) const
      { return true; }

    /**
     * @returns false always for this mask
     */
    bool outside(const Box<D> &) const
      { return false; }

    Box<D> get_bbox() const
      {
        const double inf = std::numeric_limits<double>::infinity();
        return Box<D>(Position<D>(-inf,-inf), Position<D>(inf,inf));
      }

    Mask<D> * clone() const
      { return new AllMask(); }
  };

  /**
   * Mask defining a box region.
   */
  template<int D>
  class BoxMask : public Mask<D>
  {
  public:

    /**
     * Parameters that should be in the dictionary:
     * lower_left  - Position of lower left corner (array of doubles)
     * upper_right - Position of upper right corner (array of doubles)
     */
    BoxMask(const DictionaryDatum&);

    BoxMask(const Position<D>& lower_left, const Position<D>& upper_right);

    ~BoxMask()
      {}

    using Mask<D>::inside;

    /**
     * @returns true if point is inside the box
     */
    bool inside(const Position<D> &p) const;

    /**
     * @returns true if the whole given box is inside this box
     */
    bool inside(const Box<D> &b) const;

    /**
     * @returns true if the whole given box is outside this box
     */
    bool outside(const Box<D> &b) const;

    Box<D> get_bbox() const;

    DictionaryDatum get_dict() const;

    Mask<D> * clone() const;

    /**
     * @returns the name of this mask type.
     */
    static Name get_name();

  protected:
    Position<D> lower_left_;
    Position<D> upper_right_;
  };

  /**
   * Mask defining a circular or spherical region.
   */
  template<int D>
  class BallMask : public Mask<D>
  {
  public:
    /**
     * @param center Center of sphere
     * @param radius Radius of sphere
     */
    BallMask(Position<D> center, double_t radius) :
      center_(center), radius_(radius)
      {}

    /**
     * Creates a BallMask from a Dictionary which should contain the key
     * "radius" with a double value and optionally the key "anchor" (the
     * center position) with an array of doubles.
     */
    BallMask(const DictionaryDatum&);

    ~BallMask()
      {}

    using Mask<D>::inside;

    /**
     * @returns true if point is inside the circle
     */
    bool inside(const Position<D> &p) const;

    /**
     * @returns true if the whole box is inside the circle
     */
    bool inside(const Box<D> &) const;

    /**
     * @returns true if the whole box is outside the circle
     */
    bool outside(const Box<D> &b) const;

    Box<D> get_bbox() const;

    DictionaryDatum get_dict() const;

    Mask<D> * clone() const;

    /**
     * @returns the name of this mask type.
     */
    static Name get_name();

  protected:
    Position<D> center_;
    double_t radius_;
  };

  /**
   * Mask combining two masks with a Boolean AND, the intersection.
   */
  template<int D>
  class IntersectionMask : public Mask<D> {
  public:

    /**
     * Construct the intersection of the two given masks. Copies are made
     * of the supplied Mask objects.
     */
    IntersectionMask(const Mask<D> &m1, const Mask<D> &m2):
      mask1_(m1.clone()), mask2_(m2.clone())
      {}

    /**
     * Copy constructor
     */
    IntersectionMask(const IntersectionMask &m):
      Mask<D>(m), mask1_(m.mask1_->clone()), mask2_(m.mask2_->clone())
      {}

    ~IntersectionMask()
      { delete mask1_; delete mask2_; }

    bool inside(const Position<D> &p) const;

    bool inside(const Box<D> &b) const;

    bool outside(const Box<D> &b) const;

    Box<D> get_bbox() const;

    Mask<D> * clone() const;

  protected:
    Mask<D> *mask1_, *mask2_;
  };

  /**
   * Mask combining two masks with a Boolean OR, the sum.
   */
  template<int D>
  class UnionMask : public Mask<D> {
  public:

    /**
     * Construct the union of the two given masks. Copies are made
     * of the supplied Mask objects.
     */
    UnionMask(const Mask<D> &m1, const Mask<D> &m2):
      mask1_(m1.clone()), mask2_(m2.clone())
      {}

    /**
     * Copy constructor
     */
    UnionMask(const UnionMask &m):
      Mask<D>(m), mask1_(m.mask1_->clone()), mask2_(m.mask2_->clone())
      {}

    ~UnionMask()
      { delete mask1_; delete mask2_; }

    bool inside(const Position<D> &p) const;

    bool inside(const Box<D> &b) const;

    bool outside(const Box<D> &b) const;

    Box<D> get_bbox() const;

    Mask<D> * clone() const;

  protected:
    Mask<D> *mask1_, *mask2_;
  };

  /**
   * Mask combining two masks with a minus operation, the difference.
   */
  template<int D>
  class DifferenceMask : public Mask<D> {
  public:

    /**
     * Construct the difference of the two given masks. Copies are made
     * of the supplied Mask objects.
     */
    DifferenceMask(const Mask<D> &m1, const Mask<D> &m2):
      mask1_(m1.clone()), mask2_(m2.clone())
      {}

    /**
     * Copy constructor
     */
    DifferenceMask(const DifferenceMask &m):
      Mask<D>(m), mask1_(m.mask1_->clone()), mask2_(m.mask2_->clone())
      {}

    ~DifferenceMask()
      { delete mask1_; delete mask2_; }

    bool inside(const Position<D> &p) const;

    bool inside(const Box<D> &b) const;

    bool outside(const Box<D> &b) const;

    Box<D> get_bbox() const;

    Mask<D> * clone() const;

  protected:
    Mask<D> *mask1_, *mask2_;
  };


  /**
   * Mask oriented in the opposite direction.
   */
  template<int D>
  class ConverseMask : public Mask<D> {
  public:
    /**
     * Construct the converse of the two given mask. A copy is made of the
     * supplied Mask object.
     */
    ConverseMask(const Mask<D> &m): m_(m.clone()) {}

    /**
     * Copy constructor
     */
    ConverseMask(const ConverseMask &m): Mask<D>(m), m_(m.m_->clone()) {}

    ~ConverseMask()
      { delete m_; }

    bool inside(const Position<D> &p) const;

    bool inside(const Box<D> &b) const;

    bool outside(const Box<D> &b) const;

    Box<D> get_bbox() const;

    Mask<D> * clone() const;

  protected:
    Mask<D> *m_;
  };


  /**
   * Mask shifted by an anchor
   */
  template<int D>
  class AnchoredMask : public Mask<D> {
  public:
    /**
     * Construct the converse of the two given mask. A copy is made of the
     * supplied Mask object.
     */
    AnchoredMask(const Mask<D> &m, Position<D> anchor): m_(m.clone()), anchor_(anchor) {}

    /**
     * Copy constructor
     */
    AnchoredMask(const AnchoredMask &m): Mask<D>(m), m_(m.m_->clone()), anchor_(m.anchor_) {}

    ~AnchoredMask()
      { delete m_; }

    bool inside(const Position<D> &p) const;

    bool inside(const Box<D> &b) const;

    bool outside(const Box<D> &b) const;

    Box<D> get_bbox() const;

    DictionaryDatum get_dict() const;

    Mask<D> * clone() const;

  protected:
    Mask<D> *m_;
    Position<D> anchor_;
  };

  template<>
  inline
  Name BoxMask<2>::get_name()
  {
    return names::rectangular;
  }

  template<>
  inline
  Name BoxMask<3>::get_name()
  {
    return names::box;
  }

  template<int D>
  BoxMask<D>::BoxMask(const DictionaryDatum& d)
  {
    lower_left_ = getValue<std::vector<double_t> >(d, names::lower_left);
    upper_right_ = getValue<std::vector<double_t> >(d, names::upper_right);
  }

  template<int D>
  inline
  BoxMask<D>::BoxMask(const Position<D>& lower_left, const Position<D>&upper_right):
    lower_left_(lower_left), upper_right_(upper_right)
  {}
  
  template<>
  inline
  Name BallMask<2>::get_name()
  {
    return names::circular;
  }

  template<>
  inline
  Name BallMask<3>::get_name()
  {
    return names::spherical;
  }

  template<int D>
  BallMask<D>::BallMask(const DictionaryDatum& d)
  {
    radius_ = getValue<double_t>(d, names::radius);
    if (d->known(names::anchor)) {
      center_ = getValue<std::vector<double_t> >(d, names::anchor);
    }
  }

} // namespace nest

#endif