using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Mafia2Tool.FileFormats
{
    public class MaterialLibrary
    {
        public class ShaderParameter
        {
            public string fourcc; 
            float[] data;

            public ShaderParameter(BinaryReader r)
            {
                fourcc = Encoding.ASCII.GetString(r.ReadBytes(4));
                int len = r.ReadInt32();
                System.Diagnostics.Debug.Assert(len % 4 == 0);

                int numValues = len / 4;

                data = new float[numValues];
                for (int i = 0; i < numValues; i++)
                {
                    data[i] = r.ReadSingle();
                }
            }

            public override string ToString()
            {
                List<string> tmp = new List<string>();
                for (int i = 0; i < data.Length; i++) {
                    tmp.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000}", data[i]));
                }
                string value = string.Join(",", tmp.ToArray());
                return string.Format("{0} = [{1}]", fourcc, value);
            }
        }

        public class ShaderParameterSampler
        {
            public string fourcc;

            int unk1; // 0
            int unk2; // 0
            public ulong TextureHash;

            byte[] unk5; // [16] ... sampler state ???
            /*
            [0] = 0, 2, 3, 4
            [1] = 0
            [2] = 1, 2, 3
            [3] = 1, 2, 3
            [4] = 1, 2
            [5] = 0, 1, 2
            [6] = 0, 1, 2
            [7] = 0, 1, 2
            [8 - 11] = 0 - 0xff
            [12 - 15] = 0
            */

            public string TextureFilename;

            public ShaderParameterSampler(BinaryReader r)
            {
                fourcc = Encoding.ASCII.GetString(r.ReadBytes(4));
                
                unk1 = r.ReadInt32();
                unk2 = r.ReadInt32();
                TextureHash = r.ReadUInt64();
                unk5 = r.ReadBytes(16);

                int len = r.ReadInt32();
                byte[] tmp = r.ReadBytes(len);
                TextureFilename = Encoding.ASCII.GetString(tmp);
            }

            public override string ToString()
            {
                return string.Format("{0} = \"{1}\"", fourcc, TextureFilename);
            }
        }

        public class Material
        { 
            public ulong Hash; // really 2 ints...
            public string Name;

            byte unk1; // 0 - 255
            byte unk2; // 0, 1, 50, 100
            int unk3;
            byte unk4;
            int unk5;
            int unk6;

            public ulong ShaderHash; // shader hash
            public int unk11; // hash?

            public ShaderParameter[] Parameters;
            public ShaderParameterSampler[] Samplers;

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

            public void Read(BinaryReader r)
            {
                Hash = r.ReadUInt64();
                int len = r.ReadInt32();
                byte[] tmp = r.ReadBytes(len);
                Name = Encoding.ASCII.GetString(tmp);

                unk1 = r.ReadByte();
                unk2 = r.ReadByte();
                unk3 = r.ReadInt32();
                unk4 = r.ReadByte();
                unk5 = r.ReadInt32();
                unk6 = r.ReadInt32();

                ShaderHash = r.ReadUInt64();
                unk11 = r.ReadInt32();

                int count = r.ReadInt32();
                Parameters = new ShaderParameter[count];
                for (int i = 0; i < count; i++) Parameters[i] = new ShaderParameter(r);

                count = r.ReadInt32();
                Samplers = new ShaderParameterSampler[count];
                for (int i = 0; i < count; i++) Samplers[i] = new ShaderParameterSampler(r);
            }

            public override string ToString()
            {
                return string.Format("{0}, Params:{1}, Samplers:{2}", Name, Parameters.Length, Samplers.Length);
            }
        }

        public Dictionary<ulong, Material> Materials;
        
        public MaterialLibrary(Stream input)
        {
            Read(input);
        }

        public void Read(Stream input)
        {
            BinaryReader r = new BinaryReader(input);
            if (r.ReadInt32() != 1112298573)
            {
                throw new NotSupportedException("not a material library");
            }

            int version = r.ReadInt32();
            if (version != 57)
            {
                throw new NotSupportedException("unknown version");            
            }
            int count = r.ReadInt32();

            r.ReadInt32(); // 0

            Materials = new Dictionary<ulong, Material>(count);

            for (int i = 0; i < count; i++)
            {
                Material m = new Material(r);
                Materials.Add(m.Hash, m);
            }
        }
    }
}
