using System;
using System.Collections.Generic;
using FastReport.Table;

namespace FastReport.AdvMatrix
{
    internal class HeaderDataList : List<HeaderData>
    {
        private ValueComparer comparer;
        private HeaderData compareItem;
        public HeaderData Parent { get; private set; }
        public HeaderDescriptor Descriptor { get; private set; }
        public bool Visible { get; set; }
        private AdvMatrixObject Matrix {  get { return Descriptor.Matrix; } }

        public int Find(object value)
        {
            if (Count == 0)
                return -1;

            compareItem.Value = value;
            if (Descriptor.Sort == SortOrder.None)
            {
                for (int i = 0; i < Count; i++)
                {
                    if (comparer.Compare(compareItem, this[i]) == 0)
                        return i;
                }
                return ~Count;
            }
            else
            {
                return BinarySearch(compareItem, comparer);
            }
        }

        public List<HeaderData> GetSortedList(bool useInteractiveSort = true)
        {
            InteractiveSortInfo info = Descriptor.Matrix.Data.InteractiveSort;
            if (Descriptor.InteractiveSort && useInteractiveSort &&
                ((Descriptor.IsColumn && info.Row.Sort != SortOrder.None) ||
                (!Descriptor.IsColumn && info.Column.Sort != SortOrder.None)))
                return SortByInteractiveInfo();
            else if (!String.IsNullOrEmpty(Descriptor.SortToggledBy) && useInteractiveSort)
                return SortByToggleButton();
            else if (Descriptor.SortAggregate != null)
                return SortByAggregateValue();
            else
                return this;
        }

        private List<HeaderData> SortByToggleButton()
        {
            List<HeaderData> list = new List<HeaderData>();
            list.AddRange(this);
            MatrixSortButton btn = Matrix.Report.FindObject(Descriptor.SortToggledBy) as MatrixSortButton;
            if (btn != null)
                list.Sort(new ValueComparer(btn.Sort));
            return list;
        }

        private List<HeaderData> SortByAggregateValue()
        {
            List<HeaderData> list = new List<HeaderData>();
            list.AddRange(this);
            list.Sort(new AggregateValueComparer(Descriptor));
            return list;
        }

        private List<HeaderData> SortByInteractiveInfo()
        {
            List<HeaderData> list = new List<HeaderData>();
            list.AddRange(this);

            InteractiveSortInfo.SortInfo info;
            TableCell cell;
            if (Descriptor.IsColumn)
            {
                info = Matrix.Data.InteractiveSort.Row;
                cell = Matrix[Descriptor.TemplateColumn.Index, info.Descriptor.TemplateRow.Index];
            }
            else
            {
                info = Matrix.Data.InteractiveSort.Column;
                cell = Matrix[info.Descriptor.TemplateColumn.Index, Descriptor.TemplateRow.Index];
            }

            AggregateExpressionPair aggr = Descriptor.InteractiveSortAggregate;
            if (aggr == null)
            {
                // no aggregate was set - use the first aggregate in a cell
                CellDescriptor cellDescriptor = cell.GetDescriptor();
                if (cellDescriptor.Aggregates.Count > 0)
                    aggr = cellDescriptor.Aggregates[0];
            }
            if (aggr != null)
                list.Sort(new InteractiveComparer(aggr, info));

            return list;
        }

        public HeaderDataList(HeaderData parent, HeaderDescriptor descr)
        {
            Parent = parent;
            Descriptor = descr;
            Visible = descr.Visible;
            comparer = new ValueComparer(descr.Sort);
            compareItem = new HeaderData(null, null);
        }

        private class ValueComparer : IComparer<HeaderData>
        {
            private SortOrder sort;

            public int Compare(HeaderData x, HeaderData y)
            {
                int result = 0;
                IComparable i1 = x.Value as IComparable;
                IComparable i2 = y.Value as IComparable;

                if (i1 != null)
                    result = i1.CompareTo(i2);
                else if (i2 != null)
                    result = -1;
                if (sort == SortOrder.Descending)
                    result = -result;

                return result;
            }

            public ValueComparer(SortOrder sort)
            {
                this.sort = sort;
            }
        }

        private class AggregateValueComparer : IComparer<HeaderData>
        {
            private HeaderDescriptor descriptor;
            private AggregateExpressionPair aggr;

            public int Compare(HeaderData x, HeaderData y)
            {
                int result = 0;

                IComparable i1 = aggr.GetValue(x.Index, 0) as IComparable;
                IComparable i2 = aggr.GetValue(y.Index, 0) as IComparable;

                if (i1 != null)
                    result = i1.CompareTo(i2);
                else if (i2 != null)
                    result = -1;
                if (descriptor.Sort == SortOrder.Descending)
                    result = -result;

                return result;
            }

            public AggregateValueComparer(HeaderDescriptor descriptor)
            {
                this.descriptor = descriptor;
                aggr = descriptor.SortAggregate;
            }
        }

        private class InteractiveComparer : IComparer<HeaderData>
        {
            private AggregateExpressionPair aggr;
            private InteractiveSortInfo.SortInfo info;

            private object GetValue(HeaderData data)
            {
                if (info.IsColumn)
                    return aggr.GetValue(info.Index, data.Index);
                else
                    return aggr.GetValue(data.Index, info.Index);
            }

            public int Compare(HeaderData x, HeaderData y)
            {
                int result = 0;

                IComparable i1 = GetValue(x) as IComparable;
                IComparable i2 = GetValue(y) as IComparable;

                if (i1 != null)
                    result = i1.CompareTo(i2);
                else if (i2 != null)
                    result = -1;
                if (info.Sort == SortOrder.Descending)
                    result = -result;

                return result;
            }

            public InteractiveComparer(AggregateExpressionPair aggr, InteractiveSortInfo.SortInfo info)
            {
                this.aggr = aggr;
                this.info = info;
            }
        }
    }
}