using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;

namespace MvcExplorer.Models
{
    public static class ControlPages
    {
        private static List<ControlPageGroup> _pageGroups;
        private static List<ControlPage> _pages;
        private static List<ControlGroup> _controlGroups;
        private static IDictionary<string, ControlPage> _pageDic;
        private const string PagesFileInServer = "wwwroot/Content/ControlPages.xml";
        private static readonly object _locker = new object();

        public static IDictionary<string, string> GetPageSources(string controllerName, string actionName, string mapPath)
        {
            var pageSources = new Dictionary<string, string>();

            var controllerFileName = actionName + "Controller.cs";
            var controllerFilePath = string.Format("Controllers/{0}/{1}", controllerName, controllerFileName);
            var controllerFileHtml = GetResourceContent(controllerFilePath);
            pageSources.Add(controllerFileName, controllerFileHtml);

            var viewFileName = actionName + ".cshtml";
            var viewFilePath = Path.Combine(mapPath,
                string.Format("Views/{0}/{1}", controllerName, viewFileName));
            var viewFileHtml = GetFileAsHtmlContent(viewFilePath);
            pageSources.Add(viewFileName, viewFileHtml);

            return pageSources;
        }

        public static ControlPageGroup GetControlPageGroup(string controlName, string mapPath)
        {
            EnsurePageDic(mapPath);
            foreach (var pageGroup in _pageGroups)
            {
                if (pageGroup.ControlName.ToLower() == controlName.ToLower())
                    return pageGroup;
            }
            return null;
        }

        private static string GetFileAsHtmlContent(string controllerFilePath)
        {
            return System.IO.File.ReadAllText(controllerFilePath);
        }

        public static string GetPageText(string controllerName, string actionName, string mapPath)
        {
            EnsurePageDic(mapPath);
            var key = CreatePageKey(controllerName, actionName);
            return _pageDic.ContainsKey(key) ? _pageDic[key].Text : "OverView";
        }

        private static void EnsurePageDic(string mapPath)
        {
            if (_pageDic != null)
            {
                return;
            }

            _pageDic = new Dictionary<string, ControlPage>(StringComparer.OrdinalIgnoreCase);
            foreach (var page in GetPages(mapPath))
            {
                _pageDic.Add(CreatePageKey(page.ControlName, page.Name), page);
            }
        }

        private static string CreatePageKey(string controllerName, string actionName)
        {
            return string.Format("{0}-{1}", controllerName, actionName);
        }

        public static IEnumerable<ControlPage> GetPages(string mapPath)
        {
            EnsurePages(mapPath);
            return _pages;
        }

        public static IEnumerable<ControlGroup> GetControlGroups(string mapPath)
        {
            EnsurePages(mapPath);
            return _controlGroups;
        }

        private static void EnsurePages(string mapPath)
        {
            if (_pages != null)
            {
                return;
            }

            lock (_locker)
            {
                if (_pages != null)
                {
                    return;
                }

                using (var stream = GetPageConfigStream())
                {
                    var root = XElement.Load(stream);
                    _controlGroups = new List<ControlGroup>();
                    _pages = new List<ControlPage>();
                    _pageGroups = new List<ControlPageGroup>();
                    foreach (var controlElement in root.Elements("ControlGroup"))
                    {
                        var pageGroups = new List<ControlPageGroup>();
                        foreach (var pageElement in controlElement.Elements("Control"))
                        {
                            var currentControl = pageElement.Attribute("name").Value;
                            var docElement = pageElement.Attribute("documentation");
                            var documentation = docElement == null ? null : docElement.Value;
                            var pages = GetControlPagesFromXEle(pageElement, currentControl);
                            _pages.AddRange(pages);
                            pageGroups.Add(new ControlPageGroup
                            {
                                ControlName = currentControl,
                                Documentation = documentation,
                                Pages = pages
                            });
                        }
                        _pageGroups.AddRange(pageGroups);
                        ControlGroup controlGroup = new ControlGroup { GroupName = controlElement.Attribute("name").Value, controls = pageGroups };
                        _controlGroups.Add(controlGroup);
                    }
                }
            }
        }

        private static Stream GetPageConfigStream()
        {
            return GetResourceStream("ControlPages.xml");
        }

        private static Stream GetResourceStream(string name)
        {
            var assembly = typeof(ControlPages).GetTypeInfo().Assembly;
            var res = assembly.GetManifestResourceNames().Where(resName => resName.Contains(name)).ToList();
            if(res.Count == 0)
            {
                throw new ArgumentOutOfRangeException("name");
            }
            return assembly.GetManifestResourceStream(res[0]);
        }

        private static string GetResourceContent(string name)
        {
            using(var stream = GetResourceStream(name))
            {
                stream.Position = 0;
                using(var reader = new StreamReader(stream))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        private static List<ControlPage> GetControlPagesFromXEle(XElement controlElement, string controlName)
        {
            var pages = controlElement.Elements("action").Select(e =>
            {
                ControlPage page = new ControlPage
                {
                    Text = e.Attribute("text").Value,
                    Name = e.Attribute("name").Value,
                    ControlName = controlName
                };
                if (e.Element("subactions") != null)
                {
                    List<ControlPage> subPages = GetControlPagesFromXEle(e.Element("subactions"), controlName);
                    if (subPages.Count() > 0)
                    {
                        page.SubPages = subPages;
                    }
                }

                return page;
            }).ToList();
            return pages;
        }
    }

    public class ControlGroup
    {
        public IEnumerable<ControlPageGroup> controls { get; set; }
        public string GroupName { get; set; }
    }

    public class ControlPageGroup
    {
        private const string DocumentationRoot = "http://helpcentral.componentone.com/nethelp/c1mvchelpers";
        private string _documentation;

        public string Documentation
        {
            get
            {
                if (string.IsNullOrEmpty(_documentation))
                {
                    return DocumentationRoot;
                }

                return _documentation;
            }
            set { _documentation = value; }
        }

        public IEnumerable<ControlPage> Pages { get; set; }
        public string ControlName { get; set; }
        public int GetSelectedPageIndex(string actionName)
        {
            int sIndex = 0, index = 0;
            foreach (var page in Pages)
            {
                if (page.Name.ToLower() == actionName.ToLower())
                    return index;
                index++;
            }
            return sIndex;
        }
    }

    public class ControlPage
    {
        public string Name { get; set; }
        public string Text { get; set; }
        public string Path { get; set; }
        public string ControlName { get; set; }
        public List<ControlPage> SubPages { get; set; }
    }
}