using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using nft.framework;
using System.Collections;
using System.Drawing;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace nft.test
{
    public class TestEntryListView : ListView
    {
        protected ListViewItemComparer columnComparer = new ListViewItemComparer();
        protected int resultColumn;
        protected int stateColumn;
        public ToolStripLabel statusLabel = null;
        protected bool ignoreStatusUpdate = false;
        private static readonly string EntryStartLogText = "vvvvvv Start of {0} vvvvvv";
        private ImageList iconList;
        private static readonly string EntryEndLogText = "^^^^^^ End of {0} ^^^^^^";

        public TestEntryListView() {
            InitListView();
            InitImageList();
        }

        private void InitListView() {
            View = View.Details;
            Sorting = SortOrder.Descending;
            OwnerDraw = true;
            ListViewItemSorter = columnComparer;
            CheckBoxes = true;
            AddColumn("Name",100);
            AddColumn("ID", -2);
            AddColumn("Class", -2);
            AddColumn("Method", -2);
            AddColumn("Arguments", -2);
            AddColumn("State", 16);
            AddColumn("Result", 100);

            stateColumn = Columns["State"].Index;
            resultColumn = Columns["Result"].Index;
            DrawSubItem += new DrawListViewSubItemEventHandler(TestEntryListView_DrawSubItem);
            DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(TestEntryListView_DrawColumnHeader);
            ItemChecked += new ItemCheckedEventHandler(TestEntryListView_ItemChecked);
            //listEntryView.Columns
        }

        private void InitImageList() {
            iconList = new ImageList();
            Bitmap img = Properties.Resources.listicons;
            img.MakeTransparent(Color.Magenta);
            iconList.Images.AddStrip(img);            
        }

        #region EventHandlers
        void TestEntryListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) {
            e.DrawDefault = true;
        }

        void TestEntryListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) {
            if (e.Header.Index != stateColumn) {
                e.DrawDefault = true;
            } else {
                TestInfo info = e.Item.Tag as TestInfo;
                Image img = iconList.Images[(int)info.TestState];

                Rectangle cellRect = e.Bounds;
                if (this.GridLines) {
                    cellRect.Height--;
                    cellRect.Width--;
                    cellRect.X++;
                }
                e.Graphics.SetClip(cellRect);
                if (e.Item.Selected) {
                    e.Graphics.FillRectangle(SystemBrushes.Highlight, cellRect);

                } else {
                    e.DrawBackground();
                }
                Rectangle icoRect = new Rectangle(cellRect.X, cellRect.Y, img.Width, img.Height);
                e.Graphics.DrawImage(img, icoRect);
            }
        }

        void TestEntryListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
            if (!ignoreStatusUpdate) {
                UpdateStatusLabel();
            }
        }

        protected override void OnColumnClick(ColumnClickEventArgs e) {
            base.OnColumnClick(e);
            columnComparer.Column = e.Column;
            Sort();
        }
        #endregion

        protected void UpdateStatusLabel() {
            if (statusLabel == null) return;
            int errs = 0;
            int chk = 0;
            foreach (ListViewItem item in Items) {
                if (item.Checked) chk++;
                if (((TestInfo)item.Tag).IsError) errs++;
            }
            int total = Items.Count;
            string s = String.Format("Checked={0} Errors={1} Total={2}", chk, errs, total);
            statusLabel.Text = s;
        }

        protected void AddColumn(string name_and_key, int width) {
            Columns.Add(name_and_key, name_and_key, width);
        }

        public void AddTestInfo(TestInfo info) {
            ListViewItem item = new ListViewItem();
            item.Tag = info;
            item.Text = info.TestAttribute.Caption;
            item.SubItems.Add(info.ID);
            item.SubItems.Add(info.Method.DeclaringType.Name);
            item.SubItems.Add(info.Method.Name);
            item.SubItems.Add(info.DumpArguments());
            item.SubItems.Add(GetStatusText(info));
            item.SubItems.Add("");
            
            Items.Add(item);
        }

        public void AdjustColumns() {
            Columns["Name"].Width = -2;
            Columns["Class"].Width = -2;
            Columns["Method"].Width = -2;
            if (Items.Count > 0) {
                ColumnHeader h = Columns[stateColumn];
                h.Width = GetItemRect(0).Height+2;
            }
            int wiall = ClientSize.Width;
            foreach (ColumnHeader h in Columns) {
                if (h.Name.Equals("Result")) continue;
                wiall -= h.Width;
            }
            Columns[resultColumn].Width = wiall;
            UpdateStatusLabel();
        }

        #region menu commands
        public void ReverseCheckAll() {
            ignoreStatusUpdate = true;
            foreach (ListViewItem item in Items) {
                item.Checked = !item.Checked;
            }
            ignoreStatusUpdate = false;
            UpdateStatusLabel();
        }

        public void ReverseCheckSelected() {
            ignoreStatusUpdate = true;
            foreach (ListViewItem item in SelectedItems) {
                item.Checked = !item.Checked;
            }
            ignoreStatusUpdate = false;
            UpdateStatusLabel();
        }

        public void SetCheckSelected(bool b) {
            ignoreStatusUpdate = true;
            foreach (ListViewItem item in SelectedItems) {
                item.Checked = b;
            }
            UpdateStatusLabel();
            ignoreStatusUpdate = false;
        }

        public void RunCheckedEntries() {
            ignoreStatusUpdate = true;
            foreach (ListViewItem item in Items) {
                if (item.Checked) {
                    Run(item);
                }
            }
            ignoreStatusUpdate = false;
            UpdateStatusLabel();
        }

        public void RunSelectedEntries() {
            ignoreStatusUpdate = true;
            foreach (ListViewItem item in SelectedItems) {
                Run(item);
            }
            ignoreStatusUpdate = false;
            UpdateStatusLabel();
        }

        public async void Run(ListViewItem item) {
            Debug.WriteLine(StartLogText(item));
            TestInfo info = (TestInfo)item.Tag;
            if (info.TestAttribute.ShouldRunOnUIThread)
            {
                info.Run();
                AfterRun(item);
                return;
            }
            var o = Observable.Start(async () =>
            {
                await (Task)info.Run();
            });
            await o.ObserveOn(SynchronizationContext.Current).Finally(() => AfterRun(item));
        }
        #endregion

        private void AfterRun(ListViewItem item)
        {
            TestInfo info = (TestInfo)item.Tag;
            item.SubItems[stateColumn].Text = GetStatusText(info);
            Debug.WriteLineIf(info.TestState != TestState.Success, "### ERROR ###");
            item.SubItems[resultColumn].Text = "" + info.LastResult;
            if (info.LastResult is Exception)
            {
                Exception ex = (Exception)info.LastResult;
                Debug.WriteLine(ex.GetType().Name + ":" + ex.Message);
                Debug.WriteLine(ex.StackTrace);
            }
            Debug.WriteLine(EndLogText(item));
            Debug.WriteLine("");
            if (!ignoreStatusUpdate)
            {
                UpdateStatusLabel();
            }
        }

        #region static utils
        static protected string GetStatusText(TestInfo info) {
            return String.Format("{0}:{1}",
                (int)info.TestState,
                Enum.GetName(typeof(TestState), info.TestState)
                );
        }

        public string StartLogText(ListViewItem item) {
            TestInfo info = (TestInfo)item.Tag;
            return String.Format(EntryStartLogText, info.ID);
        }

        public string EndLogText(ListViewItem item) {
            TestInfo info = (TestInfo)item.Tag;
            return String.Format(EntryEndLogText, info.ID);
        }
        #endregion
    }

    public class ListViewItemComparer : IComparer
    {
        /// <summary>
        /// 比較する方法
        /// </summary>
        public enum ComparerMode
        {
            String,
            Integer,
            DateTime
        };

        private int _column;
        private SortOrder _order;
        private ComparerMode _mode = ComparerMode.String;

        /// <summary>
        /// 並び替えるListView列の番号
        /// </summary>
        public int Column {
            set {
                if (_column == value) {
                    if (_order == SortOrder.Ascending)
                        _order = SortOrder.Descending;
                    else if (_order == SortOrder.Descending)
                        _order = SortOrder.Ascending;
                }
                _column = value;
            }
            get {
                return _column;
            }
        }
        /// <summary>
        /// 昇順か降順か
        /// </summary>
        public SortOrder Order {
            set {
                _order = value;
            }
            get {
                return _order;
            }
        }
        /// <summary>
        /// 並び替えの方法
        /// </summary>
        public ComparerMode Mode {
            set {
                _mode = value;
            }
            get {
                return _mode;
            }
        }

        /// <summary>
        /// ListViewItemComparerクラスのコンストラクタ
        /// </summary>
        /// <param name="col">並び替える列番号</param>
        /// <param name="ord">昇順か降順か</param>
        /// <param name="cmod">並び替えの方法</param>
        public ListViewItemComparer(int col, SortOrder ord, ComparerMode cmod) {
            _column = col;
            _order = ord;
            _mode = cmod;
        }
        public ListViewItemComparer() {
            _column = 0;
            _order = SortOrder.Ascending;
            _mode = ComparerMode.String;
        }

        // xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返す
        public int Compare(object x, object y) {
            int result = 0;
            // ListViewItemの取得
            ListViewItem itemx = (ListViewItem)x;
            ListViewItem itemy = (ListViewItem)y;

            // 並び替えの方法別に、xとyを比較する
            switch (_mode) {
                case ComparerMode.String:
                    result = string.Compare(itemx.SubItems[_column].Text, itemy.SubItems[_column].Text);
                    break;
                case ComparerMode.Integer:
                    result = int.Parse(itemx.SubItems[_column].Text) - int.Parse(itemy.SubItems[_column].Text);
                    break;
                case ComparerMode.DateTime:
                    result = DateTime.Compare(DateTime.Parse(itemx.SubItems[_column].Text), DateTime.Parse(itemy.SubItems[_column].Text));
                    break;
            }

            // 降順の時は結果を+-逆にする
            if (_order == SortOrder.Descending)
                result = -result;
            else if (_order == SortOrder.None)
                result = 0;

            // 結果を返す
            return result;
        }
    }
}
