using skyscraper5.Skyscraper.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace skyscraper5.src.Skyscraper.IO.PortableExecutables
{
	public static class RsrcParser
	{
		public static ImageResourceDirectory ParseRsrc(PeSectionStream s, int depth = 0)
		{
			ImageResourceDirectory result = new ImageResourceDirectory();
			result.Characteristics = s.ReadInt32LE();
			result.TimeDateStamp = s.ReadInt32LE();
			int major = s.ReadUInt16LE();
			int minor = s.ReadUInt16LE();
			result.Version = new Version(major, minor);
			result.NumNamedEntries = s.ReadInt16LE();
			result.NumIdEntries = s.ReadInt16LE();
			result.Depth = depth;

			ImageResourceDirectoryEntry[] entries = new ImageResourceDirectoryEntry[result.NumEntries];
			for (int i = 0; i < entries.Length; i++)
			{
				entries[i] = new ImageResourceDirectoryEntry();
				entries[i].NameId = s.ReadUInt32LE();
				entries[i].Data = s.ReadUInt32LE();

				if (entries[i].UsingNameId)
				{
					entries[i].Name = entries[i].NameId.ToString();
				}
				else
				{
					long currentPos = s.Position;
					s.Position = entries[i].Lower31BitOfNameId;
					int length = s.ReadUInt16LE() * 2;
					entries[i].Name = s.ReadUTF8FixedLength(length);
					s.Position = currentPos;
				}

				if (entries[i].IsSubdirectory)
				{
					long currentPos = s.Position;
					s.Position = entries[i].NextDirOffset;
					entries[i].Payload = ParseRsrc(s,depth + 1);
					s.Position = currentPos;
				}
				else
				{
					long currentPos = s.Position;
					s.Position = entries[i].NextDirOffset;

					ImageResourceDataEntry info = new ImageResourceDataEntry();
					info.Data = s.ReadInt32LE();
					info.Size = s.ReadInt32LE();
					info.CodePage = s.ReadInt32LE();
					info.Reserved = s.ReadInt32LE();
					info.SetContents(s, info.Data, info.Size,s.Section);
					entries[i].Payload = info;

					s.Position = currentPos;
				}
			}
			result.Entries = entries;
			return result;
		}
	}

	public class ImageResourceDirectory
	{
		public int Characteristics { get; internal set; }
		public int TimeDateStamp { get; internal set; }
		public Version Version { get; internal set; }
		public short NumNamedEntries { get; internal set; }
		public short NumIdEntries { get; internal set; }

		public int NumEntries => NumNamedEntries + NumIdEntries;

		public ImageResourceDirectoryEntry[] Entries { get; internal set; }
		public int Depth { get; internal set; }
	}

	public class ImageResourceDirectoryEntry
	{
		public uint NameId { get; internal set; }
		public uint Data { get; internal set; }

		public bool UsingNameId
		{
			get
			{
				return (NameId >> 31) != -1;
			}
		}

		public uint Lower31BitOfNameId
		{
			get
			{
				return (NameId & 0x7FFFFFFF);
			}
		}

		public bool IsSubdirectory
		{
			get
			{
				return (Data >> 31) == 1;
			}
		}

		public uint NextDirOffset
		{
			get
			{
				return Data & 0x7FFFFFFF;
			}
		}

		public string Name { get; internal set; }
		public object Payload { get; internal set; }
	}

	public class ImageResourceDataEntry
	{
		public int Data { get; internal set; }
		public int Size { get; internal set; }
		public int CodePage { get; internal set; }
		public int Reserved { get; internal set; }
		public byte[] Buffer { get; private set; }

		internal void SetContents(Stream s, int data, int size, PeSection section)
		{
			long currentPosition = s.Position;
			s.Position = data - section.virtualAddress;
			this.Buffer = new byte[size];
			if (s.Read(this.Buffer, 0, size) != size)
				throw new EndOfStreamException();
			s.Position = currentPosition;
		}
	}
}
