//	Spludlow Software
//	Copyright © Samuel P. Ludlow 2020 All Rights Reserved
//	Distributed under the terms of the GNU General Public License version 3
//	Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
//	https://www.spludlow.co.uk/LICENCE.TXT
//	The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission
//	v1.14

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.IO;

namespace Spludlow
{
    public static class Text
    {
		// Byte.MaxValue	1	255 Bytes
		// Int16.MaxValue	2	32 Kilobytes (KB)
		// UInt16.MaxValue	2	64 Kilobytes (KB)
		// Int32.MaxValue	4	2.0 Gigabytes (GB)
		// UInt32.MaxValue	4	4.0 Gigabytes (GB)
		// Int64.MaxValue	8	8.0 Exabytes (EB)
		// UInt64.MaxValue	8	16 Exabytes (EB)

		private static string[] SystemOfUnits =
		{
			"Bytes",
			"Kilobytes (KB)",
			"Megabytes (MB)",
			"Gigabytes (GB)",
			"Terabytes (TB)",
			"Petabytes (PB)",
			"Exabytes (EB)"
		};

		public static string DataSize(int sizeBytes)
		{
			return DataSize((ulong)sizeBytes);
		}

		public static string DataSize(long sizeBytes)
		{
			return DataSize((ulong)sizeBytes);
		}

		public static string DataSize(ulong sizeBytes)
		{
			for (int index = 0; index < SystemOfUnits.Length; ++index)
			{
				ulong nextUnit = (ulong)Math.Pow(2, (index + 1) * 10);

				if (sizeBytes < nextUnit || nextUnit == 0 || index == (SystemOfUnits.Length - 1))
				{
					ulong unit = (ulong)Math.Pow(2, index * 10);
					decimal result = (decimal)sizeBytes / (decimal)unit;
					int decimalPlaces = 0;
					if (result <= 9.9M)
						decimalPlaces = 1;
					result = Math.Round(result, decimalPlaces);
					return result.ToString() + " " + SystemOfUnits[index];
				}
			}

			throw new ApplicationException("Failed to find Data Size\t" + sizeBytes.ToString());
		}

        public static string[] Split(string text, char delimiter)
        {
            return Split(text, delimiter, false, false);
        }

		public static string[] Split(string text, char delimiter, bool removeEmptyEntries)
		{
			return Split(text, delimiter, removeEmptyEntries, false);
		}

		public static string[] Split(string text, char delimiter, bool removeEmptyEntries, bool leaveWhiteSpace)
		{
			return Split(text, delimiter, removeEmptyEntries, leaveWhiteSpace, -1);
		}

		public static string[] Split(string text, char delimiter, bool removeEmptyEntries, bool leaveWhiteSpace, int maxCount)
        {
            List<string> list = new List<string>();

            StringSplitOptions options = StringSplitOptions.None;
            if (removeEmptyEntries == true)
                options = StringSplitOptions.RemoveEmptyEntries;

			string[] parts;
			if (maxCount == -1)
				parts = text.Split(new char[] { delimiter }, options);
			else
				parts = text.Split(new char[] { delimiter }, maxCount, options);

            foreach (string rawPart in parts)
            {
                if (leaveWhiteSpace == true)
                {
                    list.Add(rawPart);
                }
                else
                {
                    string trim = rawPart.Trim();

                    if (trim.Length > 0 || removeEmptyEntries == false)
                        list.Add(trim);
                }
            }
            return list.ToArray();
        }

		public static string[] SplitLines(string lines)
		{
			return SplitLines(lines, false, false);
		}
		public static string[] SplitLines(string lines, bool removeEmptyEntries, bool leaveWhiteSpace)
		{
			List<string> result = new List<string>();

			using (StringReader reader = new StringReader(lines))
			{
				string line;
				while ((line = reader.ReadLine()) != null)
				{
					if (leaveWhiteSpace == false)
						line = line.Trim();

					if (removeEmptyEntries == true && line.Length == 0)
						continue;

					result.Add(line);
				}
			}
			
			return result.ToArray();
		}

		public static string TakeLine(string text, int index)
		{
			using (StringReader reader = new StringReader(text))
			{
				string line;
				for (int pos = 0; pos <= index && ((line = reader.ReadLine()) != null); ++pos)
				{
					if (pos == index)
						return line;
				}
			}
			return null;
		}

