using System;
using System.Diagnostics;

namespace Spark3D.Algebra3D
{
    public class Vector3D
    {
        private double[] data = new double[3];

        public Vector3D()
        { }

        public Vector3D(double x, double y, double z)
        {
            data = new double[] { x, y, z };
        }

        public Vector3D(double[] _data)
        {
            data = _data;
        }
        public void Fill(double val)
        {
            for (int i = 0; i < 3; i++)
                this[i] = val;
        }

        public void PrintMe()
        {
            Console.WriteLine("[{0:0.00}, {1:0.00}, {2:0.00}]\n", this[0], this[1], this[2]);
        }
        public double this[int i]
        {
            get { return data[i]; }
            set { data[i] = value; }
        }

        public double Magnitude()
        {
            return Math.Sqrt(data[0] * data[0] + data[1] * data[1] + data[2] * data[2]);
        }

        public void Normalize()
        {
            double m = Magnitude();
            Debug.Assert(m > 0.0);
            data[0] /= m;
            data[1] /= m;
            data[2] /= m;
        }
        // Vector dot product
        public static double operator *(Vector3D a, Vector3D b)
        {
            double ret = 0;
            for (int i = 0; i < 3; i++)
                ret += a[i] * b[i];

            return ret;
        }

        public static Vector3D Cross(Vector3D a, Vector3D b)
        {
            return new Vector3D(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
        }

        /// <summary>
        /// Scalar product
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static Vector3D operator *(Vector3D a, double b)
        {
            return new Vector3D(a[0] * b, a[1] * b, a[2] * b);
        }
        public static Vector3D operator /(Vector3D a, double b)
        {
            return new Vector3D(a[0] / b, a[1] / b, a[2] / b);
        }


        public static Vector3D operator +(Vector3D a, Vector3D b)
        {
            Vector3D ret = new Vector3D();
            for (int i = 0; i < 3; i++)
                ret[i] += a[i] + b[i];

            return ret;
        }
        public static Vector3D operator -(Vector3D a, Vector3D b)
        {
            return new Vector3D(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
        }


        public static Vector3D Sin(Vector3D t)
        {
            return new Vector3D(Math.Sin(t[0]), Math.Sin(t[1]), Math.Sin(t[2]));
        }
        public static Vector3D Cos(Vector3D t)
        {
            return new Vector3D(Math.Cos(t[0]), Math.Cos(t[1]), Math.Cos(t[2]));
        }
        public static Vector3D DegreesToRadians(Vector3D deg)
        {
            return deg * (Math.PI / 180.0);
        }

        //
        //  Create a Cartesian vector from 3D polar coordinates.
        //
        public static Vector3D FromPolarCoordinates(double r, double theta, double phi)
        {
            return new Vector3D
                    (
                    r * Math.Sin(phi) * Math.Cos(theta),
                    r * Math.Sin(phi) * Math.Sin(theta),
                    r * Math.Cos(phi)
                    );
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="polar">(r,theta,phi), angles in degrees</param>
        /// <returns></returns>
        public static Vector3D FromPolarCoordinates(Vector3D polar)
        {
            return FromPolarCoordinates(polar[0], polar[1] * Math.PI / 180.0, polar[2] * Math.PI / 180.0);
        }
    }
}
