using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Data;
using System.Xml;

namespace SqlDynamite
{
    /// <summary>
    /// Interaction logic for WPFAboutBox.xaml
    /// </summary>
    public sealed partial class WpfAboutBox
    {
        /// <summary>
        /// Default constructor is protected so callers must use one with a parent.
        /// </summary>
        public WpfAboutBox()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Constructor that takes a parent for this WPFAboutBox dialog.
        /// </summary>
        /// <param name="parent">Parent window for this dialog.</param>
        public WpfAboutBox(Window parent)
            : this()
        {
            Owner = parent;
        }

        /// <summary>
        /// Handles click navigation on the hyperlink in the About dialog.
        /// </summary>
        /// <param name="sender">Object the sent the event.</param>
        /// <param name="e">Navigation events arguments.</param>
        private void hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
        {
            if (e.Uri != null && string.IsNullOrWhiteSpace(e.Uri.OriginalString) == false)
            {
                string uri = e.Uri.AbsoluteUri;
                Process.Start(new ProcessStartInfo(uri));
                e.Handled = true;
            }
        }

        #region AboutData Provider
        #region Member data
        private XmlDocument _xmlDoc;

        private const string PropertyNameTitle = "Title";
        private const string PropertyNameDescription = "Description";
        private const string PropertyNameProduct = "Product";
        private const string PropertyNameCopyright = "Copyright";
        private const string PropertyNameCompany = "Company";
        private const string XPathRoot = "ApplicationInfo/";
        private const string XPathTitle = XPathRoot + PropertyNameTitle;
        private const string XPathVersion = XPathRoot + "Version";
        private const string XPathDescription = XPathRoot + PropertyNameDescription;
        private const string XPathProduct = XPathRoot + PropertyNameProduct;
        private const string XPathCopyright = XPathRoot + PropertyNameCopyright;
        private const string XPathCompany = XPathRoot + PropertyNameCompany;
        private const string XPathLink = XPathRoot + "Link";
        private const string XPathLinkUri = XPathRoot + "Link/@Uri";
        #endregion

        #region Properties
        /// <summary>
        /// Gets the title property, which is display in the About dialogs window title.
        /// </summary>
        public string ProductTitle
        {
            get
            {
                string result = CalculatePropertyValue<AssemblyTitleAttribute>(PropertyNameTitle, XPathTitle);
                if (string.IsNullOrWhiteSpace(result))
                {
                    // otherwise, just get the name of the assembly itself.
                    result = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
                }
                return result;
            }
        }

        /// <summary>
        /// Gets the application's version information to show.
        /// </summary>
        public string Version
        {
            get
            {
                // first, try to get the version string from the assembly.
                Version assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version;
                return assemblyVersion?.ToString() ?? GetLogicalResourceString(XPathVersion);
            }
        }

        /// <summary>
        /// Gets the description about the application.
        /// </summary>
        public string Description => CalculatePropertyValue<AssemblyDescriptionAttribute>(PropertyNameDescription, XPathDescription);

        /// <summary>
        ///  Gets the product's full name.
        /// </summary>
        public string Product => CalculatePropertyValue<AssemblyProductAttribute>(PropertyNameProduct, XPathProduct);

        /// <summary>
        /// Gets the copyright information for the product.
        /// </summary>
        public string Copyright => CalculatePropertyValue<AssemblyCopyrightAttribute>(PropertyNameCopyright, XPathCopyright);

        /// <summary>
        /// Gets the product's company name.
        /// </summary>
        public string Company => CalculatePropertyValue<AssemblyCompanyAttribute>(PropertyNameCompany, XPathCompany);

        /// <summary>
        /// Gets the link text to display in the About dialog.
        /// </summary>
        public string LinkText => GetLogicalResourceString(XPathLink);

        /// <summary>
        /// Gets the link uri that is the navigation target of the link.
        /// </summary>
        public string LinkUri => GetLogicalResourceString(XPathLinkUri);

        #endregion

        #region Resource location methods
        /// <summary>
        /// Gets the specified property value either from a specific attribute, or from a resource dictionary.
        /// </summary>
        /// <typeparam name="T">Attribute type that we're trying to retrieve.</typeparam>
        /// <param name="propertyName">Property name to use on the attribute.</param>
        /// <param name="xpathQuery">XPath to the element in the XML data resource.</param>
        /// <returns>The resulting string to use for a property.
        /// Returns null if no data could be retrieved.</returns>
        private string CalculatePropertyValue<T>(string propertyName, string xpathQuery)
        {
            string result = string.Empty;
            // first, try to get the property value from an attribute.
            object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(T), false);
            if (attributes.Length > 0)
            {
                T attrib = (T)attributes[0];
                PropertyInfo property = attrib.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
                if (property != null)
                {
                    result = property.GetValue(attributes[0], null) as string;
                }
            }

            // if the attribute wasn't found or it did not have a value, then look in an xml resource.
            if (result == string.Empty)
            {
                // if that fails, try to get it from a resource.
                result = GetLogicalResourceString(xpathQuery);
            }
            return result;
        }

        /// <summary>
        /// Gets the XmlDataProvider's document from the resource dictionary.
        /// </summary>
        private XmlDocument ResourceXmlDocument
        {
            get
            {
                // if we haven't already found the resource XmlDocument, then try to find it.
                if (_xmlDoc == null && TryFindResource("aboutProvider") is XmlDataProvider provider)
                {
                    // save away the XmlDocument, so we don't have to get it multiple times.
                    _xmlDoc = provider.Document;
                }

                return _xmlDoc;
            }
        }

        /// <summary>
        /// Gets the specified data element from the XmlDataProvider in the resource dictionary.
        /// </summary>
        /// <param name="xpathQuery">An XPath query to the XML element to retrieve.</param>
        /// <returns>The resulting string value for the specified XML element. 
        /// Returns empty string if resource element couldn't be found.</returns>
        private string GetLogicalResourceString(string xpathQuery)
        {
            string result = string.Empty;
            // get the About xml information from the resources.
            XmlDocument doc = ResourceXmlDocument;
            // if we found the XmlDocument, then look for the specified data. 
            XmlNode node = doc?.SelectSingleNode(xpathQuery);
            if (node != null)
            {
                result = node is XmlAttribute ? node.Value : node.InnerText;
            }
            return result;
        }
        #endregion

        private void okButton_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }
        #endregion
    }
}