		public static string TakeFromLine(string text, int startIndex)
		{
			StringBuilder result = new StringBuilder();

			using (StringReader reader = new StringReader(text))
			{
				string line;
				for (int pos = 0; (line = reader.ReadLine()) != null; ++pos)
				{
					if (pos >= startIndex)
						result.AppendLine(line);
				}
			}

			return result.ToString();
		}

		public static string JoinLines(string[] lines)
		{
			StringBuilder text = new StringBuilder();
			foreach (string line in lines)
				text.AppendLine(line);
			return text.ToString();
		}

		public static string TimeTook(DateTime startTime)
		{
			TimeSpan timeSpan = DateTime.Now - startTime;
			return TimeTook(timeSpan);
		}

		public static string TimeTook(TimeSpan span)
		{
			StringBuilder text = new StringBuilder();

			if (((int)span.TotalDays) > 0)
			{
				text.Append((int)span.TotalDays);
				text.Append("d");
				if (span.Hours > 0)
				{
					text.Append(" ");
					text.Append(span.Hours);
					text.Append("h");
				}
				if (span.Minutes > 0)
				{
					text.Append(" ");
					text.Append(span.Minutes);
					text.Append("m");
				}

				return text.ToString();
			}

			if (((int)span.TotalHours) > 0)
			{
				text.Append(span.Hours);
				text.Append("h");
				if (span.Minutes > 0)
				{
					text.Append(" ");
					text.Append(span.Minutes);
					text.Append("m");
				}

				return text.ToString();
			}

			if (((int)span.TotalMinutes) > 0)
			{
				text.Append(span.Minutes);
				text.Append("m");
				if (span.Seconds > 0)
				{
					text.Append(" ");
					text.Append(span.Seconds);
					text.Append("s");
				}

				return text.ToString();
			}

			if (((int)span.TotalSeconds) > 0)
			{
				text.Append(span.Seconds);
				text.Append("s");
				if (span.Milliseconds > 0)
				{
					text.Append(" ");
					text.Append(span.Milliseconds);
					text.Append("ms");
				}

				return text.ToString();
			}

			text.Append(span.Milliseconds);
			text.Append("ms");

			return text.ToString();
		}

		public static string AfterWord(string line, string delimeter, int counts)
		{
			if (counts == 0)
				return line;

			int offset = 0;

			for (int count = 0; count < counts; ++count)
			{
				int next = line.IndexOf(delimeter, offset);
				if (next == -1)
					break;
				offset = next + 1;
			}

			return line.Substring(offset);
		}

		public static string ChopAfter(string text, string match)
		{
			int index = text.IndexOf(match);
			
			if (index == -1)
				return text;

			return text.Substring(index + match.Length);
		}

		public static string ChopBefore(string text, string match, bool lastIndexOf)
		{
			int index;

			if (lastIndexOf == false)
				index = text.IndexOf(match);
			else
				index = text.LastIndexOf(match);

			if (index == -1)
				return text;

			return text.Substring(0, index);
		}

		public static string ChopBetween(string text, string startMatch, string endMatch, bool lastIndexOf)
		{
			text = ChopAfter(text, startMatch);
			text = ChopBefore(text, endMatch, lastIndexOf);
			return text;
		}

		public static string List(object[] items)
		{
			StringBuilder text = new StringBuilder();

			for (int index = 0; index < items.Length; ++index)
			{
				text.Append(index);
				text.Append("\t");

				string typeText = "Null";
				string dataText = "";

				object item = items[index];

				if (item != null)
				{
					Type type = item.GetType();
					typeText = type.Name;

					if (Spludlow.SimpleEncoding.IsSimple(type) == true)
						dataText = Spludlow.SimpleEncoding.Encode(item, type.Name);
				}

				text.Append(typeText);
				text.Append("\t");

				text.Append(dataText);

				text.AppendLine();
			}

			return text.ToString();
		}

		public static byte[] GetBytes(string text)
		{
			return GetBytes(text, Encoding.Default);
		}
		public static byte[] GetBytes(string text, Encoding encoding)	//	Best way ?
		{
			if (encoding == null)
			{
				byte[] data = new byte[text.Length * sizeof(char)];
				System.Buffer.BlockCopy(text.ToCharArray(), 0, data, 0, data.Length);
				return data;
			}

			return encoding.GetBytes(text);
		}

