/*
 * 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.Explorer.Report;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace Sasa.QualityTools.DrFx.Explorer
{
    /// <summary>
    /// DrFx Explorer のレポート表示画面を表します。
    /// </summary>
    public partial class FxCopReportWindow : Window
    {
        /// <summary>
        /// 読み込んだ FxCop レポートを表す依存プロパティ。
        /// </summary>
        public static readonly DependencyProperty FxCopReportProperty = DependencyProperty.Register("FxCopReport", typeof(FxCopReport), typeof(FxCopReportWindow));

        /// <summary>
        /// ルール違反情報と、ルール違反対象や違反したルールの詳細情報の全リレーションのリスト。
        /// </summary>
        private List<FxCopIssueRelationship> relationships = new List<FxCopIssueRelationship>();

        /// <summary>
        /// 画面上で現在選択中の違反対象についての、ルール違反情報と、ルール違反対象や違反したルールの詳細情報のリレーションのリスト。
        /// </summary>
        private ObservableCollection<FxCopIssueRelationship> currentRelationships = new ObservableCollection<FxCopIssueRelationship>();



        /// <summary>
        /// 読み込んだ FxCop レポート。
        /// </summary>
        public FxCopReport FxCopReport
        {
            get
            {
                return (FxCopReport)GetValue(FxCopReportProperty);
            }
            set
            {
                SetValue(FxCopReportProperty, value);
            }
        }

        /// <summary>
        /// 画面上で現在選択中の違反対象についての、ルール違反情報と、ルール違反対象や違反したルールの詳細情報のリレーションのリスト。
        /// </summary>
        public ObservableCollection<FxCopIssueRelationship> CurrentRelationships
        {
            get
            {
                return this.currentRelationships;
            }
        }



        /// <summary>
        /// <see cref="Sasa.QualityTools.DrFx.Explorer.FxCopReportWindow"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        public FxCopReportWindow()
        {
            InitializeComponent();
        }



        /// <summary>
        /// 画面表示時に、画面情報を初期化します。
        /// </summary>
        /// <param name="e">イベントデータ。</param>
        protected override void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);

            if (FxCopReport == null)
            {
                throw new InvalidOperationException("この画面を Open する前に、FxCopReport プロパティに値を設定してください。");
            }

            Title = "FxCop レポート - " + FxCopReport.ReportFile;
            this.relationships.AddRange(CreateRelationships(FxCopReport));
        }

        /// <summary>
        /// ルール違反情報と、ルール違反対象や違反したルールの詳細情報のリレーションのコレクションを作成します。。
        /// </summary>
        /// <param name="report">リレーションコレクションの作成に使用する、FxCop レポートの情報。</param>
        internal static IEnumerable<FxCopIssueRelationship> CreateRelationships(FxCopReport report)
        {
            List<FxCopIssueRelationship> relationships = new List<FxCopIssueRelationship>();
            foreach (AssemblyWarning assembly in report.Assemblies)
            {
                foreach (FxCopIssueRelationship relationship in CreateRelationships(assembly))
                {
                    relationships.Add(relationship);
                }
            }

            foreach (NamespaceWarning nameSpace in report.Namespaces)
            {
                foreach (FxCopIssueRelationship relationship in CreateRelationships(nameSpace))
                {
                    relationships.Add(relationship);
                }
            }

            foreach (FxCopIssueRelationship relationship in relationships)
            {
                relationship.Rule = report.Rules[relationship.Issue.CheckId];
            }

            return relationships;
        }

        /// <summary>
        /// 指定されたアセンブリに関するルール違反情報のリレーションシップコレクションを作成します。
        /// </summary>
        /// <param name="assembly">FxCop ルールに違反した (もしくはルール違反した型などを含む) アセンブリ。</param>
        /// <returns>ルール違反情報のリレーションシップコレクション。</returns>
        internal static IEnumerable<FxCopIssueRelationship> CreateRelationships(AssemblyWarning assembly)
        {
            foreach (FxCopIssue issue in assembly.Issues)
            {
                yield return new FxCopIssueRelationship { Assembly = assembly, Issue = issue, Target = WarningTarget.Assembly };
            }
            foreach (NamespaceWarning nameSpace in assembly.Namespaces)
            {
                foreach (FxCopIssueRelationship relationship in CreateRelationships(nameSpace))
                {
                    relationship.Assembly = assembly;
                    yield return relationship;
                }
            }
        }

        /// <summary>
        /// 指定された名前空間に関するルール違反情報のリレーションシップコレクションを作成します。
        /// </summary>
        /// <param name="nameSpace">FxCop ルールに違反した (もしくはルール違反した型などを含む) 名前空間。</param>
        /// <returns>ルール違反情報のリレーションシップコレクション。</returns>
        internal static IEnumerable<FxCopIssueRelationship> CreateRelationships(NamespaceWarning nameSpace)
        {
            foreach (FxCopIssue issue in nameSpace.Issues)
            {
                yield return new FxCopIssueRelationship { Namespace = nameSpace, Issue = issue, Target = WarningTarget.Namespace };
            }
            foreach (TypeWarning type in nameSpace.Types)
            {
                foreach (FxCopIssueRelationship relationship in CreateRelationships(type))
                {
                    relationship.Namespace = nameSpace;
                    yield return relationship;
                }
            }
        }

        /// <summary>
        /// 指定された型に関するルール違反情報のリレーションシップコレクションを作成します。
        /// </summary>
        /// <param name="type">FxCop ルールに違反した (もしくはルール違反したメンバーを含む) 型。</param>
        /// <returns>ルール違反情報のリレーションシップコレクション。</returns>
        internal static IEnumerable<FxCopIssueRelationship> CreateRelationships(TypeWarning type)
        {
            foreach (FxCopIssue issue in type.Issues)
            {
                yield return new FxCopIssueRelationship { Type = type, Issue = issue, Target = WarningTarget.Type };
            }
            foreach (MemberWarning member in type.Members)
            {
                foreach (FxCopIssueRelationship relationship in CreateRelationships(member))
                {
                    relationship.Type = type;
                    yield return relationship;
                }
            }
        }

        /// <summary>
        /// 指定されたメンバーに関するルール違反情報のリレーションシップコレクションを作成します。
        /// </summary>
        /// <param name="member">FxCop ルールに違反したメンバー。</param>
        /// <returns>ルール違反情報のリレーションシップコレクション。</returns>
        internal static IEnumerable<FxCopIssueRelationship> CreateRelationships(MemberWarning member)
        {
            foreach (FxCopIssue issue in member.Issues)
            {
                yield return new FxCopIssueRelationship { Member = member, Issue = issue, Target = WarningTarget.Member };
            }
        }

        /// <summary>
        /// 画面上で選択された違反対象に対応する違反情報コレクションを作成します。
        /// </summary>
        /// <param name="sender">イベント送信元オブジェクト。</param>
        /// <param name="e">イベントデータ。</param>
        private void ShowIssues(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = (TreeViewItem)sender;

            Predicate<FxCopIssueRelationship> predicate = null;
            if (item.DataContext is AssemblyWarning)
            {
                AssemblyWarning assembly = (AssemblyWarning)item.DataContext;
                predicate = r => r.Assembly == assembly;
            }
            else if (item.DataContext is NamespaceWarning)
            {
                NamespaceWarning nameSpace = (NamespaceWarning)item.DataContext;
                predicate = r => r.Namespace == nameSpace;
            }
            else if (item.DataContext is TypeWarning)
            {
                TypeWarning type = (TypeWarning)item.DataContext;
                predicate = r => r.Type == type;
            }
            else if (item.DataContext is MemberWarning)
            {
                MemberWarning member = (MemberWarning)item.DataContext;
                predicate = r => r.Member == member;
            }

            this.currentRelationships.Clear();
            if (predicate != null)
            {
                foreach (FxCopIssueRelationship relationship in this.relationships.FindAll(predicate))
                {
                    this.currentRelationships.Add(relationship);
                }
            }

            e.Handled = true;
        }
    }
}
