/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
 */

#pragma once

#include "../../coord/fwd.hpp"
#include "../../extent.hpp"
#include "../../random.hpp"

#include "../ops.hpp"

#include <cstddef>
#include <random>


///////////////////////////////////////////////////////////////////////////////
namespace cruft::geom::sample {
    ///////////////////////////////////////////////////////////////////////
    /// A convenience function that calls sample::fn to select a random
    /// point in a provided shape.
    ///
    /// This function is useful because we can perform use partial
    /// specialisation if we pass the functionality off to a well known
    /// class.
    template <
        typename ShapeT,
        typename GeneratorT  // random generator
    >
    auto
    eval (ShapeT const &shape, GeneratorT &&gen)
    {
        volume query (shape);
        return query.eval (std::forward<GeneratorT> (gen));
    }


    /// A function object that selects a uniformly random point inside a
    /// shape using a provided random generator. The point will lie
    /// within the shape, inclusive of boundaries.
    ///
    /// This should be specialised for anything complex, but will work
    /// (with a varying performance hit) for anything that provides
    /// bounds` and `intersects`.
    template <typename ShapeT>
    class volume {
    public:
        using shape_type = ShapeT;

        explicit volume (shape_type&&) = delete;
        explicit volume (shape_type const &target):
            m_target (target)
        { ; }


        /// Returns a point which lies within the supplied shape, inclusive
        /// of borders.
        ///
        template <typename GeneratorT>
        auto
        eval (GeneratorT &&g) const
        {
            auto b = bounds (m_target);

            while (true) {
                auto p = ::cruft::geom::sample::eval (b, g);
                if (intersects (m_target, p))
                    return p;
            }
        }

    private:
        ShapeT const &m_target;
    };


    ///////////////////////////////////////////////////////////////////////
    /// A specialisation of the volume sampler for extents.
    template <size_t S, typename T>
    class volume<cruft::extent<S,T>> {
    public:
        using shape_type = extent<S,T>;

        explicit volume (shape_type&&) = delete;
        explicit volume (shape_type const &target):
            m_target (target)
        { ; }


        template <typename GeneratorT>
        auto
        eval (GeneratorT &&gen) const noexcept
        {
            cruft::point<S,T> p;

            for (size_t i = 0; i < S; ++i)
                p[i] = random::uniform (T{0}, m_target[i], gen);

            return p;
        }

    private:
        shape_type const &m_target;
    };
}