		public static string GetString(byte[] data)
		{
			return GetString(data, Encoding.Default);
		}
		public static string GetString(byte[] data, Encoding encoding)
		{
			if (encoding == null)
			{
				char[] chars = new char[data.Length / sizeof(char)];
				System.Buffer.BlockCopy(data, 0, chars, 0, data.Length);
				return new string(chars);
			}

			return encoding.GetString(data);
		}

		public static string FixNewLines(string input)
		{
			return FixNewLines(input, null, false);
		}
		public static string FixNewLines(string input, string newLine, bool htmlEncode)
		{
			StringBuilder output = new StringBuilder();

			using (StringReader reader = new StringReader(input))
			{
				string line;
				while ((line = reader.ReadLine()) != null)
				{
					if (htmlEncode == true)
						line = System.Web.HttpUtility.HtmlEncode(line);

					output.Append(line);

					if (newLine != null)
						output.Append(newLine);

					output.AppendLine();
				}
			}

			return output.ToString();
		}

		public static bool MatchWords(string text, string[] words)
		{
			int count = 0;

			foreach (string word in words)
			{
				if (text.Contains(word) == true)
					++count;
			}

			return ((count > 0) && (count == words.Length));
		}

		public static string TimeStamp()
		{
			return TimeStamp(DateTime.Now);
		}
		public static string TimeStamp(DateTime dateTime)
		{
			return dateTime.ToString("yyyy-MM-dd-HH-mm-ss-fff");
		}

		public static string TimeStampDataTableSelect(DateTime dateTime)
		{
			return dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
		}

		public static string RemoveDiacritics(string source)
		{
			String normalizedString = source.Normalize(NormalizationForm.FormD);
			StringBuilder result = new StringBuilder();

			foreach (char c in normalizedString)
			{
				if (System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c) != System.Globalization.UnicodeCategory.NonSpacingMark)
					result.Append(c);
			}

			return result.ToString();
		}

		public static string[] Swivel(string[] inputLines, bool removeEmptyEntries)
		{
			int sourceWidth = 0;

			foreach (string inputLine in inputLines)
			{
				int width = Spludlow.Text.Split(inputLine, '\t', removeEmptyEntries).Length;
				if (width > sourceWidth)
					sourceWidth = width;
			}

			Console.WriteLine("sourceWidth:" + sourceWidth);

			List<string> results = new List<string>();

			for (int sourceColumn = 0; sourceColumn < sourceWidth; ++sourceColumn)
			{
				StringBuilder newLine = new StringBuilder();

				foreach (string inputLine in inputLines)
				{
					string[] words = Spludlow.Text.Split(inputLine, '\t', removeEmptyEntries);

					string value = "";
					if (sourceColumn < words.Length)
						value = words[sourceColumn];

					if (newLine.Length > 0)
						newLine.Append("\t");

					newLine.Append(value);
				}

				results.Add(newLine.ToString());
			}

			return results.ToArray();
		}

		public static string TestTextLines(int approximateLineLength, int lineCount, bool trailingNewLine)
		{
			StringBuilder result = new StringBuilder();

			for (int index = 0; index < lineCount; ++index)
			{
				string line = TestText(approximateLineLength);

				if (trailingNewLine == true)
				{
					result.AppendLine(line);
				}
				else
				{
					if (result.Length > 0)
						result.AppendLine();
					result.Append(line);
				}
			}

			return result.ToString();
		}
		public static string TestText(int approximateLength)
		{
			StringBuilder result = new StringBuilder();

			while (result.Length < approximateLength)
			{
				if (result.Length > 0)
					result.Append(" ");
				result.Append(Spludlow.Data.Fictional.FullName());
			}

			return result.ToString();
		}

		public static string DelimitedText<T>(List<T> list, string delimeter)
		{
			StringBuilder text = new StringBuilder();
			foreach (T item in list)
			{
				if (text.Length > 0)
				{
					text.Append(delimeter);
					text.Append(' ');
				}
				text.Append(Convert.ToString(item));
			}
			return text.ToString();
		}

		public static bool IsASCII(string text)
		{
			foreach (char c in text)
			{
				if (c > 127)
					return false;
			}
			return true;
		}

	}
}