using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
using UniRx;
using KairoEngine.Core;
namespace KairoEngine.Core.GameActions
{
[System.Serializable]
public class GameActionsController : ISerializationCallbackReceiver
{
/// A context object that holds a list of GameActionContextVariables to be accessible for GameActions to use.
[InlineProperty, HideLabel, ShowIf("showContext")]
public GameActionContext context = new GameActionContext();
[HideInInspector] public bool showContext = false;
/// A list of GameActions to be executed in order.
[ListDrawerSettings(HideAddButton = true, HideRemoveButton = false, DraggableItems = true, Expanded = false, ShowPaging = false, ShowItemCount = true)]
[ShowInInspector, NonSerialized, OnInspectorInit("InitializeActionList")]
[Space(4)]
public List actions = new List();
[NonSerialized] private CompositeDisposable cancel;
private int actionIndex = 0;
public bool started => _started;
public bool isDone => _done;
private bool _started = false;
private bool _done = false;
[HideInInspector] public int depth = 0;
#region FlowControl
/// Run GameActions on this controller. Each action will be fired after the previous action ended.
public void Start()
{
actionIndex = 0;
_done = false;
_started = true;
var observable = Observable.EveryUpdate();
cancel = new CompositeDisposable();
observable.Subscribe(xs => ExecuteActions()).AddTo(cancel);
}
/// Executes GameActions until it has to wait for an Update or reach the end of the action list.
private void ExecuteActions()
{
if(actionIndex >= actions.Count)
{
// done
_done = true;
cancel.Dispose();
return;
}
if(actions[actionIndex].done)
{
actionIndex += 1;
ExecuteActions();
}
else
{
if(actions[actionIndex].started == false)
{
actions[actionIndex].Start();
ExecuteActions();
}
else actions[actionIndex].Update();
}
}
/// Stop executing GameActions on this controller.
public void Stop()
{
if(cancel != null) cancel.Dispose();
}
public void Restart()
{
actionIndex = 0;
_started = false;
_done = false;
foreach (var action in actions) action.Restart();
}
#endregion
#region Serialization
[SerializeField, HideInInspector] private List serializedActions = new List();
[SerializeField, HideInInspector] public GameActionObjectSerializer objectSerializer = new GameActionObjectSerializer();
private void SerializeActions()
{
if(actions == null) return;
serializedActions = new List();
objectSerializer.ClearGameObjects();
objectSerializer.ClearScriptableObjects();
for (int i = 0; i < actions.Count; i++)
{
actions[i].OnBeforeSerialize(objectSerializer, i, depth);
string data = JsonUtility.ToJson(actions[i]);
serializedActions.Add(data);
}
}
private void DeserializeActions()
{
if(serializedActions == null) return;
if(actions == null) actions = new List();
if(actions.Count > 0) actions.Clear();
for (int i = 0; i < serializedActions.Count; i++)
{
GameAction action = JsonUtility.FromJson(serializedActions[i]);
action = GameAction.InvokeStringMethod(action.className, action.typeName, serializedActions[i]);
action.controller = this;
action.OnBeforeDeserialize(objectSerializer, i, depth);
actions.Add(action);
// Type t = Type.GetType(action.typeName);
// dynamic obj = Convert.ChangeType(action, t);
// if(obj != null) actions.Add(obj.Duplicate());
// else if(action != null) actions.Add(action);
}
for (int i = 0; i < actions.Count; i++)
{
actions[i].Initialize(objectSerializer);
}
objectSerializer.ClearGameObjects();
objectSerializer.ClearScriptableObjects();
//serializedActions = new List();
}
public void OnBeforeSerialize() => SerializeActions();
public void OnAfterDeserialize() => DeserializeActions();
#endregion
#region NewAction
[OnInspectorInit("GetCompatibleVariablenames"), OnValueChanged("AddNewAction")]
[ValueDropdown("possibleActions", IsUniqueList = false)]
[LabelText("Add New Action")]
[NonSerialized]
public GameAction newAction;
[NonSerialized] private IEnumerable possibleActions = new ValueDropdownList();
private void GetCompatibleVariablenames()
{
possibleActions = ReflectiveEnumerator.GetEnumerableOfType(this)
.Where(x => x.GetTypeName() != "GameActionBase")
.Where(x => x.GetTypeName() != "GameAction")
.Select(x => new ValueDropdownItem(x.GetActionName(), x));
}
private void AddNewAction()
{
if(newAction != null)
{
newAction.controller = this;
newAction.className = newAction.GetType().AssemblyQualifiedName;
actions.Add(newAction);
newAction = null;
}
}
#endregion
#region Utilities
private void InitializeActionList()
{
if(actions == null) actions = new List();
}
public GameActionsController Duplicate()
{
GameActionsController newController = new GameActionsController();
newController.actions = new List();
var newSerializedActions = new List();
objectSerializer.ClearGameObjects();
for (int i = 0; i < actions.Count; i++)
{
actions[i].OnBeforeSerialize(objectSerializer, i, depth);
string data = JsonUtility.ToJson(actions[i]);
newSerializedActions.Add(data);
}
newController.objectSerializer = objectSerializer;
objectSerializer = new GameActionObjectSerializer();
newController.context = this.context.Duplicate();
for (int i = 0; i < newSerializedActions.Count; i++)
{
//Debug.Log(newSerializedActions[i]);
GameAction action = JsonUtility.FromJson(newSerializedActions[i]);
if(action == null) continue;
action = GameAction.InvokeStringMethod(action.className, action.typeName, newSerializedActions[i]);
action.controller = newController;
action.OnBeforeDeserialize(newController.objectSerializer, i, depth);
newController.actions.Add(action);
}
newController.objectSerializer.ClearGameObjects();
return newController;
}
#endregion
}
}