using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.CompilerServices;

namespace Vortex.Debugging {

	/// <summary>
	/// Log contains methods for output debug information into Console and File
	/// </summary>
	public static class Log {
		private static bool _writeConsoleEnabled;
		private static bool _configured;
		private static HtmlLogWriter _logWriter;
		private static LogHistory _history = new LogHistory();
		private static PaddingLevel _paddingLevel = new PaddingLevel();

		public static void Configure(bool writeConsole, string fileName) {
			_writeConsoleEnabled = writeConsole;
			if (!string.IsNullOrEmpty(fileName)) {
				_logWriter = new HtmlLogWriter(fileName, false);
			} else {
				_logWriter = null;
			}
			_configured = true;
		}

		private static void ConfigureDefault() {
			Configure(true, @"Vortex2D.log.html");
		}

		/// <summary>
		/// Gets the history of log.
		/// </summary>
		/// <value>The history of log.</value>
		public static LogHistory History {
			get { return _history; }
		}

		#region Log Level class

		/// <summary>
		/// Provides automation for scoped adding and removing log level
		/// </summary>
		private class LogLevel : IDisposable {
			private PaddingLevel _paddingLevel;

			public LogLevel(PaddingLevel paddingLevel) {
				_paddingLevel = paddingLevel;
				_paddingLevel.AddLevel();
			}

			public void Dispose() {
				_paddingLevel.RemoveLevel();
			}
		}

		#endregion


		#region Padding Level Class

		///<summary>Padding level wrapper</summary>
		private class PaddingLevel {
			private int _level;

			public int Level {
				get { return _level; }
			}

			public void AddLevel() {
				_level++;
			}

			public void RemoveLevel() {
				_level = Math.Max(_level - 1, 0);
			}
		}

		#endregion

		#region Public Interface

		/// <summary>
		/// Write details message into log
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Details(string format, params object[] args) {
			WriteLine(Severity.Details, format, args);
		}

		/// <summary>
		/// Write info message into log
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Info(string format, params object[] args) {
			WriteLine(Severity.Info, format, args);
		}

		/// <summary>
		/// Write common message into log
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Trace(string format, params object[] args) {
			WriteLine(Severity.Normal, format, args);
		}


		/// <summary>
		/// Write common message into log and promote log level for a scope
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static IDisposable TraceUp(string format, params object[] args) {
			WriteLine(Severity.Normal, format, args);
			return new LogLevel(_paddingLevel);
		}

		/// <summary>
		/// Write important message into log
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Important(string format, params object[] args) {
			WriteLine(Severity.Important, format, args);
		}

		/// <summary>
		/// Writes important message into log and promote log level for a scope
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static IDisposable ImportantUp(string format, params object[] args) {
			WriteLine(Severity.Important, format, args);
			return new LogLevel(_paddingLevel);
		}


		/// <summary>
		/// Write success operation message into log
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Success(string format, params object[] args) {
			WriteLine(Severity.Success, format, args);
		}

		/// <summary>
		/// Write warning message into log
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Warning(string format, params object[] args) {
			WriteLine(Severity.Warning, format, args);
		}

		/// <summary>
		/// Write error message into log
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Error(string format, params object[] args) {
			WriteLine(Severity.Error, format, args);
		}

		/// <summary>
		/// Write error message into log
		/// </summary>
		/// <param name="e">Exception caused engine error</param>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Error(Exception e, string format, params object[] args) {
			WriteLine(Severity.Error, string.Format(format, args) + " : " + e.Message);
			using (Up()) {
				WriteLine(Severity.StackTrace, e.StackTrace, new object[] { });
			}
		}

		/// <summary>
		/// Write critical panic message into log
		/// </summary>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Panic(string format, params object[] args) {
			WriteLine(Severity.Panic, format, args);
		}

		/// <summary>
		/// Write critical panic message into log
		/// </summary>
		/// <param name="e">Exception caused engine panic</param>
		/// <param name="format">The string format.</param>
		/// <param name="args">The args.</param>
		public static void Panic(Exception e, string format, params object[] args) {
			WriteLine(Severity.Panic, string.Format(format, args) + " : " + e.Message);
			using (Up()) {
				WriteLine(Severity.StackTrace, e.StackTrace, new object[] { });
			}
		}

		/// <summary>
		/// Writes the message into log.
		/// </summary>
		/// <param name="severity">The severity of message.</param>
		/// <param name="format">The string format.</param>
		/// <param name="args">The arguments.</param>
		[MethodImpl(MethodImplOptions.Synchronized)]
		public static void WriteLine(Severity severity, string format, params object[] args) {
			if (!_configured) {
				ConfigureDefault();
			}

			string message = string.Format(format, args);
			if (_writeConsoleEnabled) {
				if (severity == Severity.Details) {
					Console.Write("Details: ");
				}
				Console.WriteLine(message);
			}

			if (null != _logWriter) {
				_logWriter.WriteLine(_paddingLevel.Level, severity, message);
			}

			//store history in log
			_history.AddMessage(_paddingLevel.Level, severity, message);
		}

		/// <summary>
		/// Adds log level for a specified scope
		/// </summary>
		/// <returns></returns>
		public static IDisposable Up() {
			return new LogLevel(_paddingLevel);
		}

		#endregion

	}
}
