using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using Decal.Adapter.NetParser;
using Decal.Interop.Net;

namespace Decal.Adapter;

/// <summary>
/// Protocol Message Data
/// </summary>
public class Message : MessageStruct
{
	private static Dictionary<int, MemberParser> mRecv;

	private static Dictionary<int, MemberParser> mSend;

	private static Dictionary<string, MemberParser> mTypes;

	private int mType;

	internal MessageStruct mStruct;

	/// <summary>
	/// Message Type
	/// </summary>
	public int Type => mType;

	/// <summary>
	/// Returns the number of fields (or vector length)
	/// </summary>
	public override int Count => mStruct.Count;

	/// <summary>
	/// Returns the specified field data
	/// </summary>
	/// <param name="index">Field index</param>
	/// <returns>Field value</returns>
	public override object this[int index] => mStruct[index];

	/// <summary>
	/// Returns the specified field data
	/// </summary>
	/// <param name="name">Field name</param>
	/// <returns>Field value</returns>
	public override object this[string name] => mStruct[name];

	/// <summary>
	/// Returns the raw bytes for this field
	/// </summary>
	public override byte[] RawData => mStruct.RawData;

	/// <summary>
	/// Returns the next object in the (parent) vector
	/// </summary>
	public override object Next => mStruct.Next;

	/// <summary>
	/// Returns the parent field
	/// </summary>
	public override MessageStruct Parent => mStruct.Parent;

	internal Message()
	{
	}

	internal Message(IMessage2 msg, MessageDirection dir)
	{
		mType = BitConverter.ToInt32(msg.RawData, 0);
		mStruct = new MessageStruct(msg.RawData, 4, GetParser(mType, dir));
	}

	internal Message(byte[] Data, MemberParser Parser)
	{
		mType = BitConverter.ToInt32(Data, 0);
		mStruct = new MessageStruct(Data, 4, Parser);
	}

	internal Message(Message Source)
	{
		mType = Source.mType;
		mStruct = Source.mStruct;
	}

	internal static MemberParser GetParser(int type, MessageDirection dir)
	{
		MemberParser result = null;
		switch (dir)
		{
		case MessageDirection.Inbound:
			if (mRecv.ContainsKey(type))
			{
				result = mRecv[type];
			}
			break;
		case MessageDirection.Outbound:
			if (mSend.ContainsKey(type))
			{
				result = mSend[type];
			}
			break;
		}
		return result;
	}

	/// <summary>
	/// Return the field name for the specified index
	/// </summary>
	/// <param name="index">Field index</param>
	/// <returns>Name of the field</returns>
	public override string Name(int index)
	{
		return mStruct.Name(index);
	}

	/// <summary>
	/// Returns the passed in value?
	/// </summary>
	/// <param name="memberName"></param>
	/// <returns></returns>
	public override string Name(string memberName)
	{
		return mStruct.Name(memberName);
	}

	/// <summary>
	/// Returns the specified child structure
	/// </summary>
	/// <param name="index">Field index</param>
	/// <returns>MessageStruct for the specified field</returns>
	public override MessageStruct Struct(int index)
	{
		return mStruct.Struct(index);
	}

	/// <summary>
	/// Returns the specified child structure
	/// </summary>
	/// <param name="name">Field name</param>
	/// <returns>MessageStruct for the specified field</returns>
	public override MessageStruct Struct(string name)
	{
		return mStruct.Struct(name);
	}

	/// <summary>
	/// Returns the specified field value
	/// </summary>
	/// <typeparam name="FieldType">Type of the field</typeparam>
	/// <param name="index">Field index</param>
	/// <returns>Field value cast to the specified FieldType</returns>
	public override FieldType Value<FieldType>(int index)
	{
		return mStruct.Value<FieldType>(index);
	}

	/// <summary>
	/// Returns the specified field value
	/// </summary>
	/// <typeparam name="FieldType">Type of the field</typeparam>
	/// <param name="name">Field name</param>
	/// <returns>Field value cast to the specified FieldType</returns>
	public override FieldType Value<FieldType>(string name)
	{
		return mStruct.Value<FieldType>(name);
	}

	/// <summary>
	/// Returns the raw bytes of the specified field
	/// </summary>
	/// <param name="index">Field index</param>
	/// <returns>Raw field value</returns>
	public override byte[] RawValue(int index)
	{
		return mStruct.RawValue(index);
	}

	/// <summary>
	/// Returns the raw bytes of the specified field
	/// </summary>
	/// <param name="name">Field name</param>
	/// <returns>Raw field value</returns>
	public override byte[] RawValue(string name)
	{
		return mStruct.RawValue(name);
	}

