using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Xml;
using FxpConvert.Common;

namespace NIFPGA.lvbitx
{
    public static class Parse
    {
        public static Bitfile ParseFile(string path)
        {
            XmlDocument document = new XmlDocument();
            document.PreserveWhitespace = false;
            document.Load(path);
            Bitfile bitfile = new Bitfile();
            bitfile.FilePath = path;
            var bitfilenode = document.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Bitfile");
            if (bitfilenode == null) return bitfile;
            bitfile.BitfileVersion = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Bitfile.BitfileVersion))?.InnerText;
            bitfile.SignatureRegister = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Bitfile.SignatureRegister))?.InnerText;
            bitfile.SignatureGuids = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Bitfile.SignatureGuids))?.InnerText;
            bitfile.SignatureNames = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Bitfile.SignatureNames))?.InnerText;
            bitfile.BitstreamVersion = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Bitfile.BitstreamVersion))?.InnerText;
            var vinode = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Bitfile.VI));
            if (vinode != null)
            {
                bitfile.VI.Name = vinode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(VI.Name))?.InnerText;
                var reglistnode = vinode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "RegisterList")?.ChildNodes.Cast<XmlNode>().Where(x => x.Name == "Register")?.ToList();
                if (reglistnode != null)
                {
                    reglistnode.ForEach(x =>
                    {
                        Register register = new Register();
                        register.Name = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.Name))?.InnerText;
                        string s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.Hidden))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.Hidden = bool.Parse(s);
                        }
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.Indicator))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.Indicator = bool.Parse(s);
                        }
                        var datatypenode = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.Datatype));
                        if (datatypenode != null && datatypenode.ChildNodes.Count > 0)
                        {
                            var name = datatypenode.ChildNodes[0].Name;
                            var index = Enum.GetValues<Datatype>().ToList().FindIndex(x =>
                            {
                                var datatype = x.GetType().GetField(x.ToString()).GetCustomAttribute<DataTypeAttribute>();
                                if (datatype == null) return false;
                                return datatype.Name.Contains(name);
                            });
                            if (index >= 0)
                            {
                                register.Datatype = Enum.GetValues<Datatype>()[index];
                                if (register.Datatype == Datatype.Array)
                                {
                                    s = datatypenode.ChildNodes[0].ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Size")?.InnerText;
                                    if (!string.IsNullOrEmpty(s))
                                    {
                                        register.Size = uint.Parse(s);
                                    }
                                    var typenode = datatypenode.ChildNodes[0].ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Type");
                                    if (typenode != null)
                                    {
                                        name = typenode.ChildNodes[0].Name;
                                        index = Enum.GetValues<Datatype>().ToList().FindIndex(x =>
                                        {
                                            var datatype = x.GetType().GetField(x.ToString()).GetCustomAttribute<DataTypeAttribute>();
                                            if (datatype == null) return false;
                                            return datatype.Name.Contains(name);
                                        });
                                        if (index >= 0)
                                        {
                                            register.ArrayValueType = Enum.GetValues<Datatype>()[index];
                                            if (register.ArrayValueType == Datatype.FXP)
                                            {
                                                typenode = typenode.ChildNodes[0];
                                                register.FxpTypeInfo.Name = typenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(NiFpga_FxpTypeInfo.Name))?.InnerText;
                                                s = typenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Signed")?.InnerText;
                                                if (!string.IsNullOrEmpty(s))
                                                {
                                                    register.FxpTypeInfo.isSigned = bool.Parse(s);
                                                }
                                                s = typenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "WordLength")?.InnerText;
                                                if (!string.IsNullOrEmpty(s))
                                                {
                                                    register.FxpTypeInfo.wordLength = byte.Parse(s);
                                                }
                                                s = typenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "IntegerWordLength")?.InnerText;
                                                if (!string.IsNullOrEmpty(s))
                                                {
                                                    register.FxpTypeInfo.integerWordLength = short.Parse(s);
                                                }
                                                s = typenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Minimum")?.InnerText;
                                                if (!string.IsNullOrEmpty(s))
                                                {
                                                    register.FxpTypeInfo.Min = double.Parse(s);
                                                }
                                                s = typenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Maximum")?.InnerText;
                                                if (!string.IsNullOrEmpty(s))
                                                {
                                                    register.FxpTypeInfo.Max = double.Parse(s);
                                                }
                                                s = typenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Delta")?.InnerText;
                                                if (!string.IsNullOrEmpty(s))
                                                {
                                                    register.FxpTypeInfo.Delta = double.Parse(s);
                                                }
                                            }
                                        }
                                    }
                                }
                                else if (register.Datatype == Datatype.FXP)
                                {
                                    s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault().ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Delta")?.InnerText;
                                    if (!string.IsNullOrEmpty(s))
                                    {
                                        register.FxpTypeInfo.Delta = double.Parse(s);
                                    }
                                    s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault().ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Maximum")?.InnerText;
                                    if (!string.IsNullOrEmpty(s))
                                    {
                                        register.FxpTypeInfo.Max = double.Parse(s);
                                    }
                                    s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault().ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Minimum")?.InnerText;
                                    if (!string.IsNullOrEmpty(s))
                                    {
                                        register.FxpTypeInfo.Min = double.Parse(s);
                                    }
                                    s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault().ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "IntegerWordLength")?.InnerText;
                                    if (!string.IsNullOrEmpty(s))
                                    {
                                        register.FxpTypeInfo.integerWordLength = short.Parse(s);
                                    }
                                    s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault().ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Signed")?.InnerText;
                                    if (!string.IsNullOrEmpty(s))
                                    {
                                        register.FxpTypeInfo.isSigned = bool.Parse(s);
                                    }
                                    s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault().ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "WordLength")?.InnerText;
                                    if (!string.IsNullOrEmpty(s))
                                    {
                                        register.FxpTypeInfo.wordLength = byte.Parse(s);
                                    }
                                }
                            }
                        }
                        register.FlattenedType = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.FlattenedType))?.InnerText;
                        register.MechanicalAction = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.MechanicalAction))?.InnerText;
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.Offset))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.Offset = uint.Parse(s);
                        }
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.SizeInBits))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.SizeInBits = uint.Parse(s);
                        }
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.Class))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.Class = uint.Parse(s);
                        }
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.ID))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.ID = uint.Parse(s);
                        }
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.Internal))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.Internal = bool.Parse(s);
                        }
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.Bidirectional))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.Bidirectional = bool.Parse(s);
                        }
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.Synchronous))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.Synchronous = bool.Parse(s);
                        }
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.AccessMayTimeout))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.AccessMayTimeout = bool.Parse(s);
                        }
                        s = x.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Register.RegisterNode))?.InnerText;
                        if (!string.IsNullOrEmpty(s))
                        {
                            register.RegisterNode = bool.Parse(s);
                        }
                        if (string.IsNullOrEmpty(register.Name)) return;
                        bitfile.VI.Registers.Add(register);
                    });
                }
            }
            bitfilenode = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Project");
            if(bitfilenode!=null)
            {
                bitfile.Project.TargetClass = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Project.TargetClass))?.InnerText;
                string s = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(Project.AutoRunWhenDownloaded))?.InnerText;
                if (!string.IsNullOrEmpty(s))
                {
                    bitfile.Project.AutoRunWhenDownloaded = bool.Parse(s);
                }
                bitfilenode = bitfilenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "CompilationResultsTree")?.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "CompilationResults")?.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "NiFpga")?.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "DmaChannelAllocationList");
                if (bitfilenode!=null)
                {
                    var nodelist = bitfilenode.ChildNodes.Cast<XmlNode>().Where(x => x.Name == "Channel")?.ToList();
                    if(nodelist!=null && nodelist.Count>0)
                    {
                        foreach (XmlNode node in nodelist)
                        {
                            DMA dma = new DMA();
                            dma.Name = node.Attributes.Cast<XmlAttribute>().FirstOrDefault(x => x.Name == "name")?.InnerText;
                            dma.BaseAddressTag = node.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(DMA.BaseAddressTag))?.InnerText;
                            s = node.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(DMA.ControlSet))?.InnerText;
                            if(!string.IsNullOrEmpty(s))
                            {
                                dma.ControlSet = uint.Parse(s);
                            }
                            var datatypenode = node.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "DataType");
                            if(datatypenode!=null)
                            {
                                s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "SubType")?.InnerText;
                                int index = Enum.GetValues<Datatype>().ToList().FindIndex(x =>
                                {
                                    var attribute = x.GetType().GetField(x.ToString()).GetCustomAttribute<DataTypeAttribute>();
                                    if (attribute == null) return false;
                                    return attribute.Name.Contains(s);
                                });
                                if(index>=0)
                                {
                                    dma.Datatype = Enum.GetValues<Datatype>()[index];
                                    if(dma.Datatype == Datatype.FXP)
                                    {
                                        s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Delta")?.InnerText;
                                        if(!string.IsNullOrEmpty(s))
                                        {
                                            dma.FxpTypeInfo.Delta = double.Parse(s);
                                        }
                                        s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Maximum")?.InnerText;
                                        if (!string.IsNullOrEmpty(s))
                                        {
                                            dma.FxpTypeInfo.Max = double.Parse(s);
                                        }
                                        s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Minimum")?.InnerText;
                                        if (!string.IsNullOrEmpty(s))
                                        {
                                            dma.FxpTypeInfo.Min = double.Parse(s);
                                        }
                                        s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "IntegerWordLength")?.InnerText;
                                        if (!string.IsNullOrEmpty(s))
                                        {
                                            dma.FxpTypeInfo.integerWordLength = short.Parse(s);
                                        }
                                        s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "Signed")?.InnerText;
                                        if (!string.IsNullOrEmpty(s))
                                        {
                                            dma.FxpTypeInfo.isSigned = bool.Parse(s);
                                        }
                                        s = datatypenode.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == "WordLength")?.InnerText;
                                        if (!string.IsNullOrEmpty(s))
                                        {
                                            dma.FxpTypeInfo.wordLength = byte.Parse(s);
                                        }
                                    }
                                }
                            }
                            dma.Direction = node.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(DMA.Direction))?.InnerText;
                            dma.Implementation = node.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(DMA.Implementation))?.InnerText;
                            s = node.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(DMA.Number))?.InnerText;
                            if(!string.IsNullOrEmpty(s))
                            {
                                dma.Number = int.Parse(s);
                            }
                            s = node.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(DMA.NumberOfElements))?.InnerText;
                            if (!string.IsNullOrEmpty(s))
                            {
                                dma.NumberOfElements = uint.Parse(s);
                            }
                            s = node.ChildNodes.Cast<XmlNode>().FirstOrDefault(x => x.Name == nameof(DMA.UserVisible))?.InnerText;
                            if (!string.IsNullOrEmpty(s))
                            {
                                dma.UserVisible = bool.Parse(s);
                            }
                            bitfile.Project.DMA.Add(dma);
                        }
                    }
                }
            }
            return bitfile;
        }
    }
}
