using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mafia2Tool.Parsed
{
    // ONLY rotation + translation, no scale
    public struct Matrix44
    {
        public float M11;
        public float M12;
        public float M13;
        public float M14;

        public float M21;
        public float M22;
        public float M23;
        public float M24;

        public float M31;
        public float M32;
        public float M33;
        public float M34;

        public float OffsetX;
        public float OffsetY;
        public float OffsetZ;
        public float M44;

        public Matrix44(Matrix33 rot)
            : this()
        {
            this = Identity;
            M11 = rot.M11;
            M12 = rot.M12;
            M13 = rot.M13;
            M21 = rot.M21;
            M22 = rot.M22;
            M23 = rot.M23;
            M31 = rot.M31;
            M32 = rot.M32;
            M33 = rot.M33;
        }

        public Matrix44(Vector3 trans)
            : this()
        {
            this = Identity;
            OffsetX = trans.X;
            OffsetY = trans.Y;
            OffsetZ = trans.Z;
        }

        public Matrix44(Matrix33 rot, Vector3 trans)
            : this()
        {
            M11 = rot.M11;
            M12 = rot.M12;
            M13 = rot.M13;
            M21 = rot.M21;
            M22 = rot.M22;
            M23 = rot.M23;
            M31 = rot.M31;
            M32 = rot.M32;
            M33 = rot.M33;
            OffsetX = trans.X;
            OffsetY = trans.Y;
            OffsetZ = trans.Z;
            M44 = 1.0f;
        }

        public Matrix44(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float offsetX, float offsetY, float offsetZ, float m44)
            : this()
        {
            M11 = m11; M12 = m12; M13 = m13; M14 = m14;
            M21 = m21; M22 = m22; M23 = m23; M24 = m24;
            M31 = m31; M32 = m32; M33 = m33; M34 = m34;
            OffsetX = offsetX; OffsetY = offsetY; OffsetZ = offsetZ; M44 = m44;
        }

        public static Matrix44 Identity
        {
            get { return new Matrix44(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
        }

        public static Matrix44 operator *(Matrix44 m1, Matrix44 m2)
        {
            return new Matrix44(
                (((m1.M11     * m2.M11) + (m1.M12     * m2.M21)) + (m1.M13     * m2.M31)) + (m1.M14 * m2.OffsetX),
                (((m1.M11     * m2.M12) + (m1.M12     * m2.M22)) + (m1.M13     * m2.M32)) + (m1.M14 * m2.OffsetY),
                (((m1.M11     * m2.M13) + (m1.M12     * m2.M23)) + (m1.M13     * m2.M33)) + (m1.M14 * m2.OffsetZ),
                (((m1.M11     * m2.M14) + (m1.M12     * m2.M24)) + (m1.M13     * m2.M34)) + (m1.M14 * m2.M44),
                (((m1.M21     * m2.M11) + (m1.M22     * m2.M21)) + (m1.M23     * m2.M31)) + (m1.M24 * m2.OffsetX),
                (((m1.M21     * m2.M12) + (m1.M22     * m2.M22)) + (m1.M23     * m2.M32)) + (m1.M24 * m2.OffsetY),
                (((m1.M21     * m2.M13) + (m1.M22     * m2.M23)) + (m1.M23     * m2.M33)) + (m1.M24 * m2.OffsetZ),
                (((m1.M21     * m2.M14) + (m1.M22     * m2.M24)) + (m1.M23     * m2.M34)) + (m1.M24 * m2.M44),
                (((m1.M31     * m2.M11) + (m1.M32     * m2.M21)) + (m1.M33     * m2.M31)) + (m1.M34 * m2.OffsetX),
                (((m1.M31     * m2.M12) + (m1.M32     * m2.M22)) + (m1.M33     * m2.M32)) + (m1.M34 * m2.OffsetY),
                (((m1.M31     * m2.M13) + (m1.M32     * m2.M23)) + (m1.M33     * m2.M33)) + (m1.M34 * m2.OffsetZ),
                (((m1.M31     * m2.M14) + (m1.M32     * m2.M24)) + (m1.M33     * m2.M34)) + (m1.M34 * m2.M44),
                (((m1.OffsetX * m2.M11) + (m1.OffsetY * m2.M21)) + (m1.OffsetZ * m2.M31)) + (m1.M44 * m2.OffsetX),
                (((m1.OffsetX * m2.M12) + (m1.OffsetY * m2.M22)) + (m1.OffsetZ * m2.M32)) + (m1.M44 * m2.OffsetY),
                (((m1.OffsetX * m2.M13) + (m1.OffsetY * m2.M23)) + (m1.OffsetZ * m2.M33)) + (m1.M44 * m2.OffsetZ),
                (((m1.OffsetX * m2.M14) + (m1.OffsetY * m2.M24)) + (m1.OffsetZ * m2.M34)) + (m1.M44 * m2.M44)
             );
        }

        /// <summary>
        /// invert matrix, ignore scale (affine matrix)
        /// </summary>
        /// <returns>false if not invertable</returns>
        public bool InvertRotTransAffine()
        {
            float num01 = (M22 * M33) - (M32 * M23);
            float num02 = (M32 * M13) - (M12 * M33);
            float num03 = (M12 * M23) - (M22 * M13);

            float det = ((M31 * num03) + (M21 * num02)) + (M11 * num01);
            if (det == 0)
            {
                return false;
            }
            
            float num04 = (M31 * M23) - (M21 * M33);
            float num05 = (M11 * M33) - (M31 * M13);
            float num06 = (M21 * M13) - (M11 * M23);

            float num07 = (M21 * OffsetY) - (OffsetX * M22);
            float num08 = (M21 * M32) - (M31 * M22);
            float num09 = (M31 * OffsetY) - (OffsetX * M32);

            float num10 = (M11 * OffsetY) - (OffsetX * M12);
            float num11 =  (M11 * M32) - (M31 * M12);
            float num12 = (M11 * M22) - (M21 * M12);
            
            float num13 = num08;
            float num14 = -num11;
            float num15 = num12;

            float num16 = ((M33 * num07) - (OffsetZ * num08)) - (M23 * num09);
            float num17 = ((M13 * num09) - (M33 * num10)) + (OffsetZ * num11);
            float num18 = ((M23 * num10) - (OffsetZ * num12)) - (M13 * num07);

            float det_1 = 1.0f / det;
            
            M11 = num01 * det_1;
            M12 = num02 * det_1;
            M13 = num03 * det_1;
            
            M21 = num04 * det_1;
            M22 = num05 * det_1;
            M23 = num06 * det_1;
            
            M31 = num13 * det_1;
            M32 = num14 * det_1;
            M33 = num15 * det_1;
            
            OffsetX = num16 * det_1;
            OffsetY = num17 * det_1;
            OffsetZ = num18 * det_1;

            return true;
        }

        public void Transpose()
        {
            Matrix44 m = new Matrix44(
                M11, M21, M31, OffsetX,
                M12, M22, M32, OffsetY,
                M13, M23, M33, OffsetZ,
                M14, M24, M34, M44);
            this = m;
        }

        public override string ToString()
        {
            return string.Format(
                System.Globalization.CultureInfo.InvariantCulture,
                "{0:0.000000} {1:0.000000} {2:0.000000} {3:0.000000} {4:0.000000} {5:0.000000} {6:0.000000} {7:0.000000} {8:0.000000} {9:0.000000} {10:0.000000} {11:0.000000} {12:0.000000} {13:0.000000} {14:0.000000} {15:0.000000}",
                M11, M12, M13, M14,
                M21, M22, M23, M24,
                M31, M32, M33, M34,
                OffsetX, OffsetY, OffsetZ, M44);
        }
    }
}
