/////////////////////////////////////////////////////////////
//                                                         //
// Copyright (c) 2003-2017 by The University of Queensland //
// Centre for Geoscience Computing                         //
// http://earth.uq.edu.au/centre-geoscience-computing      //
//                                                         //
// Primary Business: Brisbane, Queensland, Australia       //
// Licensed under the Open Software License version 3.0    //
// http://www.apache.org/licenses/LICENSE-2.0              //
//                                                         //
/////////////////////////////////////////////////////////////


#ifndef ESYS_LSMPACKERGENERATORS_H
#define ESYS_LSMPACKERGENERATORS_H

#include "Foundation/vec3.h"

namespace esys
{
  namespace lsm
  {
    /**
     * Generates particles of a specified constant radius.
     */
    template <typename TmplParticle>
    class ConstRadiusGen
    {
    public:
      typedef TmplParticle Particle;

      ConstRadiusGen(double radius);

      /**
       * Returns the radius of particles generated by the
       * getParticle method.
       */
      const double &getParticleRadius() const;

      /**
       * Returns the radius of particles generated by the
       * getParticle method.
       */
      const double &getMinFitRadius() const;

      /**
       * Returns the radius of particles generated by the
       * getParticle method.
       */
      const double &getMaxFitRadius() const;

      /**
       * Returns a particle of radius this->getParticleRadius().
       *
       * @param posn The position of the returned particle.
       */
      Particle getParticle(const Vec3 &posn) const;

      /**
       * Returns a particle of radius this->getParticleRadius().
       *
       * @param posn      The position of the returned particle.
       * @param maxRadius The suggested particle radius, ignored.
       */
      Particle getParticle(const Vec3 &posn, const double &maxRadius) const;

      /**
       * Returns whether fitRadius == this->getRadius()
       */
      bool isValidFitRadius(const double &fitRadius) const;

    private:
      double m_radius;
    };

    /**
     * Base class for generators which generate particles
     * whose radius ranges between a specified minimum and maximum.
     */
    template <typename TmplParticle>
    class RangeRadiusGen
    {
    public:
      typedef TmplParticle Particle;

      RangeRadiusGen(double minFitRadius, double maxFitRadius);

      virtual ~RangeRadiusGen();
      
      /**
       * Returns the radius of particles generated by the
       * getParticle method.
       */
      const double &getMinFitRadius() const;

      /**
       * Returns the radius of particles generated by the
       * getParticle method.
       */
      const double &getMaxFitRadius() const;

      /**
       * Returns whether fitRadius == this->getRadius()
       */
      bool isValidFitRadius(const double &fitRadius) const;

    private:
      double m_minFitRadius;
      double m_maxFitRadius;
    };

    /**
     * Class for generators which generate particles
     * whose radius is from a uniform random distribution.
     */
    template <typename TmplParticle>
    class RndRadiusGen : public RangeRadiusGen<TmplParticle>
    {
    public:
      typedef RangeRadiusGen<TmplParticle> Inherited;
      typedef typename Inherited::Particle Particle;

      RndRadiusGen(
        double minFitRadius,
        double maxFitRadius
      );

      /**
       * Returns a randomly generated radius value.
       */
      double getRandomRadius() const;

      Particle getParticle(const Vec3 &posn) const;

      Particle getParticle(const Vec3 &posn, double suggestedRadius) const;

    private:
    };

    template <typename TmplGrain>
    class GrainRndRadiusGen : public RndRadiusGen<typename TmplGrain::Particle>
    {
    public:
      typedef RndRadiusGen<typename TmplGrain::Particle> Inherited;
      typedef TmplGrain                                  Grain;
      typedef typename Grain::Particle                   Particle;

      GrainRndRadiusGen(double minGrainRadius, double maxGrainRadius);

      virtual ~GrainRndRadiusGen();

      const double &getMinGrainRadius() const;

      const double &getMaxGrainRadius() const;

      virtual Grain getGrain(const Particle &p) = 0;
    };

    template <typename TmplGrain>
    class SingleParticleGrainGen : public GrainRndRadiusGen<TmplGrain>
    {
    public:
      typedef GrainRndRadiusGen<TmplGrain> Inherited;
      typedef typename Inherited::Grain    Grain;
      typedef typename Grain::Particle     Particle;

      SingleParticleGrainGen(double minGrainRadius, double maxGrainRadius);

      const double &getMinParticleRadius() const;

      const double &getMaxParticleRadius() const;

      virtual Grain getGrain(const Particle &p);
    };
  }
}

#include "Geometry/PackerGenerators.hpp"

#endif
