using System;
using System.Linq;
using System.Collections.Generic;

namespace %%%PROJECT_ID%%%
{
	internal static class TranslationHelper
	{
		private static readonly Random random = new Random();

		public static ProgramData ProgramData { get; set; }

		public static readonly bool IsWindows =
			Environment.OSVersion.Platform == PlatformID.Win32NT ||
			Environment.OSVersion.Platform == PlatformID.Win32S ||
			Environment.OSVersion.Platform == PlatformID.Win32Windows ||
			Environment.OSVersion.Platform == PlatformID.WinCE;

		private static string appDataRoot = null;
		public static string AppDataRoot
		{
			get
			{
				if (appDataRoot == null)
				{
					// TODO: test with Mono
					appDataRoot = System.IO.Path.Combine(System.Environment.GetEnvironmentVariable("APPDATA"), "%%%PROJECT_ID%%%");
					appDataRoot = appDataRoot.Replace('\\', '/');
				}
				return appDataRoot;
			}
		}

		public static void ShuffleInPlace(List<Value> list)
		{
			if (list.Count < 2) return;

			int length = list.Count;
			int tIndex;
			Value tValue;
			for (int i = length - 1; i >= 0; --i)
			{
				tIndex = random.Next(length);
				tValue = list[tIndex];
				list[tIndex] = list[i];
				list[i] = tValue;
			}
		}

		public static double GetRandomNumber()
		{
			return random.NextDouble();
		}

		public static List<Value> ValueListConcat(List<Value> a, List<Value> b)
		{
			List<Value> output = new List<Value>(a.Count + b.Count);
			output.AddRange(a);
			output.AddRange(b);
			return output;
		}

		public static List<Value> MultiplyList(List<Value> items, int times)
		{
			List<Value> output = new List<Value>(items.Count * times);
			while (times-- > 0)
			{
				output.AddRange(items);
			}
			return output;
		}

		public static List<T> NewListOfSize<T>(int size)
		{
			List<T> output = new List<T>(size);
			while (size-- > 0)
			{
				output.Add(default(T));
			}
			return output;
		}

		private static readonly string[] SPLIT_SEP = new string[1];

		public static string[] StringSplit(string value, string sep)
		{
			if (sep.Length == 1)
			{
				return value.Split(sep[0]);
			}

			if (sep.Length == 0)
			{
				return value.ToCharArray().Select<char, string>(c => "" + c).ToArray();
			}

			SPLIT_SEP[0] = sep;
			return value.Split(SPLIT_SEP, StringSplitOptions.None);
		}

		public static string StringReverse(string value)
		{
			if (value.Length < 2) return value;
			System.Text.StringBuilder sb = new System.Text.StringBuilder();
			for (int i = value.Length - 1; i >= 0; --i)
			{
				sb.Append(value[i]);
			}
			return sb.ToString();
		}

		public static object ImagetteFlushToNativeBitmap(Imagette img)
		{
			System.Drawing.Bitmap target = new System.Drawing.Bitmap(img.width, img.height);
			target.SetResolution(96, 96);
			System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(target);
			for (int i = img.nativeBitmaps.Count - 1; i >= 0; i--)
			{
				System.Drawing.Bitmap source = (System.Drawing.Bitmap)img.nativeBitmaps[i];
				int x = img.xs[i];
				int y = img.ys[i];
				g.DrawImage(source, x, y);
			}
			g.Flush();
			return target;
		}

		public static void DownloadImageFromInternetTubes(string key, string url)
		{
			System.ComponentModel.BackgroundWorker bgWorker = new System.ComponentModel.BackgroundWorker();
			bgWorker.DoWork += (sender, args) =>
			{
				System.Net.WebClient wc = new System.Net.WebClient();
				try
				{
					byte[] imageBytes = wc.DownloadData(url);
					System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(new System.IO.MemoryStream(imageBytes));
					bmp.SetResolution(96, 96);
					args.Result = bmp;
				}
				catch (Exception)
				{
					args.Result = null;
				}
			};
			bgWorker.RunWorkerCompleted += (sender, args) =>
			{
				CrayonWrapper.v_imageDownloadCompletionHandler(key, args.Result);
			};
			bgWorker.RunWorkerAsync();
		}

		public static void Assertion(string message)
		{
			throw new Exception(message);
		}

		public static void SortPrimitiveValueList(List<Value> valueList, bool isString)
		{
			Value[] orderedList;
			if (isString)
			{
				orderedList = valueList.OrderBy<Value, string>(v => (string)v.internalValue).ToArray();
			}
			else
			{
				orderedList = valueList.OrderBy<Value, double>(v => v.type == %%%TYPE_INTEGER%%% ? (double)(int)v.internalValue : (double)v.internalValue).ToArray();
			}
			for (int i = orderedList.Length - 1; i >= 0; --i)
			{
				valueList[i] = orderedList[i];
			}
		}

		public static SoundInstance GetSoundInstance(string path)
		{
			IList<byte> resource = ResourceReader.ReadBytes(path, true);
			if (resource == null) return null;
			SdlDotNet.Audio.Sound sdlSound = new SdlDotNet.Audio.Sound(resource.ToArray());
			// TODO: determine length of sound.
			return new SoundInstance(sdlSound, false, -1, 0);
		}

		public static void PlaySoundInstance(SoundInstance sound)
		{
			SdlDotNet.Audio.Sound sdlSound = (SdlDotNet.Audio.Sound)sound.nativeObject;
			sdlSound.Play();
		}

