using System;

namespace SunflowSharp.Maths
{

    public class OrthoNormalBasis
    {
        private Vector3 u, v, w;

        private OrthoNormalBasis()
        {
            u = new Vector3();
            v = new Vector3();
            w = new Vector3();
        }

        public void flipU()
        {
            u.negate();
        }

        public void flipV()
        {
            v.negate();
        }

        public void flipW()
        {
            w.negate();
        }

        public void swapUV()
        {
            Vector3 t = u;
            u = v;
            v = t;
        }

        public void swapVW()
        {
            Vector3 t = v;
            v = w;
            w = t;
        }

        public void swapWU()
        {
            Vector3 t = w;
            w = u;
            u = t;
        }

        public Vector3 transform(Vector3 a, Vector3 dest)
        {
            dest.x = (a.x * u.x) + (a.y * v.x) + (a.z * w.x);
            dest.y = (a.x * u.y) + (a.y * v.y) + (a.z * w.y);
            dest.z = (a.x * u.z) + (a.y * v.z) + (a.z * w.z);
            return dest;
        }

        public Vector3 transform(Vector3 a)
        {
            float x = (a.x * u.x) + (a.y * v.x) + (a.z * w.x);
            float y = (a.x * u.y) + (a.y * v.y) + (a.z * w.y);
            float z = (a.x * u.z) + (a.y * v.z) + (a.z * w.z);
            return a.set(x, y, z);
        }

        public Vector3 untransform(Vector3 a, Vector3 dest)
        {
            dest.x = Vector3.dot(a, u);
            dest.y = Vector3.dot(a, v);
            dest.z = Vector3.dot(a, w);
            return dest;
        }

        public Vector3 untransform(Vector3 a)
        {
            float x = Vector3.dot(a, u);
            float y = Vector3.dot(a, v);
            float z = Vector3.dot(a, w);
            return a.set(x, y, z);
        }

        public float untransformX(Vector3 a)
        {
            return Vector3.dot(a, u);
        }

        public float untransformY(Vector3 a)
        {
            return Vector3.dot(a, v);
        }

        public float untransformZ(Vector3 a)
        {
            return Vector3.dot(a, w);
        }

        public static OrthoNormalBasis makeFromW(Vector3 w)
        {
            OrthoNormalBasis onb = new OrthoNormalBasis();
            w.normalize(onb.w);
            if ((Math.Abs(onb.w.x) < Math.Abs(onb.w.y)) && (Math.Abs(onb.w.x) < Math.Abs(onb.w.z)))
            {
                onb.v.x = 0;
                onb.v.y = onb.w.z;
                onb.v.z = -onb.w.y;
            }
            else if (Math.Abs(onb.w.y) < Math.Abs(onb.w.z))
            {
                onb.v.x = onb.w.z;
                onb.v.y = 0;
                onb.v.z = -onb.w.x;
            }
            else
            {
                onb.v.x = onb.w.y;
                onb.v.y = -onb.w.x;
                onb.v.z = 0;
            }
            Vector3.cross(onb.v.normalize(), onb.w, onb.u);
            return onb;
        }

        public static OrthoNormalBasis makeFromWV(Vector3 w, Vector3 v)
        {
            OrthoNormalBasis onb = new OrthoNormalBasis();
            w.normalize(onb.w);
            Vector3.cross(v, onb.w, onb.u).normalize();
            Vector3.cross(onb.w, onb.u, onb.v);
            return onb;
        }
    }
}