#region Copyright & License Information

/*
 * Copyright (c) The OpenRA Developers and Contributors
 * This file is part of OpenRA, which is free software. It is made
 * available to you under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version. For more
 * information, see COPYING.
 */

#endregion

using System;

namespace OpenRA.Mods.Common.FileFormats
{
	public static class WestwoodCompressedReader
	{
		static readonly int[] AudWsStepTable2 = { -2, -1, 0, 1 };
		static readonly int[] AudWsStepTable4 = { -9, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8 };

		public static void DecodeWestwoodCompressedSample(ReadOnlySpan<byte> input, Span<byte> output)
		{
			if (input.Length == output.Length)
			{
				input.CopyTo(output);

				return;
			}

			var sample = 0x80;
			var r = 0;
			var w = 0;

			while (r < input.Length)
			{
				var count = input[r++] & 0x3f;

				switch (input[r - 1] >> 6)
				{
					case 0:
						for (count++; count > 0; count--)
						{
							var code = input[r++];
							output[w++] = (byte)(sample = (sample + AudWsStepTable2[(code >> 0) & 0x03]).Clamp(byte.MinValue, byte.MaxValue));
							output[w++] = (byte)(sample = (sample + AudWsStepTable2[(code >> 2) & 0x03]).Clamp(byte.MinValue, byte.MaxValue));
							output[w++] = (byte)(sample = (sample + AudWsStepTable2[(code >> 4) & 0x03]).Clamp(byte.MinValue, byte.MaxValue));
							output[w++] = (byte)(sample = (sample + AudWsStepTable2[(code >> 6) & 0x03]).Clamp(byte.MinValue, byte.MaxValue));
						}

						break;

					case 1:
						for (count++; count > 0; count--)
						{
							var code = input[r++];
							output[w++] = (byte)(sample = (sample + AudWsStepTable4[(code >> 0) & 0x0f]).Clamp(byte.MinValue, byte.MaxValue));
							output[w++] = (byte)(sample = (sample + AudWsStepTable4[(code >> 4) & 0xff]).Clamp(byte.MinValue, byte.MaxValue));
						}

						break;

					case 2 when (count & 0x20) != 0:
						output[w++] = (byte)(sample += (sbyte)((sbyte)count << 3) >> 3);

						break;

					case 2:
						for (count++; count > 0; count--)
							output[w++] = input[r++];

						sample = input[r - 1];

						break;

					default:
						for (count++; count > 0; count--)
							output[w++] = (byte)sample;

						break;
				}
			}
		}
	}
}
