using System;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine;

namespace Intrepid.Core.Grounding
{
    [Serializable]
    public abstract class GroundProfile
    {
    }
    
    [Serializable, GroupConfigurations("Motion Profile"), HideLabel]
    public sealed class GroundProfileMotion : GroundProfile
    {
        [SerializeField] private Vector3 velocity;
        [SerializeField] private Vector3 deltaPosition;
        [SerializeField] private bool isMoving;
        [SerializeField] private bool isStatic;

        public Vector3 Velocity
        {
            get => velocity;
            set => velocity = value;
        }

        public Vector3 DeltaPosition
        {
            get => deltaPosition;
            set => deltaPosition = value;
        }

        public bool IsMoving
        {
            get => isMoving;
            set => isMoving = value;
        }

        public bool IsStatic
        {
            get => isStatic;
            set => isStatic = value;
        }
    }
    
    [Serializable, GroupConfigurations("Movement Profile"), HideLabel]
    public  class GroundProfileMovement : GroundProfile
    {
        [SerializeField] private float speedMultiplier;
        [SerializeField] private float accelerationMultiplier;
        [SerializeField] private float decelerationMultiplier;
        [SerializeField] private float uphillSpeedMultiplier;
        [SerializeField] private float downhillSpeedMultiplier;
        [SerializeField] private float sidewaysSpeedMultiplier;

        public float SpeedMultiplier
        {
            get => speedMultiplier;
            protected set => speedMultiplier = value;
        }
        public float AccelerationMultiplier
        {
            get => accelerationMultiplier;
            protected set => accelerationMultiplier = value;
        }
        public float DecelerationMultiplier
        {
            get => decelerationMultiplier;
            protected set => decelerationMultiplier = value;
        }
        public float UphillSpeedMultiplier
        {
            get => uphillSpeedMultiplier;
            protected set => uphillSpeedMultiplier = value;
        }
        public float DownhillSpeedMultiplier
        {
            get => downhillSpeedMultiplier;
            protected set => downhillSpeedMultiplier = value;
        }
        public float SidewaysSpeedMultiplier
        {
            get => sidewaysSpeedMultiplier;
            protected set => sidewaysSpeedMultiplier = value;
        }

        public static GroundProfileMovement NoModifiers => new()
        {
            SpeedMultiplier = 1f,
            AccelerationMultiplier = 1f,
            DecelerationMultiplier = 1f,
            UphillSpeedMultiplier = 1f,
            DownhillSpeedMultiplier = 1f,
            SidewaysSpeedMultiplier = 1f,
        };

        public static GroundProfileMovement operator *(GroundProfileMovement a, GroundProfileMovement b)
        {
            return new GroundProfileMovement
            {
                SpeedMultiplier = a.SpeedMultiplier * b.SpeedMultiplier,
                AccelerationMultiplier = a.AccelerationMultiplier * b.AccelerationMultiplier,
                DecelerationMultiplier = a.DecelerationMultiplier * b.DecelerationMultiplier,
                UphillSpeedMultiplier = a.UphillSpeedMultiplier * b.UphillSpeedMultiplier,
                DownhillSpeedMultiplier = a.DownhillSpeedMultiplier * b.DownhillSpeedMultiplier,
                SidewaysSpeedMultiplier = a.SidewaysSpeedMultiplier * b.SidewaysSpeedMultiplier,
            };
        }
    }
    
    public readonly struct GroundProfiles
    {
        public static readonly GroundProfiles None = new(false);

        private readonly bool _isSafe;
        private GroundProfileMotion GroundProfileMotion { get; }
        private GroundProfileMovement GroundProfileMovement { get; }
        public bool IsSafe => CheckIfThisProfileIsSafe();

        public GroundProfiles(bool isSafe,
            GroundProfileMotion groundProfileMotion = null,
            GroundProfileMovement groundProfileMovement = null)
        {
            _isSafe = isSafe;
            GroundProfileMotion = groundProfileMotion;
            GroundProfileMovement = groundProfileMovement;
        }
        
        private bool CheckIfThisProfileIsSafe()
        {
            if (GroundProfileMotion.IsNull())
            {
                return _isSafe;
            }
            
            return GroundProfileMotion.IsStatic;
        }

        private bool TryGet<T>(T value, out T profile) where T: GroundProfile
        {
            if (value is null)
            {
                profile = null;
                return false;
            }
            
            profile = value;
            return true;
        }

        public bool TryGetGroundProfileMotion(out GroundProfileMotion profile)
        {
            return TryGet(GroundProfileMotion, out profile);
        }

        public bool TryGetGroundProfileMovement(out GroundProfileMovement profile)
        {
            return TryGet(GroundProfileMovement, out profile);
        }
    }
}