﻿#region Header

// Copyright (c) 2021-2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#endregion

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.LowLevel;

namespace BlackBox
{
    public enum InitCycle
    {
        Invalid,
#if UNITY_ANDROID || UNITY_IOS
        PreInitialize,
        PreInitializing, // this will only wait for preinit to complete
#endif
        Initialize,
        StartSession,
        Update
    }
    
    public class Cycle
    {
        #region Singleton
        
        private static Cycle instance_ = null;

        public static Cycle Instance 
        {
            get
            {
                if (instance_ == null)
                    instance_ = new Cycle();

                return instance_;
            }
        }

        #endregion
        
        #region Consts

        public const int UNITY_UPDATE_INDEX = 4;
        
        #endregion
        
        #region Fields

        private InitCycle initCycle_ = InitCycle.Invalid;

        public event Action OnInitialize;
        public event Action OnStartSession;
        public event Action OnEndSession;
        public event Action OnUpdate;
        
 #if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
        public event Action OnPreInitialize;
        public void PreInitComplete()
        {
            initCycle_ = InitCycle.Initialize;
        }
#endif
        
        #endregion

        public void Initialize()
        {
            SetUpBlackBoxLoop();
        }
        
        private void SetUpBlackBoxLoop()
        {
            var unityUpdateSystem = PlayerLoop.GetCurrentPlayerLoop();
            var updateSystem      = unityUpdateSystem.subSystemList[UNITY_UPDATE_INDEX];
            
            // Create and add BlackBox loop
            // TODO: Support late update
            var blackBoxUpdate = new PlayerLoopSystem()
            {
                type           = typeof(Cycle),
                updateDelegate = Update
            };
            
            // Insert BlackBox loop
            var loops = new List<PlayerLoopSystem>();
            loops.AddRange(updateSystem.subSystemList);
            loops.Add(blackBoxUpdate);

            updateSystem.subSystemList                          = loops.ToArray();
            unityUpdateSystem.subSystemList[UNITY_UPDATE_INDEX] = updateSystem;
            
            // Update loop
            PlayerLoop.SetPlayerLoop(unityUpdateSystem);
            
            // Quit loop
            Application.quitting += EndSession;
        }

        private void StartSession()
        {
            OnStartSession?.Invoke();
        }

        private void EndSession()
        {
            OnEndSession?.Invoke();
        }
        
        private void Update()
        {
            switch (initCycle_)
            {
#if UNITY_ANDROID || UNITY_IOS
                case InitCycle.Invalid:
                    initCycle_ = InitCycle.PreInitialize;
                    break;
                case InitCycle.PreInitialize:
                    OnPreInitialize?.Invoke();
                    break;              
#else
                case InitCycle.Invalid:
                    initCycle_ = InitCycle.Initialize;
                    break;
#endif
                case InitCycle.Initialize:
                    initCycle_ = InitCycle.StartSession;
                    OnInitialize?.Invoke();
                    break;
                case InitCycle.StartSession:
                    initCycle_ = InitCycle.Update;
                    StartSession();
                    break;
                case InitCycle.Update:
                    OnUpdate?.Invoke();
                    break;
            }
        }
    }
}
