using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

namespace BansheeGz.BGDatabase
{
    [Serializable]
    [IgnoreBinder]
    public class BGDBUiElementBinderGraph : BGDBUiElementBinderA
    {
        [SerializeField] public string graphContent;
        [SerializeField] public byte typeCode;


        private BGCalcGraph graph;
        private BGCalcFlowEvents events;

        private GameObject go;
        private UIDocument doc;
        private bool attachBatchListeners;

        public BGCalcTypeCode TypeCode
        {
            get => typeCode == 0 ? BGCalcTypeCodeRegistry.String : BGCalcTypeCodeRegistry.Get(typeCode);
            set => typeCode = value?.TypeCode ?? BGCalcTypeCodeRegistry.String.TypeCode;
        }

        public BGCalcGraph Graph
        {
            get
            {
                if (graph != null) return graph;
                if (string.IsNullOrEmpty(graphContent))
                {
                    graph = BGCalcGraph.NewGraph(TypeCode);
                }
                else
                {
                    graph = BGCalcGraph.ExistingGraph();
                    graph.FromBytes(Convert.FromBase64String(graphContent));
                }

                return graph;
            }
            set => this.graph = value;
        }

        public override string Title => $"Graph ({TypeCode.Name})";


        public override object Value
        {
            get
            {
                this.RemoveFieldsListeners();
                if (LiveUpdate && (Application.isPlaying || BGUtil.TestIsRunning))
                {
                    if (events == null) events = new BGCalcFlowEvents(MyBind);
                    events.AddBatchListeners = this.attachBatchListeners;
                }
                else events = null;

                var context = BGCalcFlowContext.Get();
                try
                {
                    context.CurrentGameObject = go;
                    context.GraphType = BGCalcGraphTypeEnum.GraphBinder;
                    context.Events = events;
                    var flow = Graph.Launch(context);
                    events?.AddListeners();
                    return flow.Result;
                }
                finally
                {
                    BGCalcFlowContext.Return(context);
                }
            }
        }

        public void RemoveFieldsListeners() => events?.Clear();

        private string MyBind()
        {
            Bind(doc);
            return null;
        }

        public override Type ValueType => typeof(string);

        public void SetContext(GameObject go, UIDocument doc, bool attachBatchListeners)
        {
            this.go = go;
            this.doc = doc;
            this.attachBatchListeners = attachBatchListeners;
        }

        public override void ReverseBind(UIDocument doc)
        {
            //not supported (not possible to implement)
        }

        public class GraphFactory : Factory
        {
            private readonly BGCalcTypeCode typeCode;

            public override string BinderName => $"Graph ({typeCode.Name})";

            internal GraphFactory(BGCalcTypeCode typeCode) : base(typeCode.Type) => this.typeCode = typeCode;

            public override BGDBUiElementBinderA Create() => new BGDBUiElementBinderGraph() { typeCode = typeCode.TypeCode };
        }

        public class GraphProvider : FactoryProvider
        {
            List<Factory> FactoryProvider.Factories => new()
            {
                new GraphFactory(BGCalcTypeCodeRegistry.String),
                new GraphFactory(BGCalcTypeCodeRegistry.Bool),
                new GraphFactory(BGCalcTypeCodeRegistry.Int),
                new GraphFactory(BGCalcTypeCodeRegistry.Float),
                new GraphFactory(BGCalcTypeCodeRegistry.Object),
            };
        }
    }
}