using System;
using UniRx;
using UniRx.Triggers;
using UnityEditor;
using UnityEngine;

namespace MyGame.Scenes.MiniGame.Scripts
{
    public class GameManager : MonoBehaviour
    {
        public enum MenuState
        {
            StageSelect,
            Game,
        }
        public enum GameState
        {
            Ready,
            Play,
            Success,
            Failure
        }

        [SerializeField] private float timeLimit = 30f;
        [SerializeField] private HeaderView headerView;
        [SerializeField] private Player player;
        [SerializeField] private StageManager stageManager;
        [SerializeField] private ResultManager resultManager;
#if UNITY_EDITOR
        [SerializeField] private bool isDebug;
#endif

        private readonly ReactiveProperty<MenuState> menuState = new ReactiveProperty<MenuState>();
        private readonly ReactiveProperty<GameState> state = new ReactiveProperty<GameState>();

        private IDisposable timerDisposable;

        // Start is called before the first frame update
        private void Start()
        {
            menuState.AddTo(this);
            state.AddTo(this);
            menuState.Value = MenuState.Game;
            state.Value = GameState.Ready;
            
            

            menuState.Subscribe(x =>
            {
                switch (x)
                {
                    case MenuState.StageSelect:
                        break;
                    case MenuState.Game:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(x), x, null);
                }
            }).AddTo(this);

            this.UpdateAsObservable()
                .Where(_ => menuState.Value == MenuState.Game)
                .Where(_ => Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space))
                .Subscribe(_ =>
                {
                    state.Value = GameState.Play;
                }).AddTo(this);
            
            state.Subscribe(x =>
            {
                switch (x)
                {
                    case GameState.Ready:
                        timerDisposable?.Dispose();
                        headerView.ResetView();
                        headerView.ChangeTimeCount(timeLimit);
                        break;
                    case GameState.Play:
                        timerDisposable?.Dispose();
                        timerDisposable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1f))
                            .Select(time => (int)(timeLimit - time))
                            .TakeWhile(time => time >= 0)
                            .Subscribe(time =>
                            {
                                headerView.ChangeTimeCount(time);
                            }, () =>
                            {
                                state.Value = GameState.Success;
                            }).AddTo(this);
                        break;
                    case GameState.Success:
                        break;
                    case GameState.Failure:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(x), x, null);
                }
            }).AddTo(this);

            var playerTransform = player.transform;
            var cameraTransform = Camera.main.transform;
            var cameraOffset = cameraTransform.position - playerTransform.position.x * Vector3.right;
            this.UpdateAsObservable()
                .Where(_ => state.Value == GameState.Play)
                .DelayFrame(1)
                .Subscribe(_ =>
                {
                    player.Move();
                    // カメラ移動
                    cameraTransform.position = playerTransform.position.x * Vector3.right + cameraOffset;
                    if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space))
                    {
                        player.Jump();
                    }
                }).AddTo(this);

            playerTransform.OnTriggerEnter2DAsObservable().Subscribe(x =>
            {
                var item = x.GetComponent<StageItem>();
                if (item is null)
                {
                    return;
                }
#if UNITY_EDITOR
                var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                sphere.GetComponent<MeshRenderer>().material.color = Color.red;
                sphere.transform.SetLocalScale(.4f);
                sphere.transform.position = x.ClosestPoint(playerTransform.position);
                sphere.transform.SetPositionZ(-5f);
                if (item.ItemType == StageItem.Type.Obstacle && isDebug)
                {
                    Observable.NextFrame().Subscribe(_ =>
                    {
                        EditorApplication.isPaused = true;
                    }).AddTo(this);
                }
#endif
                x.enabled = false;
                switch (item.ItemType)
                {
                    case StageItem.Type.Item1:
                        headerView.AddCount(item.Point);
                        break;
                    case StageItem.Type.Obstacle:
                        player.Hit();
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }).AddTo(this);
        }
    }
}
