# -*- coding: utf-8 -*-
"""
This file is part of FElupe.

FElupe 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 3 of the License, or
(at your option) any later version.

FElupe 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 FElupe.  If not, see <http://www.gnu.org/licenses/>.
"""

import numpy as np


class BazantOh:
    r"""Quadrature scheme for a numeric integration of the surface of a unit sphere
    [1]_.

    Parameters
    ----------
    n : int, optional
        The number of quadrature points (default is 21).

    Notes
    -----
    The approximation is given by

    ..  math::

        \int_{\partial V} f(x) dA \approx \sum f(x_q) w_q

    with quadrature points :math:`x_q` and corresponding weights :math:`w_q`.

    Examples
    --------
    >>> import felupe as fem
    >>>
    >>> quadrature = fem.BazantOh(n=21)

    References
    ----------
    .. [1] Bazant, Z. P., & Oh, B. H. (1986). Efficient Numerical Integration on
       the Surface of a Sphere. ZAMM ‐ Journal of Applied Mathematics and
       Mechanics / Zeitschrift für Angewandte Mathematik und Mechanik, 66(1),
       37-49. https://doi.org/10.1002/zamm.19860660108
    """

    def __init__(self, n: int = 21):
        schemes = {
            21: self._scheme_21,
        }
        self.points, self.weights = schemes[n]()

    def _scheme_21(self):
        "2x21-point scheme (degree 9, orthogonal symmetries)."

        a = np.sqrt(2) / 2
        b = 0.836095596749
        c = 0.387907304067

        points = np.array(
            [
                [0, 0, 1],
                [0, 1, 0],
                [1, 0, 0],
                [0, a, a],
                [0, -a, a],
                [a, 0, a],
                [-a, 0, a],
                [a, a, 0],
                [-a, a, 0],
                [b, c, c],
                [-b, c, c],
                [b, -c, c],
                [-b, -c, c],
                [c, b, c],
                [-c, b, c],
                [c, -b, c],
                [-c, -b, c],
                [c, c, b],
                [-c, c, b],
                [c, -c, b],
                [-c, -c, b],
            ]
        )

        w1 = 0.0265214244093
        w2 = 0.0199301476312
        w3 = 0.0250712367487

        weights = 2 * np.concatenate(
            [np.repeat(w1, 3), np.repeat(w2, 6), np.repeat(w3, 12)]
        )

        return points, weights
