using System.Collections.Specialized;
using Godot;
using ObservableCollections;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
using SharpIDE.Godot.Features.Problems;

namespace SharpIDE.Godot;

public static class ControlExtensions
{
    // extension(Control control)
    // {
    //     public void BindChildren(ObservableHashSet<SharpIdeProjectModel> list, PackedScene scene)
    //     {
    //         var view = list.CreateView(x =>
    //         {
    //             var node = scene.Instantiate<ProblemsPanelProjectEntry>();
    //             node.Project = x;
    //             Callable.From(() => control.AddChild(node)).CallDeferred();
    //             return node;
    //         });
    //         view.ViewChanged += OnViewChanged;
    //     }
    //     private static void OnViewChanged(in SynchronizedViewChangedEventArgs<SharpIdeProjectModel, ProblemsPanelProjectEntry> eventArgs)
    //     {
    //         GD.Print("View changed: " + eventArgs.Action);
    //         if (eventArgs.Action == NotifyCollectionChangedAction.Remove)
    //         {
    //             eventArgs.OldItem.View.QueueFree();
    //         }
    //     }
    // }
}

public static class NodeExtensions
{
    extension(TreeItem treeItem)
    {
        public T? GetTypedMetadata<T>(int column) where T : RefCounted?
        {
            var metadata = treeItem.GetMetadata(column);
            var refCountedMetadata = metadata.As<RefCounted?>();
            if (refCountedMetadata is T correctTypeContainer)
            {
                return correctTypeContainer;
            }
            return null;
        }
    }
    extension(Node node)
    {
        public void QueueFreeChildren()
        {
            foreach (var child in node.GetChildren())
            {
                child.QueueFree();
            }
        }
        public void RemoveChildAndQueueFree(Node child)
        {
            node.RemoveChild(child);
            child.QueueFree();
        }
        public Task<T> InvokeAsync<T>(Func<T> workItem)
        {
            var taskCompletionSource = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
            Dispatcher.SynchronizationContext.Post(_ =>
            {
                try
                {
                    var result = workItem();
                    taskCompletionSource.SetResult(result);
                }
                catch (Exception ex)
                {
                    taskCompletionSource.SetException(ex);
                }
            }, null);
            return taskCompletionSource.Task;
        }
        public Task InvokeAsync(Action workItem)
        {
            var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
            //WorkerThreadPool.AddTask();
            Dispatcher.SynchronizationContext.Post(_ =>
            {
                try
                {
                    workItem();
                    taskCompletionSource.SetResult();
                }
                catch (Exception ex)
                {
                    taskCompletionSource.SetException(ex);
                }
            }, null);
            return taskCompletionSource.Task;
        }
        
        public Task InvokeAsync(Func<Task> workItem)
        {
            var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
            Dispatcher.SynchronizationContext.Post(async void (_) =>
            {
                try
                {
                    await workItem();
                    taskCompletionSource.SetResult();
                }
                catch (Exception ex)
                {
                    taskCompletionSource.SetException(ex);
                }
            }, null);
            return taskCompletionSource.Task;
        }
        
        public Task InvokeDeferredAsync(Action workItem)
        {
            var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
            //WorkerThreadPool.AddTask();
            Callable.From(() =>
            {
                try
                {
                    workItem();
                    taskCompletionSource.SetResult();
                }
                catch (Exception ex)
                {
                    taskCompletionSource.SetException(ex);
                }
            }).CallDeferred();
            return taskCompletionSource.Task;
        }
        
        public Task InvokeDeferredAsync(Func<Task> workItem)
        {
            var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
            //WorkerThreadPool.AddTask();
            Callable.From(async void () =>
            {
                try
                {
                    await workItem();
                    taskCompletionSource.SetResult();
                }
                catch (Exception ex)
                {
                    taskCompletionSource.SetException(ex);
                }
            }).CallDeferred();
            return taskCompletionSource.Task;
        }
    }
}

public static class GodotTask
{
    extension(Task task)
    {
        public static async Task GodotRun(Action action)
        {
            await Task.Run(() =>
            {
                try
                {
                    action();
                }
                catch (Exception ex)
                {
                    GD.PrintErr($"Error: {ex}");
                }
            });
        }
    
        public static async Task GodotRun(Func<Task> action)
        {
            await Task.Run(async () =>
            {
                try
                {
                    await action();
                }
                catch (Exception ex)
                {
                    GD.PrintErr($"Error: {ex}");
                }
            });
        }
    }
}