	internal static void Initialize(string xmlFile)
	{
		mRecv = new Dictionary<int, MemberParser>();
		mSend = new Dictionary<int, MemberParser>();
		mTypes = new Dictionary<string, MemberParser>();
		XmlDocument xmlDocument = new XmlDocument();
		xmlDocument.Load(xmlFile);
		foreach (XmlNode item in xmlDocument.SelectNodes("/schema/datatypes/type"))
		{
			ParseType(item, xmlDocument);
		}
		foreach (XmlNode item2 in xmlDocument.SelectNodes("/schema/messages/message"))
		{
			ParseMessage(item2, xmlDocument);
		}
	}

	private static void ParseType(XmlNode node, XmlDocument doc)
	{
		XmlAttribute xmlAttribute = node.Attributes["name"];
		if (xmlAttribute == null)
		{
			return;
		}
		string value = xmlAttribute.Value;
		if (mTypes.ContainsKey(value))
		{
			return;
		}
		MemberParser memberParser = new MemberParser();
		mTypes.Add(value, memberParser);
		xmlAttribute = node.Attributes["primitive"];
		if (xmlAttribute != null && string.Compare(xmlAttribute.Value, "true", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
		{
			if (string.Compare(value, "BYTE", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.BYTE;
			}
			else if (string.Compare(value, "WORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.WORD;
			}
			else if (string.Compare(value, "PackedWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.PackedWORD;
			}
			else if (string.Compare(value, "DWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.DWORD;
			}
			else if (string.Compare(value, "PackedDWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.PackedDWORD;
			}
			else if (string.Compare(value, "QWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.QWORD;
			}
			else if (string.Compare(value, "float", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.@float;
			}
			else if (string.Compare(value, "double", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.@double;
			}
			else if (string.Compare(value, "String", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.String;
			}
			else if (string.Compare(value, "WString", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser.MemberType = MemberParserType.WString;
			}
		}
		else
		{
			memberParser.MemberType = MemberParserType.Struct;
			memberParser.Child = ParseStruct(node.ChildNodes, doc);
		}
	}

	private static void ParseMessage(XmlNode node, XmlDocument doc)
	{
		XmlAttribute xmlAttribute = node.Attributes["type"];
		if (xmlAttribute == null)
		{
			return;
		}
		string text = xmlAttribute.Value;
		if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X'))
		{
			text = text.Substring(2);
		}
		int key = int.Parse(text, NumberStyles.HexNumber);
		bool flag = true;
		bool flag2 = false;
		xmlAttribute = node.Attributes["direction"];
		if (xmlAttribute != null)
		{
			if (string.Compare(xmlAttribute.Value, "outbound", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				flag = false;
				flag2 = true;
			}
			else if (string.Compare(xmlAttribute.Value, "both", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				flag2 = true;
			}
		}
		if ((flag && !mRecv.ContainsKey(key)) || (flag2 && !mSend.ContainsKey(key)))
		{
			MemberParser value = ParseStruct(node.ChildNodes, doc);
			if (flag && !mRecv.ContainsKey(key))
			{
				mRecv.Add(key, value);
			}
			if (flag2 && !mSend.ContainsKey(key))
			{
				mSend.Add(key, value);
			}
		}
	}

	private static MemberParser ParseStruct(XmlNodeList nodes, XmlDocument doc)
	{
		MemberParser memberParser = new MemberParser();
		MemberParser memberParser2 = memberParser;
		int count = nodes.Count;
		for (int i = 0; i < count; i++)
		{
			XmlNode xmlNode = nodes[i];
			if (xmlNode.NodeType != XmlNodeType.Element)
			{
				continue;
			}
			string name = xmlNode.Name;
			if (string.Compare(name, "field", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				XmlAttribute xmlAttribute = xmlNode.Attributes["type"];
				if (xmlAttribute != null)
				{
					string value = xmlAttribute.Value;
					if (!mTypes.ContainsKey(value))
					{
						ParseType(doc.SelectSingleNode("/schema/datatypes/type[@name='" + value + "']"), doc);
					}
					memberParser2.Next = new MemberParser(mTypes[value]);
					memberParser2 = memberParser2.Next;
					memberParser2.MemberName = xmlNode.Attributes["name"].Value;
				}
			}
			else if (string.Compare(name, "maskmap", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				XmlAttribute xmlAttribute = xmlNode.Attributes["xor"];
				long conditionXor = 0L;
				string value;
				if (xmlAttribute != null)
				{
					value = xmlAttribute.Value;
					if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
					{
						value = value.Substring(2);
					}
					conditionXor = long.Parse(value, NumberStyles.HexNumber);
				}
				value = xmlNode.Attributes["name"].Value;
				memberParser2.Next = ParseMaskMap(xmlNode.ChildNodes, doc);
				while (memberParser2.Next != null)
				{
					memberParser2 = memberParser2.Next;
					memberParser2.Condition = MemberParserCondition.NE;
					memberParser2.ConditionResult = 0L;
					memberParser2.ConditionField = value;
					memberParser2.ConditionXor = conditionXor;
				}
			}
			else if (string.Compare(name, "switch", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				string value = xmlNode.Attributes["name"].Value;
				XmlAttribute xmlAttribute = xmlNode.Attributes["mask"];
				long conditionXor = -1L;
				if (xmlAttribute != null)
				{
					value = xmlAttribute.Value;
					if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
					{
						value = value.Substring(2);
					}
					conditionXor = long.Parse(value, NumberStyles.HexNumber);
				}
				memberParser2.Next = ParseSwitch(xmlNode.ChildNodes, doc);
				while (memberParser2.Next != null)
				{
					memberParser2 = memberParser2.Next;
					memberParser2.Condition = MemberParserCondition.EQ;
					memberParser2.ConditionField = value;
					memberParser2.ConditionAnd = conditionXor;
				}
			}
			else if (string.Compare(name, "vector", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				memberParser2.Next = new MemberParser();
				memberParser2 = memberParser2.Next;
				memberParser2.MemberType = MemberParserType.Vector;
				memberParser2.MemberName = xmlNode.Attributes["name"].Value;
				memberParser2.LengthField = xmlNode.Attributes["length"].Value;
				XmlAttribute xmlAttribute = xmlNode.Attributes["mask"];
				long conditionXor = -1L;
				if (xmlAttribute != null)
				{
					string value = xmlAttribute.Value;
					if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
					{
						value = value.Substring(2);
					}
					conditionXor = long.Parse(value, NumberStyles.HexNumber);
				}
				memberParser2.LengthMask = conditionXor;
				xmlAttribute = xmlNode.Attributes["skip"];
				int num = 0;
				if (xmlAttribute != null)
				{
					string value = xmlAttribute.Value;
					if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
					{
						value = value.Substring(2);
					}
					num = int.Parse(value, NumberStyles.HexNumber);
				}
				memberParser2.LengthDelta = -num;
				memberParser2.Child = ParseStruct(xmlNode.ChildNodes, doc);
			}
			else if (string.Compare(name, "align", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
			{
				string value = xmlNode.Attributes["type"].Value;
				if (string.Compare(value, "WORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
				{
					memberParser2.PostAlignment = 2;
				}
				else if (string.Compare(value, "DWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
				{
					memberParser2.PostAlignment = 4;
				}
				else if (string.Compare(value, "QWORD", ignoreCase: true, CultureInfo.InvariantCulture) == 0)
				{
					memberParser2.PostAlignment = 8;
				}
			}
		}
		if (memberParser.PostAlignment != 0 && memberParser.Next != null)
		{
			memberParser.Next.PreAlignment = memberParser.PostAlignment;
		}
		return memberParser.Next;
	}

	private static MemberParser ParseMaskMap(XmlNodeList nodes, XmlDocument doc)
	{
		MemberParser memberParser = new MemberParser();
		MemberParser memberParser2 = memberParser;
		int count = nodes.Count;
		for (int i = 0; i < count; i++)
		{
			XmlNode xmlNode = nodes[i];
			if (xmlNode.NodeType != XmlNodeType.Element || string.Compare(xmlNode.Name, "mask", ignoreCase: true, CultureInfo.InvariantCulture) != 0)
			{
				continue;
			}
			long conditionAnd = 0L;
			XmlAttribute xmlAttribute = xmlNode.Attributes["value"];
			if (xmlAttribute != null)
			{
				string text = xmlAttribute.Value;
				if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X'))
				{
					text = text.Substring(2);
				}
				conditionAnd = long.Parse(text, NumberStyles.HexNumber);
			}
			memberParser2.Next = new MemberParser();
			memberParser2 = memberParser2.Next;
			memberParser2.MemberType = MemberParserType.Case;
			memberParser2.ConditionAnd = conditionAnd;
			memberParser2.Child = ParseStruct(xmlNode.ChildNodes, doc);
		}
		return memberParser.Next;
	}

	private static MemberParser ParseSwitch(XmlNodeList nodes, XmlDocument doc)
	{
		MemberParser memberParser = new MemberParser();
		MemberParser memberParser2 = memberParser;
		int count = nodes.Count;
		for (int i = 0; i < count; i++)
		{
			XmlNode xmlNode = nodes[i];
			if (xmlNode.NodeType != XmlNodeType.Element || string.Compare(xmlNode.Name, "case", ignoreCase: true, CultureInfo.InvariantCulture) != 0)
			{
				continue;
			}
			long conditionResult = 0L;
			XmlAttribute xmlAttribute = xmlNode.Attributes["value"];
			if (xmlAttribute != null)
			{
				string text = xmlAttribute.Value;
				if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X'))
				{
					text = text.Substring(2);
				}
				conditionResult = long.Parse(text, NumberStyles.HexNumber);
			}
			memberParser2.Next = new MemberParser();
			memberParser2 = memberParser2.Next;
			memberParser2.MemberType = MemberParserType.Case;
			memberParser2.ConditionResult = conditionResult;
			memberParser2.Child = ParseStruct(xmlNode.ChildNodes, doc);
		}
		return memberParser.Next;
	}
}
