/*
 * +================================================================================+
 *      __  ___  ______________  __  __  ______________   ____
 *     / / / / |/ /  _/_  __/\ \/ / / / / /_  __/  _/ /  / __/
 *    / /_/ /    // /  / /    \  / / /_/ / / / _/ // /___\ \
 *    \____/_/|_/___/ /_/     /_/  \____/ /_/ /___/____/___/
 *
 * +================================================================================+
 *
 *    "THE BEER-WARE LICENSE"
 *    -----------------------
 *
 *    As long as you retain this notice you can do whatever you want with this
 *    stuff. However it is expressly forbidden to sell or commercially distribute
 *    Unity Utils or parts of it apart from your games.
 *    If you meet any of the authors some day, and you think this stuff is
 *    worth it, you can buy them a beer in return.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *    THE SOFTWARE.
 *
 * +================================================================================+
 *
 *    Authors:
 *    David Krummenacher (dave@timbookedtwo.com)
 *
 */

using System;
using System.IO;
using UnityEngine;

namespace Utils.Helpers {

	/// <summary>
	/// Enables you to read and write key value pairs to a text file.
	/// Keys are represeted by strings. Values can be strings, integers, floating values or booleans.
	/// 
	/// <b>Be carefull:</b>
	/// The resulting files should not be handled manually, if you don't know what you're doing.
	/// If you add multiple values with the same key only the first found value will be returned when reading from the file.
	/// Errors are not handled correctly!
	/// </summary>
	public abstract class TextFileHelper {

		/// <summary>
		/// Separator to make file humanly readable.
		/// </summary>
		private static string _SEPARATOR = ":";
	
		/// <summary>
		/// Empties a text file.
		/// </summary>
		/// <param name="filePath">The path to the file.</param>
		public static void ClearFile(string filePath) {
			File.WriteAllText(filePath, string.Empty);
		}
	
		/// <summary>
		/// Write a string into a file.
		/// </summary>
		/// <param name="filePath">The path to the file.</param>
		/// <param name="key">The key as a string.</param>
		/// <param name="value">The string value.</param>
		/// <param name="append">If set to <c>true</c> the pair is appended to the file.</param>
		public static void WriteString(string filePath, string key, string value, bool append = true) {
			try {
				using (StreamWriter writer = new StreamWriter(filePath, append)) {
					writer.WriteLine(key + _SEPARATOR + value);
				}
			} catch (Exception e) {
				Debug.LogException(e);
			}
		}
	
		/// <summary>
		/// Write an integer into a file.
		/// </summary>
		/// <param name="filePath">The path to the file.</param>
		/// <param name="key">The key as a string.</param>
		/// <param name="value">The integer value.</param>
		/// <param name="append">If set to <c>true</c> the pair is appended to the file.</param>
		public static void WriteInt(string filePath, string key, int value, bool append = false) { WriteString(filePath, key, value.ToString(), append); }

		/// <summary>
		/// Write a floating number into a file.
		/// </summary>
		/// <param name="filePath">The path to the file.</param>
		/// <param name="key">The key as a string.</param>
		/// <param name="value">The floating number value.</param>
		/// <param name="append">If set to <c>true</c> the pair is appended to the file.</param>
		public static void WriteFloat(string filePath, string key, float value, bool append = false) { WriteString(filePath, key, value.ToString(), append); }

		/// <summary>
		/// Write a boolean into a file.
		/// </summary>
		/// <param name="filePath">The path to the file.</param>
		/// <param name="key">The key as a string.</param>
		/// <param name="value">The boolean value.</param>
		/// <param name="append">If set to <c>true</c> the pair is appended to the file.</param>
		public static void WriteBool(string filePath, string key, bool value, bool append = false) { WriteString(filePath, key, value.ToString(), append); }

		/// <summary>
		/// Reads a string from a file based on its key.
		/// </summary>
		/// <returns>The found string.</returns>
		/// <param name="filePath">The path to the file.</param>
		/// <param name="key">The key as a string.</param>
		public static string ReadString(string filePath, string key) {
			string result = find(filePath, key);
			if (result == null) {
				result = string.Empty;
				Debug.LogWarning("Key \"" + key + "\" in file \"" + filePath + "\" has no value. Returning empty string.");
			}
			return result;
		}
		
		/// <summary>
		/// Reads an integer from a file based on its key.
		/// </summary>
		/// <returns>The found integer.</returns>
		/// <param name="filePath">The path to the file.</param>
		/// <param name="key">The key as a string.</param>
		public static int ReadInt(string filePath, string key) {
			string text = find(filePath, key);
	
			int result;
			if (int.TryParse(text, out result)) return result;
			else {
				Debug.LogError(
					"Could not convert the string \"" + text +
					"\" with the key \"" + key +
					"\" in file \"" + filePath +
					"\" to an integer. Returning 0."
				);
				return 0;
			}
		}

		/// <summary>
		/// Reads a floating number from a file based on its key.
		/// </summary>
		/// <returns>The found floating number.</returns>
		/// <param name="filePath">The path to the file.</param>
		/// <param name="key">The key as a string.</param>
		public static float ReadFloat(string filePath, string key) {
			string text = find(filePath, key);
			
			float result;
			if (float.TryParse(text, out result)) return result;
			else {
				Debug.LogError(
					"Could not convert the string \"" + text +
					"\" with the key \"" + key +
					"\" in file \"" + filePath +
					"\" to a float. Returning 0.0."
					);
				return 0f;
			}
		}

		/// <summary>
		/// Reads a boolean from a file based on its key.
		/// </summary>
		/// <returns>The found boolean.</returns>
		/// <param name="filePath">The path to the file.</param>
		/// <param name="key">The key as a string.</param>
		public static bool ReadBool(string filePath, string key) {
			string text = find(filePath, key);
		
			bool result;
			if (bool.TryParse(text, out result)) return result;
			else {
				Debug.LogError(
					"Could not convert the string \"" + text +
					"\" with the key \"" + key +
					"\" in file \"" + filePath +
					"\" to a boolean. Returning false."
					);
				return false;
			}
		}

		/// <summary>
		/// Finds a string in a file based on its key to parse later on.
		/// </summary>
		/// <returns>The found string.</returns>
		/// <param name="filePath">The path to the file.</param>
		/// <param name="key">The key as a string.</param>
		private static string find(string filePath, string key) {
			if (File.Exists(filePath)) {
				try {
					using (StreamReader reader = new StreamReader(filePath)) {
						while (!reader.EndOfStream) {
							string line = reader.ReadLine();
							if (string.IsNullOrEmpty(line)) continue;
							if (line.Length < key.Length) continue;
		
							if (line.Substring(0, key.Length) == key) {
								return line.Substring(key.Length + _SEPARATOR.Length, line.Length - key.Length - _SEPARATOR.Length);
							}
						}
					}
				} catch (Exception e) {
					Debug.LogException(e);
				}
			}
			return null;
		}
	}
}
