namespace VideoDistanc
{
    using System;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    using System.Net;

    /// <summary>
    /// Controls when a stack trace should be displayed on the
    /// Error Window
    /// 
    /// Defaults to <see cref="OnlyWhenDebuggingOrRunningLocally"/>
    /// </summary>
    public enum StackTracePolicy
    {
        /// <summary>
        ///   Stack trace is showed only when running with a debugger attached
        ///   or when running the app on the local machine. Use this to get
        ///   additional debug information you don't want the end users to see
        /// </summary>
        OnlyWhenDebuggingOrRunningLocally,

        /// <summary>
        /// Always show the stack trace, even if debugging
        /// </summary>
        Always,

        /// <summary>
        /// Never show the stack trace, even when debugging
        /// </summary>
        Never
    }

    /// <summary>
    /// <see cref="ChildWindow"/> class that displays errors to the user.
    /// </summary>
    public partial class ErrorWindow : ChildWindow
    {
        /// <summary>
        /// Creates a new <see cref="ErrorWindow"/> instance.
        /// </summary>
        /// <param name="message">The error message to display.</param>
        /// <param name="errorDetails">Extra information about the error.</param>
        protected ErrorWindow(string message, string errorDetails)
        {
            InitializeComponent();
            IntroductoryText.Text = message;
            ErrorTextBox.Text = errorDetails;
        }

        #region Factory Shortcut Methods
        /// <summary>
        /// Creates a new Error Window given an error message.
        /// Current stack trace will be displayed if app is running under
        /// debug or on the local machine.
        /// </summary>
        /// <param name="message">The message to display.</param>
        public static void CreateNew(string message)
        {
            CreateNew(message, StackTracePolicy.OnlyWhenDebuggingOrRunningLocally);
        }

        /// <summary>
        /// Creates a new Error Window given an exception.
        /// Current stack trace will be displayed if app is running under
        /// debug or on the local machine
        /// 
        /// The exception is converted onto a message using
        /// <see cref="ConvertExceptionToMessage"/>
        /// </summary>
        /// <param name="exception">The exception to display.</param>
        public static void CreateNew(Exception exception)
        {
            CreateNew(exception, StackTracePolicy.OnlyWhenDebuggingOrRunningLocally);
        }

        /// <summary>
        /// Creates a new Error Window given an exception. The exception is converted onto a message using
        /// <see cref="ConvertExceptionToMessage"/>
        /// </summary>    
        /// <param name="exception">The exception to display.</param>
        /// <param name="policy">When to display the stack trace, see <see cref="StackTracePolicy"/>.</param>
        public static void CreateNew(Exception exception, StackTracePolicy policy)
        {
            if (exception == null)
            {
                throw new ArgumentNullException("exception");
            }

            string fullStackTrace = exception.StackTrace;

            // Account for nested exceptions
            Exception innerException = exception.InnerException;
            while (innerException != null)
            {
                fullStackTrace += "\nCaused by: " + exception.Message + "\n\n" + exception.StackTrace;
                innerException = innerException.InnerException;
            }

            CreateNew(ConvertExceptionToMessage(exception), fullStackTrace, policy);
        }

        /// <summary>
        /// Creates a new Error Window given an error message.
        /// </summary>   
        /// <param name="message">The message to display.</param>
        /// <param name="policy">When to display the stack trace, see <see cref="StackTracePolicy"/>.</param>
        public static void CreateNew(string message, StackTracePolicy policy)
        {
            CreateNew(message, new StackTrace().ToString(), policy);
        }
        #endregion

        #region Factory Methods
        /// <summary>
        /// All other factory methods will result in a call to this one
        /// </summary>
        /// 
        /// <param name="message">Which message to display</param>
        /// <param name="stackTrace">The associated stack trace</param>
        /// <param name="policy">In which situations the stack trace should be appended to the message</param>
        private static void CreateNew(string message, string stackTrace, StackTracePolicy policy)
        {
            string errorDetails = string.Empty;

            if (policy == StackTracePolicy.Always ||
                policy == StackTracePolicy.OnlyWhenDebuggingOrRunningLocally && IsRunningUnderDebugOrLocalhost)
            {
                errorDetails = stackTrace ?? string.Empty;
            }

            ErrorWindow window = new ErrorWindow(message, errorDetails);
            window.Show();
        }
        #endregion

        #region Factory Helpers
        /// <summary>
        /// Returns whether running under a dev environment, i.e., with a debugger attached or
        /// with the server hosted on localhost
        /// </summary>
        private static bool IsRunningUnderDebugOrLocalhost
        {
            get
            {
                if (Debugger.IsAttached)
                {
                    return true;
                }
                else
                {
                    string hostUrl = Application.Current.Host.Source.Host;
                    return hostUrl.Contains("::1") || hostUrl.Contains("localhost") || hostUrl.Contains("127.0.0.1");
                }
            }
        }

        /// <summary>
        /// Creates a user friendly message given an Exception. Currently this simply
        /// takes the Exception.Message value, optionally  but you might want to change this to treat
        /// some specific Exception classes differently
        /// </summary>
        /// <param name="e">The exception to convert.</param>
        private static string ConvertExceptionToMessage(Exception e)
        {
            return e.Message;
        }
        #endregion

        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = true;
        }
    }
}