using System;
using System.Collections.Generic;
using System.Text;
using nft.core.geometry;
using nft.framework.drawing;

namespace nft.core.geometry {
    public struct Vect3D {

        public double X;
        public double Y;
        public double Z;

        public Vect3D(double x, double y, double z) {
            X = x;
            Y = y;
            Z = z;
        }

        public Vect3D(Vect3D org) {
            this.X = org.X;
            this.Y = org.Y;
            this.Z = org.Z;
        }

        /// <summary>
        /// To make unit vector.
        /// </summary>
        public void Normalize() {
            double v = X * X + Y * Y + Z * Z;
            v = Math.Sqrt(v);
            X /= v;
            Y /= v;
            Z /= v;
        }
        /// <summary>
        /// Calculate azimuth angle(方位角). North is zero.
        /// </summary>
        public double Azimuth {
            get {
                return (Math.Atan2(Y, X) / Math.PI) * 180;
            }
        }

        /// <summary>
        /// Calculate elevation angle(仰角).
        /// </summary>
        public double Elevation {
            get {
                if (X == 0 && Y == 0) {
                    return 0;
                } else {
                    double x2 = X * X;
                    double y2 = Y * Y;
                    double r = Math.Asin((x2 + y2) / Math.Sqrt(x2 + y2) / Math.Sqrt(x2 + y2 + Z * Z));
                    return (r / Math.PI) * 180;
                }
            }
        }

        public void Multiple(double v) {
            X *= v;
            Y *= v;
            Z *= v;
        }

        /// <summary>
        /// Calculate inner product(内積) with other vector
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public double InnerProduct(Vect3D other) {
            return X * other.X + Y * other.Y + Z * other.Z;
        }

        public static Vect3D CreateUnitVector(double x, double y, double z) {
            Vect3D v = new Vect3D(x, y, z);
            v.Normalize();
            return v;
        }

        public static Vect3D operator *(Vect3D v, double d){
            Vect3D ret = new Vect3D(v);
            ret.Multiple(d);
            return ret;
        }

        /// <summary>
        /// Get normal vector for the plane contains 3 location on it.
        /// </summary>
        /// <param name="pos">Only first three locations are used for calculation.</param>
        /// <returns></returns>
        public static Vect3D CalcNormalVector(Point3D[] pos) {
            Vect3D normal = new Vect3D();
            normal.X = (pos[2].Y - pos[1].Y) * (pos[0].Z - pos[2].Z) - (pos[0].Y - pos[2].Y) * (pos[2].Z - pos[1].Z);
            normal.Y = (pos[2].Z - pos[1].Z) * (pos[0].X - pos[2].X) - (pos[0].Z - pos[2].Z) * (pos[2].X - pos[1].X);
            normal.Z = (pos[2].X - pos[1].X) * (pos[0].Y - pos[2].Y) - (pos[0].X - pos[2].X) * (pos[2].Y - pos[1].Y);
            normal.Normalize();
            return normal;
        }
    }
}
