/*
<copyright file="BGPlaymakerSetGraphVarA.cs" company="BansheeGz">
    Copyright (c) 2018-2021 All Rights Reserved
</copyright>
*/


using BansheeGz.BGDatabase;
using HutongGames.PlayMaker;
using UnityEngine;

namespace BansheeGz.BGDatabase
{
    public abstract class BGPlaymakerSetGraphVarA : FsmStateAction
    {
        [RequiredField] [HutongGames.PlayMaker.Tooltip("Table name")]
        public FsmString metaName;

        [RequiredField] [HutongGames.PlayMaker.Tooltip("Field name")]
        public FsmString fieldName;

        [RequiredField] [HutongGames.PlayMaker.Tooltip("Entity index")]
        public FsmInt entityIndex;

        [RequiredField] [HutongGames.PlayMaker.Tooltip("Graph variable name")]
        public FsmString varName;

        public override void Reset()
        {
            metaName = null;
            fieldName = null;
            entityIndex = null;
            varName = null;
        }

        public override void OnEnter()
        {
            var metaNameValue = metaName.Value;
            var table = BGRepo.I[metaNameValue];
            if (table == null)
            {
                ExitWithWarning($"Cannot find meta with name={metaNameValue}");
                return;
            }

            var fieldNameValue = fieldName.Value;
            var field = table.GetField(fieldNameValue, false);
            if (field == null)
            {
                ExitWithWarning($"Cannot find field with name={fieldNameValue}, meta={table.Name}");
                return;
            }

            if (!(field is BGFieldCalcI fieldCalcI))
            {
                ExitWithWarning($"Field with name={field.FullName} is not a calculated field!");
                return;
            }

            var entityIndexValue = entityIndex.Value;
            if (entityIndexValue < 0 || entityIndexValue >= table.CountEntities)
            {
                ExitWithWarning($"Entity index={entityIndexValue} is in invalid range, valid range is 0-{table.CountEntities - 1}");
                return;
            }

            var varNameValue = varName.Value;

            var storable = (field as BGStorable<BGFieldCalcValue>);
            var value = storable.GetStoredValue(entityIndexValue);

            if (value?.Graph != null)
            {
                //row has a custom graph with it's own variables
                var targetVar = value.Graph.GetVars()?.GetVar(varNameValue);
                if (!VarIsValid(targetVar, varNameValue)) return;

                targetVar.Value = Value;
            }
            else
            {
                var fieldGraph = fieldCalcI.Graph;
                if (fieldGraph == null)
                {
                    ExitWithWarning($"Can not set graph variable, cause there is no graph!");
                    return;
                }

                var targetVar = fieldGraph.GetVars()?.GetVar(varNameValue);
                if (!VarIsValid(targetVar, varNameValue)) return;

                if (value == null)
                {
                    value = new BGFieldCalcValue(field, field.Meta.GetEntity(entityIndexValue));
                    storable.SetStoredValue(entityIndexValue, value);
                }

                var varRef = value.GetVar(targetVar.Id);
                if (varRef == null) varRef = value.AddVar(targetVar.Id);

                varRef.Value = Value;
            }

            Finish();
        }

        private bool VarIsValid(BGCalcVar targetVar, string varNameValue)
        {
            if (targetVar == null)
            {
                ExitWithWarning($"Can not find graph variable with name={varNameValue}!");
                return false;
            }

            if (!Equals(targetVar.TypeCode, VarType))
            {
                ExitWithWarning($"Can not set graph variable because of type mismatch: expected={targetVar.TypeCode.Name}, actual={VarType.Name}!");
                return false;
            }

            return true;
        }

        private void ExitWithWarning(string message)
        {
            Debug.LogWarning(message);
            Finish();
        }

        //================================================== Abstract
        protected abstract BGCalcTypeCode VarType { get; }
        protected abstract object Value { get; }
    }
}