using System;
using System.IO;

namespace BizHawk.Client.Common
{
	public static class IPS
	{
		public static byte[] Patch(byte[] rom, Stream patch)
		{
			var ipsHeader = new byte[5];
			patch.Read(ipsHeader, 0, 5);

			string header = "PATCH";
			for (int i = 0; i < 5; i++)
			{
				if (ipsHeader[i] != header[i])
				{
					Console.WriteLine("Patch file specified is invalid.");
					return null;
				}
			}

			// header verified, loop over patch entries
			uint EOF = ('E' * 0x10000 + 'O' * 0x100 + 'F');

			var ret = new MemoryStream(rom.Length);
			ret.Write(rom, 0, rom.Length);

			while (true)
			{
				uint offset = Read24(patch);
				if (offset == EOF)
					return ret.ToArray();
				ushort size = Read16(patch);

				ret.Seek(offset, SeekOrigin.Begin);

				if (size != 0) // non-RLE patch
				{
					var patchData = new byte[size];
					patch.Read(patchData, 0, size);

					ret.Write(patchData, 0, patchData.Length);
				}
				else // RLE patch
				{
					size = Read16(patch);
					byte value = (byte)patch.ReadByte();
					for (int i = 0; i < size; i++)
					{
						ret.WriteByte(value);
					}
				}
			}
		}

		private static ushort Read16(Stream patch)
		{
			int Upper = patch.ReadByte();
			int Lower = patch.ReadByte();
			return (ushort)(Upper * 0x100 + Lower);
		}

		private static uint Read24(Stream patch)
		{
			int Upper = patch.ReadByte();
			int Middle = patch.ReadByte();
			int Lower = patch.ReadByte();
			return (uint)(Upper * 0x10000 + Middle * 0x100 + Lower);
		}
	}
}