using System;
using System.Collections.Generic;
using System.Linq;

namespace FastReport.AdvMatrix
{
    internal static class HeaderDataExt
    {
        // Returns the first item of a group. For simple type items:
        // - returns an item in the first parent group
        // - if there is no parent group, or useThisGroup parameter is true, returns the first item of a group in the same level
        internal static HeaderData GetFirstItem(this HeaderData item, bool useIneractiveSort, bool useThisGroup)
        {
            if (item.Parent == null)
                return null;

            if (item.Value == null)
            {
                HeaderData parentGroup = GetFirstItem(item.Parent, useIneractiveSort, useThisGroup);
                if (parentGroup == null || useThisGroup)
                {
                    // return the first item of a group in the same level
                    foreach (HeaderDataList dl in item.Parent.Items)
                    {
                        if (dl.Descriptor.IsGroup)
                            return dl.GetSortedList(useIneractiveSort).FirstOrDefault();
                    }
                }
                else
                {
                    // return an item in the parent group
                    foreach (HeaderDataList dl in parentGroup.Items)
                    {
                        if (dl.Descriptor == item.Descriptor)
                            return dl[0];
                    }
                }
                return null;
            }

            return item.ParentDataList.GetSortedList(useIneractiveSort).FirstOrDefault();
        }

        // The same as above but with the last item
        internal static HeaderData GetLastItem(this HeaderData item, bool useIneractiveSort, bool useThisGroup)
        {
            if (item.Parent == null)
                return null;

            if (item.Value == null)
            {
                HeaderData parentGroup = GetLastItem(item.Parent, useIneractiveSort, useThisGroup);
                if (parentGroup == null || useThisGroup)
                {
                    foreach (HeaderDataList dl in item.Parent.Items)
                    {
                        if (dl.Descriptor.IsGroup)
                            return dl.GetSortedList(useIneractiveSort).LastOrDefault();
                    }
                }
                else
                {
                    foreach (HeaderDataList dl in parentGroup.Items)
                    {
                        if (dl.Descriptor == item.Descriptor)
                            return dl[0];
                    }
                }
                return null;
            }

            return item.ParentDataList.GetSortedList(useIneractiveSort).LastOrDefault();
        }

        // The same as above but with the previous item
        internal static HeaderData GetPreviousItem(this HeaderData item, bool useIneractiveSort, bool useThisGroup)
        {
            if (item.Parent == null)
                return null;

            if (item.Value == null)
            {
                HeaderData parentGroup = GetPreviousItem(item.Parent, useIneractiveSort, useThisGroup);
                if (parentGroup == null || useThisGroup)
                {
                    foreach (HeaderDataList dl in item.Parent.Items)
                    {
                        if (dl.Descriptor.IsGroup)
                        {
                            List<HeaderData> l = dl.GetSortedList(useIneractiveSort);
                            if (l.Count > 1)
                                return l[l.Count - 2];
                            return null;
                        }
                    }
                }
                else
                {
                    foreach (HeaderDataList dl in parentGroup.Items)
                    {
                        if (dl.Descriptor == item.Descriptor)
                            return dl[0];
                    }
                }
                return null;
            }

            List<HeaderData> list = item.ParentDataList.GetSortedList(useIneractiveSort);
            int index = list.IndexOf(item);
            if (index != -1)
            {
                if (index == 0)
                    return null;
                return list[index - 1];
            }

            return null;
        }

        // The same as above but with the next item
        internal static HeaderData GetNextItem(this HeaderData item, bool useIneractiveSort, bool useThisGroup)
        {
            if (item.Parent == null)
                return null;

            if (item.Value == null)
            {
                HeaderData parentGroup = GetNextItem(item.Parent, useIneractiveSort, useThisGroup);
                if (parentGroup == null || useThisGroup)
                {
                    foreach (HeaderDataList dl in item.Parent.Items)
                    {
                        if (dl.Descriptor.IsGroup)
                        {
                            List<HeaderData> l = dl.GetSortedList(useIneractiveSort);
                            if (l.Count > 1)
                                return l[1];
                            return null;
                        }
                    }
                }
                else
                {
                    foreach (HeaderDataList dl in parentGroup.Items)
                    {
                        if (dl.Descriptor == item.Descriptor)
                            return dl[0];
                    }
                }
                return null;
            }

            List<HeaderData> list = item.ParentDataList.GetSortedList(useIneractiveSort);
            int index = list.IndexOf(item);
            if (index != -1)
            {
                if (index == list.Count - 1)
                    return null;
                return list[index + 1];
            }

            return null;
        }

        // The same as above but with the specific item
        internal static HeaderData GetSpecificItem(this HeaderData item, object value)
        {
            if (item.Parent == null)
                return null;

            if (item.Value == null)
            {
                foreach (HeaderDataList dl in item.Parent.Items)
                {
                    if (dl.Descriptor.IsGroup)
                    {
                        HeaderData d = GetSpecificItem(dl[0], value);
                        if (d != null)
                            return d;
                    }
                }
            }
            else
            {
                foreach (HeaderData d in item.ParentDataList)
                {
                    if (d.Value.Equals(value))
                        return d;
                }
            }
            
            return null;
        }

        internal static HeaderDataList GetGroup(this HeaderData item)
        {
            HeaderData item1 = item;
            
            // trying to find a parent group
            while (item1 != null && item1.Value == null)
                item1 = item1.Parent;
            if (item1 != null)
                return item1.ParentDataList;

            // trying to find a group on the same level
            item1 = item.Parent;
            while (item1 != null)
            {
                foreach (HeaderDataList dl in item1.Items)
                {
                    if (dl.Descriptor.IsGroup)
                        return dl;
                }
                item1 = item1.Parent;
            }
            
            return null;
        }

        internal static HeaderData FindByIndex(this HeaderData item, int index)
        {
            if (item.Index == index)
                return item;
            // do not use root enumerator here, avoid additional sorting which may lead to SO
            foreach (HeaderDataList dl in item.Items)
            {
                foreach (HeaderData d in dl)
                {
                    HeaderData result = FindByIndex(d, index);
                    if (result != null)
                        return result;
                }
            }
            return null;
        }

        internal static void ToggleVisible(this HeaderData item, int index, bool collapseAll, bool expandAll)
        {
            item = item.FindByIndex(index);
            if (item == null)
                return;
            MatrixCollapseButton btn = item.Descriptor.TemplateCell.GetCollapseButton();
            if (btn == null)
                return;

            Action<HeaderData, bool, bool> func = (root, visible, skip) =>
            {
                if (root == null)
                    return;
                foreach (HeaderDataList dl in root.Items)
                {
                    foreach (HeaderData d in dl)
                    {
                        if (d == item && skip)
                            continue;
                        foreach (HeaderDataList dl1 in btn.GetLinkedItems(d))
                        {
                            dl1.Visible = visible;
                        }
                    }
                }
            };

            if (collapseAll)
            {
                func(item.Parent, false, false);
                return;
            }
            if (expandAll)
            {
                func(item.Parent, true, false);
                return;
            }

            if (btn.Exclusive)
            {
                func(item.Parent, false, true);
            }

            foreach (HeaderDataList dl in btn.GetLinkedItems(item))
            {
                dl.Visible = !dl.Visible;
            }
        }

    }
}