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

namespace Ext.FileFormats
{
    public class RDShaderCache
    {
        public class ShaderCode
        {
            public class ParameterData
            {
                public class Parameter
                {
                    public int RegisterType; // 3 = sampler(s), 2 = constant(c)
                    public int StartRegister; // register num ?
                    public int NumRegisters; // 1,2,3,4,5,6,8,9,12,192

                    public int unknown_02_int; // 1,3,4 cols?
                    public int unknown_03_int; // 1,2,3,4 mask? (x, xy, xyz, xyzw)
                    public string Name;

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

                    public void Read(BinaryReader r)
                    {
                        int namelen = r.ReadInt32();
                        RegisterType = r.ReadInt32();

                        StartRegister = r.ReadInt32();
                        NumRegisters = r.ReadInt32();
                        unknown_02_int = r.ReadInt32();
                        unknown_03_int = r.ReadInt32();

                        Name = Encoding.ASCII.GetString(r.ReadBytes(namelen));
                    }

                    public override string ToString()
                    {
                        return string.Format("{0} {1}{2}[{3}] {4} {5}", Name, (RegisterType == 3 ? "s" : "c"), StartRegister, NumRegisters, unknown_02_int, unknown_03_int);
                    }
                }

                public Parameter[] parameters;

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

                public void Read(BinaryReader r)
                {
                    int count = r.ReadInt32();
                    parameters = new Parameter[count];
                    for (int i = 0; i < count; i++)
                    {
                        parameters[i] = new Parameter(r);
                    }
                }
            }

            public int hash; // hash
            public ShaderCache.ShaderType type;
            public ShaderCache.TargetGPU target;

            // code
            public byte[] compiledCode;

            // parameter
            ParameterData parameters;

            public int unknown_02_int; // 0x0000000c
            public int NumInstructionSlots;
            public int NumArithmetic;
            public int NumTextures;

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

            public void Read(BinaryReader r)
            {
                hash = r.ReadInt32();
                type = (ShaderCache.ShaderType)r.ReadInt32();
                target = (ShaderCache.TargetGPU)r.ReadUInt32();
                int parameterOffset = r.ReadInt32();
                int codeSize = r.ReadInt32();

                if (parameterOffset != 0)
                {
                    System.Diagnostics.Debug.Assert(parameterOffset == codeSize + 4);
                }

                compiledCode = r.ReadBytes(codeSize);

                int paramsize = r.ReadInt32();
                BinaryReader subr = new BinaryReader(new MemoryStream(r.ReadBytes(paramsize)));
                parameters = new ParameterData(subr);

                unknown_02_int = r.ReadInt32();
                NumInstructionSlots = r.ReadInt32();
                NumArithmetic = r.ReadInt32();
                NumTextures = r.ReadInt32();
            }

            public override string ToString()
            {
                return string.Format("0x{0:x8} {1} ({2}) [Inst: {3} Ar:{4} Tex:{5}]", hash, target, type, NumInstructionSlots, NumArithmetic, NumTextures);
            }
        }
        
        const int MAGIC = 0x43534452; // RDSC

        int version;

        public ShaderCode[] codes;
        public Dictionary<ShaderCache.TargetGPU, Dictionary<int, ShaderCode>> codesSorted;

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

        public void Read(BinaryReader r)
        {
            int magic = r.ReadInt32();
            version = r.ReadInt32();
            if (magic != MAGIC || version != 3)
            {
                System.Diagnostics.Debugger.Break();
            }
            
            int count = r.ReadInt32();
            int unk1 = r.ReadInt32(); // 3224

            codesSorted = new Dictionary<ShaderCache.TargetGPU, Dictionary<int, ShaderCode>>();
            codesSorted.Add(ShaderCache.TargetGPU.ATF4, new Dictionary<int, ShaderCode>());
            codesSorted.Add(ShaderCache.TargetGPU.ATI, new Dictionary<int, ShaderCode>());
            codesSorted.Add(ShaderCache.TargetGPU.NVIDIA, new Dictionary<int, ShaderCode>());
            codesSorted.Add(ShaderCache.TargetGPU.UNKNOWN, new Dictionary<int, ShaderCode>());

            codes = new ShaderCode[count];

            for (int i = 0; i < count; i++)
            {
                ShaderCode c = new ShaderCode(r);
                codes[i] = c;
                codesSorted[c.target].Add(c.hash, c);
            }
        }
    }
}
