/*
 * 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.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace Sasa.QualityTools.DrFx.Explorer
{
    /// <summary>
    /// FxCop のルール違反の詳細を表示するためのウェジットを表すクラスです。
    /// </summary>
    public partial class FxCopIssueDetailWidget : UserControl
    {
        /// <summary>
        /// ルール違反情報のリレーションシップを表す依存プロパティ。
        /// </summary>
        public static DependencyProperty FxCopIssueRelationshipProperty = DependencyProperty.Register("FxCopIssueRelationship", typeof(FxCopIssueRelationship), typeof(FxCopIssueDetailWidget), new PropertyMetadata(OnFxCopIssueRelationshipChanged));

        /// <summary>
        /// ルール違反したソースコード行前後のソースコード行のコレクション。
        /// </summary>
        private ObservableCollection<SourceCodeLine> lines = new ObservableCollection<SourceCodeLine>();



        /// <summary>
        /// ルール違反情報のリレーションシップ。
        /// </summary>
        public FxCopIssueRelationship FxCopIssueRelationship
        {
            get
            {
                return (FxCopIssueRelationship)GetValue(FxCopIssueRelationshipProperty);
            }
            set
            {
                SetValue(FxCopIssueRelationshipProperty, value);

            }
        }

        /// <summary>
        /// ルール違反したソースコード行前後のソースコード行のコレクション。
        /// </summary>
        public ObservableCollection<SourceCodeLine> Lines
        {
            get
            {
                return this.lines;
            }
        }



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



        /// <summary>
        /// <paramref name="filePath"/> で指定されたファイルから、<paramref name="lineNumber"/> で指定された
        /// 付近のソースコード行を取得します。
        /// </summary>
        /// <param name="filePath">ファイルパス。</param>
        /// <param name="lineNumber">行番号。</param>
        /// <returns>指定された行番号前後のソースコード行のリスト。</returns>
        internal static List<SourceCodeLine> GetViolationCodeLines(string filePath, int lineNumber)
        {
            if (String.IsNullOrEmpty(filePath) || File.Exists(filePath) == false || lineNumber <= 0)
            {
                return new List<SourceCodeLine>();
            }

            int start = (lineNumber < 6) ? 0 : lineNumber -6;
            int end = lineNumber + 5;

            List<SourceCodeLine> lines = new List<SourceCodeLine>();
            try
            {
                using (StreamReader reader = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)))
                {
                    for (int i = 0; i < start; i++)
                    {
                        if (reader.ReadLine() == null)
                        {
                            return new List<SourceCodeLine>();
                        }
                    }

                    string line;
                    for (int i = start; i < end; i++)
                    {
                        line = reader.ReadLine();
                        if (line == null)
                        {
                            break;
                        }
                        lines.Add(new SourceCodeLine { LineNumber = i + 1, Text = line.Replace(Environment.NewLine, String.Empty), IsViolationLine = (i + 1 == lineNumber) });
                    }
                }
            }
            catch (IOException)
            {
                throw new NotImplementedException();
            }
            catch (NotSupportedException)
            {
                throw new NotImplementedException();
            }

            return lines;
        }

        /// <summary>
        /// ルール違反リレーションシップの値が変わった場合に、ルール違反箇所付近のソースコードを取得しなおします。
        /// </summary>
        /// <param name="sender">イベント送信元オブジェクト。</param>
        /// <param name="e">イベントデータ。</param>
        private static void OnFxCopIssueRelationshipChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            FxCopIssueDetailWidget widget = (FxCopIssueDetailWidget)sender;
            FxCopIssueRelationship relationship = e.NewValue as FxCopIssueRelationship;
            if (relationship == null)
            {
                return;
            }

            widget.lines.Clear();
            if (relationship.Issue.Line.HasValue)
            {
                foreach (SourceCodeLine line in GetViolationCodeLines(relationship.Issue.FilePath, relationship.Issue.Line.Value))
                {
                    widget.lines.Add(line);
                }
            }
        }
    }
}
