/*
 * 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 2019 Danny Robson <danny@nerdcruft.net>
 */

#pragma once

#include "fwd.hpp"

#include "../../extent.hpp"
#include "../../region.hpp"

namespace cruft::geom::sample {
    template <typename T>
    struct edge<cruft::extent2<T>> {
        using shape_type = cruft::extent2<T>;

        edge (shape_type _shape)
            : m_shape (_shape)
        { ; }

        template <typename GeneratorT>
        point2<T>
        eval (GeneratorT &&gen)
        {
            // Generate a point at some point along the perimeter.
            //
            // Unwind half the perimeter with length `w + h - 1`.
            // This gives us the full length of the width, and subtract one of
            // the extreme axis on the height side that is covered by the full
            // width case.
            //
            // If we're in the first portion of the perimeter then choose if
            // we're on the top/bottom randomly. And use the position as the x
            // coordinate.
            //
            // Otherwise bump the coordinate above the baseline which was
            // already covered and choose the left/right side randomly.
            //
            // TODO: avoid repeated calls into uniform. We can do this by
            //  doubling the extent we're testing and using the extra bit for
            //  the side test. But I'm lacking time right now.
            auto const [w, h] = m_shape;
            auto const len  = w + h - 1;

            auto pos = cruft::random::uniform (T{0}, len, gen);

            if (pos <= w) {
                auto const _w = pos;
                auto const _h = cruft::random::uniform (1, gen) ? h : 0;
                return { _w, _h };
            }

            pos -= w;

            auto const _w = cruft::random::uniform (1, gen) ? w : 0;
            auto const _h = pos;

            return { _w, _h };
        }


    private:
        shape_type m_shape;
    };

    template <typename T>
    struct edge<cruft::region2<T>> {
        using shape_type = cruft::region2<T>;

        edge (shape_type _shape)
            : m_shape (_shape)
        { ; }

        template <typename GeneratorT>
        decltype(auto)
        eval (GeneratorT &&gen)
        {
            return edge<cruft::extent2i> (m_shape.e).eval (
                std::forward<GeneratorT> (gen)
            ) + m_shape.p.template as<vector> ();
        }

    private:
        shape_type m_shape;
    };
}