		public static bool DoesPathExist(string path, bool directoriesOnly, bool checkCase)
		{
			// TODO: check the case.
			if (System.IO.Directory.Exists(path))
			{
				return true;
			}

			if (!directoriesOnly && System.IO.File.Exists(path))
			{
				return true;
			}

			return false;
		}

		public static string[] FilesInDirectory(string path)
		{
			if (!path.Contains("\\") && path.Contains(":"))
			{
				path += "\\";
			}
			List<string> files = new List<string>();
			files.AddRange(System.IO.Directory.GetFiles(path));
			files.AddRange(System.IO.Directory.GetDirectories(path));
			HashSet<string> output = new HashSet<string>(files);
			return output
				.Select<string, string>(
					s => s.Substring((path.EndsWith("\\") ? 0 : 1) + path.Length))
				.OrderBy(s => s)
				.ToArray();
		}

		public static int WriteFile(string path, string contents)
		{
			try
			{
				System.IO.File.WriteAllText(path, contents);
			}
			catch (UnauthorizedAccessException)
			{
				return 1;
			}
			catch (System.IO.PathTooLongException)
			{
				return 2;
			}
			catch (System.IO.IOException)
			{
				return 3;
			}
			catch (Exception)
			{
				return 4;
			}
			return 0;
		}

		public static string ReadFile(string path)
		{
			try
			{
				return System.IO.File.ReadAllText(path);
			}
			catch (Exception)
			{
				return null;
			}
		}

		public static void MakeHttpRequestWithHandler(HttpRequest httpRequest, string method, string url, string body, string userAgent, string contentType, string contentLength, List<string> headerNames, List<string> headerValues)
		{
			System.ComponentModel.BackgroundWorker bgRequest = new System.ComponentModel.BackgroundWorker();
			bgRequest.DoWork += (sender, e) =>
			{
				System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);
				request.Method = method;
				request.UserAgent = userAgent;
				for (int i = 0; i < headerNames.Count; ++i)
				{
					request.Headers.Add(headerNames[i], headerValues[i]);
				}

				if (body != null)
				{
					request.ContentLength = Convert.ToInt64(contentLength);
					if (contentType != null)
					{
						request.ContentType = contentType;
					}
					System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(request.GetRequestStream());
					streamWriter.Write(body);
					streamWriter.Flush();
					streamWriter.Close();
				}

				System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();
				int statusCode = (int)response.StatusCode;
				string status = statusCode + " " + response.StatusDescription;

				System.IO.StreamReader streamReader = new System.IO.StreamReader(response.GetResponseStream());
				string responseBody = streamReader.ReadToEnd();
				streamReader.Close();

				// Request is not necessarily executed in the main UI thread in all exported C# platforms, and so 
				// BackgroundWorker's RunWorkerCompleted event may not get fired. This is why this AsyncMessageQueue
				// thingamajigger is used instead.
				AsyncMessageQueue.PushMessage(
					%%%ASYNC_MESSAGE_TYPE_HTTP_RESPONSE%%%,
					httpRequest,
					statusCode,
					status,
					responseBody,
					null);
			};
			bgRequest.RunWorkerAsync();
		}

		public static void ParseFloatOrReturnNull(double[] outParam, string rawValue)
		{
			double output;
			if (double.TryParse(rawValue, out output))
			{
				outParam[0] = 1;
				outParam[1] = output;
			}
			else
			{
				outParam[0] = -1;
			}
		}

		public static int CreateDirectory(string path)
		{
			try
			{
				System.IO.Directory.CreateDirectory(path);
				return %%%IO_ERROR_NONE%%%;
			}
			catch (UnauthorizedAccessException) { return %%%IO_ERROR_READ_ONLY%%%; }
			catch (System.IO.PathTooLongException) { return %%%IO_ERROR_PATH_TOO_LONG%%%; }
			catch (System.IO.DirectoryNotFoundException) { return %%%IO_ERROR_DOES_NOT_EXIST%%%; }
			catch (System.IO.IOException) { return %%%IO_ERROR_UNKNOWN_ERROR%%%; }
		}

		public static int DeleteDirectory(string path, bool recursive)
		{
			try
			{
				System.IO.Directory.Delete(path, recursive);
				return %%%IO_ERROR_NONE%%%;
			}
			catch (System.ArgumentException) { return %%%IO_ERROR_INVALID_PATH%%%; } // invalid path characters
			catch (System.IO.PathTooLongException) { return %%%IO_ERROR_PATH_TOO_LONG%%%; }
			catch (System.IO.DirectoryNotFoundException) { return %%%IO_ERROR_DOES_NOT_EXIST%%%; }
			catch (System.UnauthorizedAccessException) { return %%%IO_ERROR_READ_ONLY%%%; }
			catch (System.IO.IOException) { return %%%IO_ERROR_UNKNOWN_ERROR%%%; }
		}

		public static int DeleteFile(string path)
		{
			try
			{
				System.IO.File.Delete(path);
				return %%%IO_ERROR_NONE%%%;
			}
			catch (System.ArgumentException) { return %%%IO_ERROR_INVALID_PATH%%%; } // invalid path characters
			catch (System.IO.PathTooLongException) { return %%%IO_ERROR_PATH_TOO_LONG%%%; }
			catch (System.IO.FileNotFoundException) { return %%%IO_ERROR_DOES_NOT_EXIST%%%; }
			catch (System.IO.DirectoryNotFoundException) { return %%%IO_ERROR_DOES_NOT_EXIST%%%; }
			catch (System.UnauthorizedAccessException) { return %%%IO_ERROR_READ_ONLY%%%; }
			catch (System.IO.IOException) { return %%%IO_ERROR_UNKNOWN_ERROR%%%; }
		}
	}
}
