using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using ReturnType = System.String;
using ReturnType2 = SpanDemos.Program.Item;
using ReturnType3 = SpanDemos.Program.ItemStruct;
using ReturnType4 = System.ReadOnlyMemory<char>;

namespace SpanDemos
{
	unsafe class Program
	{
		public static readonly string BaseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
		public static readonly string FilePath = Path.Combine(BaseDir, "..", "..", "..", "..", "data.data");

		static void Main()
		{
			//BenchmarkRunner.Run<Measure>();
			//return;

			//foreach (var item in Do(FilePath))
			//{
			//	Console.WriteLine(item);
			//	//ReadOnlySpan<byte> bytes = MemoryMarshal.Cast<char, byte>(item.Span);
			//	//Console.WriteLine(string.Join(",", bytes.ToArray()));
			//	//ReadOnlySpan<int> ints = MemoryMarshal.Cast<char, int>(item.Span);
			//	//Console.WriteLine(string.Join(",", ints.ToArray()));
			//}
		}

		public static List<ReturnType> Do(string path)
		{
			var buffer = new byte[4 * 1024];
			var data = ReadFile(path, buffer);
			var aWords = ParseData(Encoding.UTF8.GetString(data));
			return aWords;
		}
		public unsafe static List<ReturnType4> DoStack(string path)
		{
			var buffer = stackalloc byte[4 * 1024];
			var data = ReadFile(path, buffer, 4 * 1024);
			var aWords = ParseData4(Encoding.UTF8.GetString(data));
			return aWords;
		}
		public unsafe static List<ReturnType4> DoUnmanaged(string path)
		{
			var buffer = Marshal.AllocHGlobal(4 * 1024);
			try
			{
				var data = ReadFile(path, buffer, 4 * 1024);
				var aWords = ParseData4(Encoding.UTF8.GetString(data));
				return aWords;
			}
			finally
			{
				Marshal.FreeHGlobal(buffer);
			}
		}
		public static List<ReturnType4> DoSpan(string path)
		{
			Span<byte> buffer = stackalloc byte[4 * 1024];
			var data = ReadFile(path, buffer);
			var aWords = ParseData4(Encoding.UTF8.GetString(data));
			return aWords;
		}

		static List<string> ParseData(string data)
		{
			var result = new List<string>(256);
			var pieces = data.Split(" ");
			foreach (var piece in pieces)
			{
				if (piece.Contains('a', StringComparison.OrdinalIgnoreCase))
				{
					result.Add(piece);
				}
			}
			return result;
		}

		public class Item
		{
			public string S { get; set; }
			public int Start { get; set; }
			public int End { get; set; }

			public Item(string s, int start, int end)
			{
				S = s;
				Start = start;
				End = end;
			}

			public override string ToString() => S[Start..End];
		}
		static List<Item> ParseData2(string data)
		{
			var result = new List<Item>(256);
			var startIndex = 0;
			while (true)
			{
				var index = data.IndexOf(' ', startIndex);
				if (index == -1)
					break;
				if (data.IndexOf('a', startIndex, index - startIndex) != -1)
				{
					result.Add(new Item(data, startIndex, index));
				}
				startIndex = index + 1;
			}
			if (data.IndexOf('a', startIndex) != -1)
			{
				result.Add(new Item(data, startIndex, data.Length));
			}
			return result;
		}

		public readonly struct ItemStruct
		{
			public readonly string S;
			public readonly int Start;
			public readonly int End;

			public ItemStruct(string s, int start, int end)
			{
				S = s;
				Start = start;
				End = end;
			}

			public override string ToString() => S[Start..End];
		}
		static List<ItemStruct> ParseData3(string data)
		{
			var result = new List<ItemStruct>(256);
			var startIndex = 0;
			while (true)
			{
				var index = data.IndexOf(' ', startIndex);
				if (index == -1)
					break;
				if (data.IndexOf('a', startIndex, index - startIndex) != -1)
				{
					result.Add(new ItemStruct(data, startIndex, index));
				}
				startIndex = index + 1;
			}
			if (data.IndexOf('a', startIndex) != -1)
			{
				result.Add(new ItemStruct(data, startIndex, data.Length));
			}
			return result;
		}

		static List<ReadOnlyMemory<char>> ParseData4(string data)
		{
			var result = new List<ReadOnlyMemory<char>>(256);
			var dataMemory = data.AsMemory();
			while (true)
			{
				var index = dataMemory.Span.IndexOf(' ');
				if (index == -1)
					break;
				var possible = dataMemory.Slice(0, index);
				if (possible.Span.IndexOf('a') != -1)
				{
					result.Add(possible);
				}
				dataMemory = dataMemory.Slice(index + 1);
			}
			if (dataMemory.Span.IndexOf('a') != -1)
			{
				result.Add(dataMemory);
			}
			return result;
		}

		static byte[] ReadFile(string path, byte[] buffer)
		{
			using (var fs = File.OpenRead(path))
			{
				var result = new byte[(int)fs.Length];
				var position = 0;
				while (true)
				{
					var read = fs.Read(buffer, 0, buffer.Length);
					if (read == 0)
						break;
					for (var i = 0; i < read; i++)
					{
						result[position + i] = buffer[i];
					}
				}
				return result;
			}
		}
		unsafe static byte[] ReadFile(string path, byte* buffer, int length)
		//unsafe static byte[] ReadFile(string path, byte* buffer, int startIndex, int length)
		{
			// ...
			return default;
			//ReadFile(path, new Span<byte>(buffer, length));
			//ReadFile(path, new Span<byte>(buffer, length).Slice(startIndex));
			// Unsafe.Add
		}
		unsafe static byte[] ReadFile(string path, IntPtr buffer, int length)
		//unsafe static byte[] ReadFile(string path, IntPtr buffer, int offset, int length)
		{
			// ...
			return default;
			//ReadFile(path, new Span<byte>(buffer.ToPointer(), length));
		}
		//static byte[] ReadFile(string path, Span<byte> buffer)		
		static Span<byte> ReadFile(ReadOnlySpan<char> path, Span<byte> buffer)
		{
			// https://source.dot.net/#System.Private.CoreLib/ReadOnlySpan.cs,316
			using (var fs = File.OpenRead(path.ToString()))
			{
				var result = new byte[(int)fs.Length];
				var position = 0;
				while (true)
				{
					var read = fs.Read(buffer);
					if (read == 0)
						break;
					for (var i = 0; i < read; i++)
					{
						result[position + i] = buffer[i];
					}
				}
				return result;
			}
		}
	}

	[MemoryDiagnoser]
	public class Measure
	{
		[Benchmark]
		public int MeasureDo()
		{
			return Program.Do(@"C:\Users\Jiri\Documents\speaking\2022\Techorama\span\SpanDemos\data.data").Count;
		}
	}
}
