#ifndef __JVERTEX3D__
#define __JVERTEX3D__

#include <istream>
#include <ostream>

#include "JMath/JMath.hh"
#include "JPhysics/JConstants.hh"
#include "JGeometry3D/JPosition3D.hh"
#include "JGeometry3D/JTime.hh"
#include "JGeometry3D/JAxis3D.hh"
#include "JIO/JSerialisable.hh"


/**
 * \author mdejong
 */

namespace JGEOMETRY3D {}
namespace JPP { using namespace JGEOMETRY3D; }

namespace JGEOMETRY3D {

  using JIO::JReader;
  using JIO::JWriter;
  using JMATH::JMath;
    

  /**
   * 3D vertex.
   */
  class JVertex3D : 
    public JPosition3D, 
    public JTime,
    public JMath<JVertex3D>
  {
  public:

    using JPosition3D::add;
    using JPosition3D::sub;
    using JTime::add;
    using JTime::sub;
    using JTime::getT;


    /**
     * Default constructor.
     */
    JVertex3D() :
      JPosition3D(),
      JTime()
    {}


    /**
     * Constructor.
     *
     * \param  pos        position
     * \param  t          time at position
     */
    JVertex3D(const JVector3D& pos,
	      const double     t) :
      JPosition3D(pos),
      JTime(t)
    {}


    /**
     * Prefix unary minus.
     *
     * \return                  line
     */
    JVertex3D& negate()
    {
      JPosition3D::negate();
      JTime      ::negate();

      return *this;
    }

    /**
     * Addition operator.
     *
     * \param  value            line
     * \return                  line
     */
    JVertex3D& add(const JVertex3D& value)
    {
      JPosition3D::add(value);
      JTime      ::add(value);

      return *this;
    }


    /**
     * Subtraction operator.
     *
     * \param  value            line
     * \return                  line
     */
    JVertex3D& sub(const JVertex3D& value)
    {
      JPosition3D::sub(value);
      JTime      ::sub(value);

      return *this;
    }


    /**
     * Multiplication operator.
     *
     * \param  value            multiplication factor
     * \return                  line
     */
    JVertex3D& mul(const double value)
    {
      JPosition3D::mul(value);
      JTime      ::mul(value);

      return *this;
    }


    /**
     * Division operator.
     *
     * \param  value            multiplication factor
     * \return                  line
     */
    JVertex3D& div(const double value)
    {
      JPosition3D::div(value);
      JTime      ::div(value);

      return *this;
    }


    /**
     * Get arrival time of Cherenkov light at given position.
     *
     * \param  pos              position [m]
     * \return                  time     [ns]
     */
    inline double getT(const JVector3D& pos) const
    {
      using namespace JPHYSICS;

      return this->getT() + this->getDistance(pos) * getInverseSpeedOfLight() * getIndexOfRefraction();
    }


    /**
     * Get photon direction of Cherenkov light on PMT.
     *
     * \param  pos              PMT position
     * \return                  direction
     */
    inline JVersor3D getDirection(const JVector3D& pos) const
    {
      JPosition3D D(pos);

      D.sub(this->getPosition());

      return JVersor3D(D);
    }


    /**
     * Get cosine angle of impact of Cherenkov light on PMT.
     *
     * \param  axis             PMT axis
     * \return                  cosine angle of impact
     */
    inline double getDot(const JAxis3D& axis) const
    {
      return getDirection(axis.getPosition()).getDot(axis.getDirection());
    }


    /**
     * Read vertex from input.
     *
     * \param  in            input stream
     * \param  vertex        vertex
     * \return               input stream
     */
    friend inline std::istream& operator>>(std::istream& in, JVertex3D& vertex)
    {
      in >> static_cast<JPosition3D&>(vertex);
      in >> static_cast<JTime&>      (vertex);

      return in;
    }


    /**
     * Write vertex to output.
     *
     * \param  out           output stream
     * \param  vertex        vertex
     * \return               output stream
     */
    friend inline std::ostream& operator<<(std::ostream& out, const JVertex3D& vertex)
    {
      out << static_cast<const JPosition3D&>(vertex);
      out << ' ';
      out << static_cast<const JTime&>      (vertex);

      return out;
    }


    /**
     * Read vertex from input.
     *
     * \param  in            reader
     * \param  vertex        vertex
     * \return               reader
     */
    friend inline JReader& operator>>(JReader& in, JVertex3D& vertex)
    {
      in >> static_cast<JPosition3D&>(vertex);
      in >> static_cast<JTime&>      (vertex);

      return in;
    }


    /**
     * Write vertex to output.
     *
     * \param  out           writer
     * \param  vertex        vertex
     * \return               writer
     */
    friend inline JWriter& operator<<(JWriter& out, const JVertex3D& vertex)
    {
      out << static_cast<const JPosition3D&>(vertex);
      out << static_cast<const JTime&>      (vertex);

      return out;
    }
  };
}

#endif