using DomainObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.Text;

namespace DVBServices
{
	internal class Utils
	{
		/// <summary>
		/// Convert a string of bytes to a hex string.
		/// </summary>
		/// <param name="inputChars">The string to be converted.</param>
		/// <returns>The string of hex characters.</returns>
		public static string ConvertToHex(byte[] inputChars)
		{
			return (ConvertToHex(inputChars, inputChars.Length));
		}

		/// <summary>
		/// Convert a string of bytes to a hex string.
		/// </summary>
		/// <param name="inputChars">The array holding the bytes to be converted.</param>
		/// <param name="length">The number of byte to be converted.</param>
		/// <returns>The string of hex characters.</returns>
		public static string ConvertToHex(byte[] inputChars, int length)
		{
			return (ConvertToHex(inputChars, 0, length));
		}

		/// <summary>
		/// Convert a string of bytes to a hex string.
		/// </summary>
		/// <param name="inputChars">The array holding the bytes to be converted.</param>
		/// <param name="offset">The the offset to the first byte to be converted.</param> 
		/// <param name="length">The number of byte to be converted.</param>
		/// <returns>The string of hex characters.</returns>
		public static string ConvertToHex(byte[] inputChars, int offset, int length)
		{
			char[] outputChars = new char[length * 2];
			int outputIndex = 0;

			for (int inputIndex = 0; inputIndex < length; inputIndex++)
			{
				int hexByteLeft = inputChars[offset] >> 4;
				int hexByteRight = inputChars[offset] & 0x0f;

				outputChars[outputIndex] = getHex(hexByteLeft);
				outputChars[outputIndex + 1] = getHex(hexByteRight);

				outputIndex += 2;
				offset++;
			}

			return ("0x" + new string(outputChars));
		}

		private static char getHex(int value)
		{
			if (value < 10)
				return ((char)('0' + value));

			return ((char)('a' + (value - 10)));
		}

		/// <summary>
		/// Get subset of bytes from an array.
		/// </summary>
		/// <param name="byteData">The array of bytes.</param>
		/// <param name="offset">The index of the first byte of the subset.</param>
		/// <param name="length">The length of the subset.</param>
		/// <returns>The subset of bytes.</returns>
		public static byte[] GetBytes(byte[] byteData, int offset, int length)
		{
			if (length < 1)
				throw (new ArgumentOutOfRangeException("GetBytes length wrong"));

			try
			{
				byte[] outputBytes = new byte[length];

				for (int index = 0; index < length; index++)
					outputBytes[index] = byteData[offset + index];

				return (outputBytes);
			}
			catch (OutOfMemoryException)
			{
				throw (new ArgumentOutOfRangeException("GetBytes length wrong"));
			}
		}

		/// <summary>
		/// Convert 2 bytes to an integer with the most significant byte first.
		/// </summary>
		/// <param name="byteData">The byte array containing the byes to convert.</param>
		/// <param name="index">The index of the first byte in the array.</param>
		/// <returns>The converted value.</returns>
		public static int Convert2BytesToInt(byte[] byteData, int index)
		{
			return (Convert2BytesToInt(byteData, index, 0xff));
		}

		/// <summary>
		/// Convert 2 bytes to an integer with the most significant byte first and a mask.
		/// </summary>
		/// <param name="byteData">The byte array containing the byes to convert.</param>
		/// <param name="index">The index of the first byte in the array.</param>
		/// <param name="mask">The mask for the most significant byte.</param>
		/// <returns>The converted value.</returns>
		public static int Convert2BytesToInt(byte[] byteData, int index, byte mask)
		{
			return (((byteData[index] & mask) * 256) + (int)byteData[index + 1]);
		}

		private static En300468AnnexATextDecoder _encodingProvider;
		/// <summary>
		/// Convert a subset of an array of text bytes to a string.
		/// </summary>
		/// <param name="byteData">The array of bytes.</param>
		/// <param name="offset">The index of the first byte to be converted.</param>
		/// <param name="length">The number of bytes to be converted.</param>
		/// <param name="replaceMode">Action to be taken for non-Ascii bytes.</param>
		/// <returns>The converted string.</returns>
		public static string GetString(byte[] byteData, int offset, int length, ReplaceMode replaceMode)
		{
			if (_encodingProvider == null)
			{
				_encodingProvider = En300468AnnexATextDecoder.GetInstance();
			}

			string temp = _encodingProvider.Decode(byteData, offset, length);
			if (temp == null)
				temp = "";
			return temp;
		}

		/// <summary>
		/// Convert an integer value to a hex string.
		/// </summary>
		/// <param name="value">The value to be converted.</param>
		/// <returns>The converted string.</returns>
		public static string ConvertToHex(int value)
		{
			if (value == 0)
				return ("0x00");

			uint tempValue = (uint)value;

			char[] outputChars = new char[8];

			int outputIndex = 7;

			for (int index = 3; index > -1; index--)
			{
				uint hexByte = (tempValue << 24) >> 24;
				int hexByteLeft = (int)(hexByte >> 4);
				int hexByteRight = (int)(hexByte & 0x0f);

				outputChars[outputIndex] = getHex(hexByteRight);
				outputChars[outputIndex - 1] = getHex(hexByteLeft);

				outputIndex -= 2;
				tempValue = tempValue >> 8;
			}

			string replyString = new string(outputChars).TrimStart(new char[] { '0' });

			if (replyString.Length % 2 == 0)
				return ("0x" + replyString);
			else
				return ("0x0" + replyString);
		}
	}
}
