/*
 ==============================================================================
 This file is part of the IEM plug-in suite.
 Author: Daniel Rudrich
 Copyright (c) 2018 - Institute of Electronic Music and Acoustics (IEM)
 https://iem.at

 The IEM plug-in suite 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.

 The IEM plug-in suite 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 this software.  If not, see <https://www.gnu.org/licenses/>.
 ==============================================================================
 */

#include <JuceHeader.h>
#pragma once

template <typename Type>
class Conversions
{
public:
    static Type radiansToDegrees (Type radians)
    {
        return radians * (Type (180) / Type (3.14159265358979323846264338327950288));
    }

    static Type degreesToRadians (Type degrees)
    {
        return degrees * (Type (3.14159265358979323846264338327950288) / Type (180));
    }

    static void cartesianToSpherical (const Type x,
                                      const Type y,
                                      const Type z,
                                      Type& azimuthInRadians,
                                      Type& elevationInRadians,
                                      Type& radius)
    {
        const float xSquared = x * x;
        const float ySquared = y * y;
        radius = sqrt (xSquared + ySquared + z * z);
        azimuthInRadians = atan2 (y, x);
        elevationInRadians = atan2 (z, sqrt (xSquared + ySquared));
    }

    static void cartesianToSpherical (const Type x,
                                      const Type y,
                                      const Type z,
                                      Type& azimuthInRadians,
                                      Type& elevationInRadians)
    {
        const float xSquared = x * x;
        const float ySquared = y * y;
        azimuthInRadians = atan2 (y, x);
        elevationInRadians = atan2 (z, sqrt (xSquared + ySquared));
    }

    static void cartesianToSpherical (const juce::Vector3D<Type> cartesian,
                                      Type& azimuthInRadians,
                                      Type& elevationInRadians)
    {
        cartesianToSpherical (cartesian.x,
                              cartesian.y,
                              cartesian.z,
                              azimuthInRadians,
                              elevationInRadians);
    }

    static juce::Vector3D<Type> cartesianToSpherical (juce::Vector3D<Type> cartvect)
    {
        const Type r = cartvect.length();
        return juce::Vector3D<Type> (
            r, // radius
            juce::radiansToDegrees (atan2 (cartvect.y, cartvect.x)), // azimuth
            juce::radiansToDegrees (
                atan2 (cartvect.z,
                       sqrt (cartvect.x * cartvect.x + cartvect.y * cartvect.y))) // elevation
        );
    }

    static juce::Vector3D<Type> sphericalToCartesian (const Type azimuthInRadians,
                                                      const Type elevationInRadians)
    {
        juce::Vector3D<Type> cartesian;
        sphericalToCartesian (azimuthInRadians,
                              elevationInRadians,
                              cartesian.x,
                              cartesian.y,
                              cartesian.z);
        return cartesian;
    }

    static juce::Vector3D<Type> sphericalToCartesian (const Type azimuthInRadians,
                                                      const Type elevationInRadians,
                                                      const Type radius)
    {
        return sphericalToCartesian (azimuthInRadians, elevationInRadians) * radius;
    }

    static void sphericalToCartesian (const Type azimuthInRadians,
                                      const Type elevationInRadians,
                                      const Type radius,
                                      Type& x,
                                      Type& y,
                                      Type& z)
    {
        Type _x;
        Type _y;
        Type _z;
        sphericalToCartesian (azimuthInRadians, elevationInRadians, _x, _y, _z);
        x = radius * _x;
        y = radius * _y;
        z = radius * _z;
    }

    static void sphericalToCartesian (const Type azimuthInRadians,
                                      const Type elevationInRadians,
                                      Type& x,
                                      Type& y,
                                      Type& z)
    {
        const Type cosElevation = cos (elevationInRadians);
        x = cosElevation * cos (azimuthInRadians);
        y = cosElevation * sin (azimuthInRadians);
        z = sin (elevationInRadians);
    }
};
