/*
 * DrFx - FxCop Report Translator and Visualizer.
 * Copyright (C) 2010 Sasa Yuan
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */



using Sasa.QualityTools.DrFx.Core.Translation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Linq;

namespace Sasa.QualityTools.DrFx.Explorer.Rule
{
    public class FxCopRuleLoader
    {
        private Collection<string> assemblyPaths = new Collection<string>();



        public Collection<string> AssemblyPaths
        {
            get
            {
                return this.assemblyPaths;
            }
        }



        public void Translate(IEnumerable<FxCopRule> rules)
        {
            List<DrFxRule> translationRules = new List<DrFxRule>(DrFxRuleLoader.Load());
            var lookup = translationRules.ToLookup(r => r.CheckId);

            foreach (FxCopRule rule in rules)
            {
                if (lookup.Contains(rule.CheckId))
                {
                    rule.Name = lookup[rule.CheckId].ElementAt(0).Name;
                }
            }
        }

        public Collection<FxCopRule> Load()
        {
            Collection<FxCopRule> rules = new Collection<FxCopRule>();

            foreach (string path in AssemblyPaths)
            {
                try
                {
                    foreach (FxCopRule rule in LoadFrom(path))
                    {
                        rules.Add(rule);
                    }
                }
                catch (IOException)
                {
                    // TODO: Logging
                }
            }

            return rules;
        }

        internal static Collection<FxCopRule> LoadFrom(string assemblyPath)
        {
            Collection<FxCopRule> rules = new Collection<FxCopRule>();

            Assembly assembly = Assembly.LoadFrom(assemblyPath);
            string[] resourceNames = assembly.GetManifestResourceNames();
            using (Stream stream = assembly.GetManifestResourceStream(resourceNames[0]))
            {
                XmlDocument document = new XmlDocument();
                document.Load(stream);
                foreach (XmlNode node in document.SelectNodes("/Rules/Rule"))
                {
                    try
                    {
                        FxCopRule rule = CreateRule(node);
                        rule.AssemblyName = assembly.GetName().Name;
                        rules.Add(rule);
                    }
                    catch (InvalidOperationException)
                    {
                        // TODO: Logging
                    }
                }
            }

            return rules;
        }

        internal static FxCopRule CreateRule(XmlNode ruleNode)
        {
            Validate(ruleNode);

            FxCopRule rule = new FxCopRule();
            rule.CheckId = ruleNode.Attributes["CheckId"].Value;
            rule.Category = ruleNode.Attributes["Category"].Value;
            rule.TypeName = ruleNode.Attributes["TypeName"].Value;
            rule.Name = ruleNode["Name"].InnerText;
            return rule;
        }

        private static void Validate(XmlNode ruleNode)
        {
            if (ruleNode.Attributes["CheckId"] == null)
            {
                throw new InvalidOperationException("Rule ノードが不正です。CheckId 属性がありません。");
            }
            if (ruleNode.Attributes["Category"] == null)
            {
                throw new InvalidOperationException("Rule ノードが不正です。Category 属性がありません。");
            }
            if (ruleNode.Attributes["TypeName"] == null)
            {
                throw new InvalidOperationException("Rule ノードが不正です。TypeName 属性がありません。");
            }
            if (ruleNode["Name"] == null)
            {
                throw new InvalidOperationException("Rule ノードが不正です。Name 要素がありません。");
            }
        }
    }
}
