using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace Pixiview.Utils
{
    public static class Extensions
    {
        public static T Binding<T>(this T view, BindableProperty property, string name,
            BindingMode mode = BindingMode.Default, IValueConverter converter = null) where T : BindableObject
        {
            if (name == null)
            {
                view.SetValue(property, property.DefaultValue);
            }
            else
            {
                view.SetBinding(property, name, mode, converter);
            }
            return view;
        }

        public static T DynamicResource<T>(this T view, BindableProperty property, string key) where T : Element
        {
            view.SetDynamicResource(property, key);
            return view;
        }

        public static T GridRow<T>(this T view, int row) where T : BindableObject
        {
            Grid.SetRow(view, row);
            return view;
        }

        public static T GridColumn<T>(this T view, int column) where T : BindableObject
        {
            Grid.SetColumn(view, column);
            return view;
        }

        public static T GridColumnSpan<T>(this T view, int columnSpan) where T : BindableObject
        {
            Grid.SetColumnSpan(view, columnSpan);
            return view;
        }

        public static int IndexOf<T>(this T[] array, Predicate<T> predicate)
        {
            for (var i = 0; i < array.Length; i++)
            {
                if (predicate(array[i]))
                {
                    return i;
                }
            }
            return -1;
        }

        public static int LastIndexOf<T>(this T[] array, Predicate<T> predicate)
        {
            for (var i = array.Length - 1; i >= 0; i--)
            {
                if (predicate(array[i]))
                {
                    return i;
                }
            }
            return -1;
        }

        public static bool All<T>(this T[] array, Predicate<T> predicate)
        {
            for (var i = 0; i < array.Length; i++)
            {
                if (!predicate(array[i]))
                {
                    return false;
                }
            }
            return true;
        }

        public static bool AnyFor<T>(this T[] array, int from, int to, Predicate<T> predicate)
        {
            for (var i = from; i <= to; i++)
            {
                if (predicate(array[i]))
                {
                    return true;
                }
            }
            return false;
        }
    }

    public class ParallelTask : IDisposable
    {
        public static ParallelTask Start(string tag, int from, int toExclusive, int maxCount, Predicate<int> action, Action complete = null)
        {
            if (toExclusive <= from)
            {
                if (complete != null)
                {
                    Task.Run(complete);
                }
                return null;
            }
            var task = new ParallelTask(tag, from, toExclusive, maxCount, action, complete);
            Task.Run(task.Start);
            //ThreadPool.QueueUserWorkItem(task.Start);
            return task;
        }

        private readonly object sync = new object();
        private int count;
        private bool disposed;

        private readonly string tag;
        private readonly int max;
        private readonly int from;
        private readonly int to;
        private readonly Predicate<int> action;
        private readonly Action complete;

        private ParallelTask(string tag, int from, int to, int maxCount, Predicate<int> action, Action complete)
        {
            if (maxCount <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(maxCount));
            }
            max = maxCount;
            if (from >= to)
            {
                throw new ArgumentOutOfRangeException(nameof(from));
            }
            this.tag = tag;
            this.from = from;
            this.to = to;
            this.action = action;
            this.complete = complete;
        }

        private void Start()
        {
            var list = new List<Task>();
            for (int i = from; i < to; i++)
            {
                var index = i;
                while (true)
                {
                    if (count < max)
                    {
                        break;
                    }
                    if (disposed)
                    {
#if LOG
                        App.DebugPrint($"parallel task determinate, disposed ({tag})");
#endif
                        return;
                    }
                    Thread.Sleep(100);
                }
                lock (sync)
                {
                    count++;
                }
                list.Add(Task.Run(() =>
                {
                    try
                    {
                        if (!action(index))
                        {
                            disposed = true;
                        }
                    }
                    catch (Exception ex)
                    {
                        App.DebugError("parallel.start ({tag})", $"failed to run action, index: {i}, error: {ex.Message}");
                    }
                    finally
                    {
                        lock (sync)
                        {
                            count--;
                        }
                    }
                }));
            }
            while (count > 0)
            {
                if (disposed)
                {
#if LOG
                    App.DebugPrint($"parallel task determinate, disposed ({tag})");
#endif
                    return;
                }
                Thread.Sleep(100);
            }
#if LOG
            App.DebugPrint($"parallel task done ({tag})");
#endif
            complete?.Invoke();
        }

        public void Dispose()
        {
            disposed = true;
        }
    }

    public static class Screen
    {
        private const string StatusBarStyle = nameof(StatusBarStyle);
        private const string HomeIndicatorAutoHidden = nameof(HomeIndicatorAutoHidden);

        public static readonly BindableProperty StatusBarStyleProperty = BindableProperty.CreateAttached(
            StatusBarStyle,
            typeof(StatusBarStyles),
            typeof(Page),
            StatusBarStyles.WhiteText);
        public static StatusBarStyles GetStatusBarStyle(VisualElement page) => (StatusBarStyles)page.GetValue(StatusBarStyleProperty);
        public static void SetStatusBarStyle(VisualElement page, StatusBarStyles value) => page.SetValue(StatusBarStyleProperty, value);

        public static readonly BindableProperty HomeIndicatorAutoHiddenProperty = BindableProperty.CreateAttached(
            HomeIndicatorAutoHidden,
            typeof(bool),
            typeof(Shell),
            false);
        public static bool GetHomeIndicatorAutoHidden(VisualElement page) => (bool)page.GetValue(HomeIndicatorAutoHiddenProperty);
        public static void SetHomeIndicatorAutoHidden(VisualElement page, bool value) => page.SetValue(HomeIndicatorAutoHiddenProperty, value);
    }

    public enum StatusBarStyles
    {
        Default,
        // Will behave as normal. 
        // White text on black NavigationBar/in iOS Dark mode and 
        // Black text on white NavigationBar/in iOS Light mode
        DarkText,
        // Will switch the color of content of StatusBar to black. 
        WhiteText,
        // Will switch the color of content of StatusBar to white. 
        Hidden
        // Will hide the StatusBar
    }
}
