using System;
using System.Collections.Generic;
using EscapeRoomEngine.Desert.Runtime.Puzzle_A;
using EscapeRoomEngine.Engine.Runtime;
using EscapeRoomEngine.Engine.Runtime.Modules;
using JetBrains.Annotations;
using NaughtyAttributes;
using UnityEngine;

namespace EscapeRoomEngine.Desert.Runtime.Puzzle_B
{
    [Serializable]
    internal struct ButtonAction
    {
        public SymbolButton button;
        public List<int> stateIndices;
    }
    
    /// <summary>
    /// The main component for the hexagon module.
    /// </summary>
    public class PuzzleB : StatePuzzle
    {
        [Tooltip("How many seconds the buttons should be disabled until another input is accepted.")]
        [SerializeField] [Min(0)]
        private float buttonCooldown;
        [ValidateInput("CorrectRotatorCount")]
        public List<Rotator> rotators;
        [InfoBox("Every button action will toggle the states at the given indices between 0 and 90 degrees.")]
        [SerializeField]
        private List<ButtonAction> buttonActions;
        [BoxGroup("Internal")] [SerializeField]
        private List<Emission> lights;
        [BoxGroup("Internal")] [SerializeField]
        private Crystal crystal;
        
        private float _previousPress = -1;

        protected override void Start()
        {
            base.Start();
            
            PuzzleEvent += (_, type) =>
            {
                // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
                switch (type)
                {
                    case PuzzleEventType.Restarted:
                        var color = Engine.Runtime.Engine.Theme.puzzleColor;
                        crystal.Color = color;
                        lights.ForEach(emission => emission.color = color.hdr);
                        rotators.ForEach(rotator => rotator.Emission.color = color.hdr);
                        buttonActions.ForEach(action => action.button.Enable());
                        break;
                    case PuzzleEventType.Solved:
                        color = Engine.Runtime.Engine.Theme.solvedColor;
                        crystal.Color = color;
                        lights.ForEach(emission => emission.color = color.hdr);
                        rotators.ForEach(rotator => rotator.Emission.color = color.hdr);
                        buttonActions.ForEach(action => action.button.Disable());
                        break;
                }
            };

            foreach (var buttonAction in buttonActions)
            {
                buttonAction.button.ButtonEvent += (_, type) =>
                {
                    if (type == ButtonEventType.Pressed && _previousPress + buttonCooldown <= Time.time)
                    {
                        Switch(buttonAction);

                        _previousPress = Time.time;
                    }
                };
            }
            
            crystal.Active = true;
            lights.ForEach(emission => emission.active = true);
            rotators.ForEach(rotator => rotator.Emission.active = true);
        }

        protected override void SetState(int index, int value, bool checkSolution)
        {
            base.SetState(index, value, checkSolution);

            for (var i = 0; i < stateCount; i++)
            {
                rotators[i].angle = states[i];
            }
        }

        /// <summary>
        /// Switch all rotators specified by a given action.
        /// </summary>
        private void Switch(ButtonAction action)
        {
            for (var i = 0; i < action.stateIndices.Count; i++)
            {
                var stateIndex = action.stateIndices[i];
                var current = OrientationExtensions.FromAngle(states[stateIndex]);
                current = current == Orientation.North ? current.Rotated(1) : current.Rotated(-1);
                
                // set state but only check solution on the last change
                SetState(stateIndex, current.AngleInt(), i == action.stateIndices.Count - 1);
            }
        }

        [UsedImplicitly] // used for field validation
        private bool CorrectRotatorCount(List<Rotator> list) => list != null && list.Count == stateCount;
    }
}