using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using UnityEditor;
using UnityObject = UnityEngine.Object;

namespace Unity.VisualScripting
{
    public abstract class GraphContext<TGraph, TCanvas> : IGraphContext
        where TGraph : class, IGraph
        where TCanvas : class, ICanvas
    {
        protected GraphContext(GraphReference reference)
        {
            Ensure.That(nameof(reference)).IsNotNull(reference);
            Ensure.That(nameof(reference.graph)).IsOfType<TGraph>(reference.graph);

            this.reference = reference;

            analyserProvider = new AnalyserProvider(reference);
            graphMetadata = Metadata.Root().StaticObject(reference.graph);
            extensions = this.Extensions().ToList().AsReadOnly();
            sidebarPanels = SidebarPanels().ToList().AsReadOnly();
        }

        public GraphReference reference { get; }

        public AnalyserProvider analyserProvider { get; }

        public TCanvas canvas => (TCanvas)graph.Canvas();

        ICanvas IGraphContext.canvas => canvas;

        public GraphSelection selection => canvas.selection;

        public TGraph graph => (TGraph)reference.graph;

        IGraph IGraphContext.graph => graph;

        public Metadata graphMetadata { get; }

        public Metadata selectionMetadata => selection.Count == 1 ? ElementMetadata(selection.Single()) : null;

        public Metadata ElementMetadata(IGraphElement element)
        {
            // Static object is faster than indexer
            // return graphMetadata[nameof(IGraph.elements)].Indexer(element.guid).Cast(element.GetType());
            return graphMetadata[nameof(IGraph.elements)].StaticObject(element);
        }

        public ReadOnlyCollection<IGraphContextExtension> extensions { get; }

        IEnumerable<IGraphContextExtension> IGraphContext.extensions => extensions;

        public virtual string windowTitle => "Graph";

        protected virtual IEnumerable<ISidebarPanelContent> SidebarPanels() => Enumerable.Empty<ISidebarPanelContent>();

        public ReadOnlyCollection<ISidebarPanelContent> sidebarPanels { get; }

        IEnumerable<ISidebarPanelContent> IGraphContext.sidebarPanels => sidebarPanels;

        public bool isPrefabInstance => reference.serializedObject?.IsConnectedPrefabInstance() ?? false;

        #region Lifecycle

        public virtual void Dispose()
        {
            // Manually free the providers so that the
            // disposable decorators free their event handlers

            analyserProvider?.FreeAll();
        }

        public void BeginEdit(bool disablePrefabInstance = true)
        {
            LudiqEditorUtility.editedObject.BeginOverride(reference.serializedObject);
            LudiqGraphsEditorUtility.editedContext.BeginOverride(this);
            EditorGUI.BeginDisabledGroup(disablePrefabInstance && isPrefabInstance);
            EditorGUI.BeginChangeCheck();
        }

        public void EndEdit()
        {
            if (EditorGUI.EndChangeCheck())
            {
                DescribeAndAnalyze();
            }

            EditorGUI.EndDisabledGroup();
            LudiqGraphsEditorUtility.editedContext.EndOverride();
            LudiqEditorUtility.editedObject.EndOverride();
        }

        public void DescribeAndAnalyze()
        {
            foreach (var element in graph.elements)
            {
                if (element.HasDescriptor())
                {
                    element.Describe();
                }
            }

            graph.Describe();

            analyserProvider.AnalyzeAll();

            reference.parent.Describe();
        }

        #endregion
    }
}