using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Vortex.Wrapper;
using System.IO;

namespace Vortex.Debugging {

	/// <summary>
	/// Watcher is debugging class for checking engine execution parameters during lifetime
	/// </summary>
	public static class Watcher {

		/// <summary>
		/// Enables tracking of unhandled exceptions.
		/// </summary>
		public static void TrackUnhandledExceptions() {
			AppDomain.CurrentDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs e) {
				Exception exception = e.ExceptionObject as Exception;
				if (null != exception) {
					Log.Error(exception, "Unhandled application exception");
				}
				if (e.IsTerminating) {
					Log.Error("Application will be terminated...");
				}
			};
		}

		/// <summary>
		/// Checks the specified result.
		/// </summary>
		/// <param name="result">The result.</param>
		/// <param name="operation">The operation was executed.</param>
		/// <param name="args">The args.</param>
		public static void Assert(HResult result, string operation, params object[] args) {
			if (result.IsFailed) {
				string preMessage = string.Format("API call failed, HRESULT = {0} : ", result.Value);
				Log.Error(preMessage + operation, args);
				using (Log.Up()) {
					Log.WriteLine(Severity.StackTrace, DecorateStackTrace(new StackTrace(1, true)));
				}
			}
		}

		/// <summary>
		/// Checks the specified result of critical operation.
		/// </summary>
		/// <param name="result">The result.</param>
		/// <param name="operation">The operation was executed.</param>
		/// <param name="args">The args.</param>
		public static void AssertCritical(HResult result, string operation, params object[] args) {
			if (result.IsFailed) {
				string preMessage = string.Format("Required API call failed, HRESULT = {0} : ", result.Value);
				Log.Panic(preMessage + operation, args);
				using (Log.Up()) {
					Log.WriteLine(Severity.StackTrace, DecorateStackTrace(new StackTrace(1, true)));
				}
				throw new Exception(string.Format(preMessage + operation, args));
			}
		}

		/// <summary>
		/// Decorates the stack trace in better format.
		/// </summary>
		/// <param name="stackTrace">The stack trace.</param>
		/// <returns></returns>
		private static string DecorateStackTrace(StackTrace stackTrace) {
			StringBuilder builder = new StringBuilder();
			foreach (StackFrame frame in stackTrace.GetFrames()) {
				string fileName = frame.GetFileName();
				if (!string.IsNullOrEmpty(fileName)) {
					builder.AppendFormat("> {0} @ {1} : {2}\n", frame.GetMethod(), fileName, frame.GetFileLineNumber());
				}
			}
			return builder.ToString();
		}

		/// <summary>
		/// Crushes the application with specified message
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="args">The args.</param>
		public static void Crush(string message, params object[] args) {
			Log.Panic(message, args);
			using (Log.Up()) {
				Log.WriteLine(Severity.StackTrace, DecorateStackTrace(new StackTrace(1, true)));
			}
			throw new Exception(string.Format(message, args));
		}

		public static void AssertNotEmpty(string str, string name) {
			if (string.IsNullOrEmpty(str)) {
				Log.Panic("{0} can not be null or empty", name);
				using (Log.Up()) {
					Log.WriteLine(Severity.StackTrace, DecorateStackTrace(new StackTrace(1, true)));
				}
			}
		}

		/// <summary>
		/// Check file name for being not null and not empty string.
		/// </summary>
		/// <param name="fileName">File name</param>
		public static void AssertFileName(string fileName) {
			if (string.IsNullOrEmpty(fileName)) {
				throw new FileNotFoundException("File name can't be empty or null string");
			}
		}
	}
}
