using System;
using System.IO;
using System.Linq;

namespace Mafia2Tool.DataFormats.FrameResources
{
    // complete mesh, geo + skeleton, etc.
    public class ModelComponent : SingleMeshComponent
    {
        public class attachmentReference
        {
            FrameResourceEntryList _list;
            SkeletonEntry _skel;

            public int attachmentIndex;
            public byte jointIndex;

            public FrameResourceEntry Object
            {
                get
                { 
                    return _list.SingleOrDefault(c => c.Index == attachmentIndex); 
                }
            }

            public Hash joint
            {
                get
                {
                    return _skel.names[jointIndex];
                }
            }

            public attachmentReference(BinaryReader r, FrameResourceEntryList list, SkeletonEntry skel)
            {
                _list = list;
                _skel = skel;
                Read(r);
            }

            public void Read(BinaryReader r)
            {
                attachmentIndex = r.ReadInt32();
                jointIndex = r.ReadByte();
            }
        }

        public class unk_struct2
        {
            public class unk_struct2_1
            {
                public class unk_struct2_1_1
                {
                    public class unk_struct2_1_1_1
                    {
                        public short startIndex;    // index
                        public short numFaces;      // * 3 = num indices

                        public unk_struct2_1_1_1(BinaryReader r)
                        {
                            startIndex = r.ReadInt16();
                            numFaces = r.ReadInt16();
                        }
                    }

                    public short materialIndex;
                    public unk_struct2_1_1_1[] data;

                    public unk_struct2_1_1(BinaryReader r)
                    {
                        Read(r);
                    }

                    public void Read(BinaryReader r)
                    {
                        materialIndex = r.ReadInt16();

                        short count = r.ReadInt16();

                        data = new unk_struct2_1_1_1[count];
                        for (int i = 0; i < count; i++)
                        {
                            data[i] = new unk_struct2_1_1_1(r);
                        }
                    }
                }
                
                public unk_struct2_1_1[] data;

                public unk_struct2_1(BinaryReader r)
                {
                    Read(r);
                }

                public void Read(BinaryReader r)
                {
                    short count = r.ReadInt16();

                    data = new unk_struct2_1_1[count];
                    for (int i = 0; i < count; i++)
                    {
                        data[i] = new unk_struct2_1_1(r);
                    }
                }
            }

            public short blendIndex;
            public unk_struct2_1[] data;

            public string jointName;

            public unk_struct2(BinaryReader r)
            {
                Read(r);
            }

            public void Read(BinaryReader r)
            {
                blendIndex = r.ReadInt16();

                short count = r.ReadInt16();

                data = new unk_struct2_1[count];
                for (int i = 0; i < count; i++)
                {
                    data[i] = new unk_struct2_1(r);
                }
            }

            public override string ToString()
            {
                return string.Format("{0} {1}:[{2}]", blendIndex, jointName, data.Length);
            }
        }

        public int BlendInfoIndex;
        public int SkeletonIndex;
        public int SkeletonHierachyIndex;

        public SkeletonBlendInfo BlendInfo
        {
            get { return GetObject<SkeletonBlendInfo>(BlendInfoIndex); }
        }

        public SkeletonEntry Skeleton
        {
            get { return GetObject<SkeletonEntry>(SkeletonIndex); }
        }

        public SkeletonHierachyEntry SkeletonHierachy
        {
            get { return GetObject<SkeletonHierachyEntry>(SkeletonHierachyIndex); }
        }

        public TransformMatrix[] RestPose; // world space
        
        public TransformMatrix _unkTransform;

        public attachmentReference[] attachmentRefList;

        int unknown_28_int;
        int unknown_29_int;
 
        public unk_struct2[] unknown_30_list;
        //byte[] unknown_30_data;

        public ModelComponent(BinaryReader r, FrameResourceEntryList list)
            : base(r, list)
        {

        }

        public override void Read(BinaryReader r)
        {
            base.Read(r);

            BlendInfoIndex = r.ReadInt32();
            SkeletonIndex = r.ReadInt32();
            SkeletonHierachyIndex = r.ReadInt32();

            if (Skeleton == null)
            {
                throw new NotSupportedException("no skeleton found");
            }

            RestPose = new TransformMatrix[Skeleton.count1];
            for (int i = 0; i < RestPose.Length; i++)
            {
                RestPose[i] = new TransformMatrix(r);
            }

            _unkTransform = new TransformMatrix(r);

            int count = r.ReadInt32();
            attachmentRefList = new attachmentReference[count];
            for (int i = 0; i < count; i++)
            {
                attachmentRefList[i] = new attachmentReference(r, _list, Skeleton);
            }

            unknown_28_int = r.ReadInt32();
            unknown_29_int = r.ReadInt32();

            int unknown_30_data_size = r.ReadInt32();

            count = r.ReadInt16();
            unknown_30_list = new unk_struct2[count];

            for (int i = 0; i < count; i++)
            {
                unknown_30_list[i] = new unk_struct2(r);
            }

            /**********************/
            /* mapping to joint   */
            var lodInfo = Skeleton.lodInfo[0];

            byte[] blendindices = new byte[lodInfo.LodBlendIndexMap.Length];

            int start = 0;
            foreach (var l in BlendInfo.BlendDataToBoneIndexMaps[0].BlendIndices)
            {
                Array.Copy(l, 0, blendindices, start, l.Length);
                start += l.Length;
            }

            for (int i = 0; i < unknown_30_list.Length; i++)
            {
                int jointIndex = blendindices[unknown_30_list[i].blendIndex];
                unknown_30_list[i].jointName = Skeleton.names[jointIndex].ToString();
            }
            /**********************/

            // unknown_30_list[].data.Length * 16
            byte[] unknown_30_data = r.ReadBytes(unknown_30_data_size);
        }

        public override string ToString()
        {
            return string.Format("Model {0}", name.ToString());
        }
    }
}

