using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using MonoGame.FourEngine;
using MonoGame.FourEngine.Components;
using MonoGame.FourEngine.Interfaces;
using MonoGame.FourEngine.Raycast;
using Raycast.Win32.Scenes;
using System;

namespace Raycast.Win32.Entities
{
    public partial class Player : Entity, IRaycastCamera, IHaveSizeAndPosition
    {
        public const float Radius = 0.4f;
        
        private Orientation _orientation;
        public Orientation Orientation
        {
            get => _orientation;
            set
            {
                _orientation = value;
                Direction = Orientation.GetDirection();
                Plane = Orientation.GetPlane();

                _direction.X *= -1; // because... who knows? "just"!
            }
        }

        private Vector2 _position;
        public Vector2 Position
        {
            get => _position;
            set => _position = value;
        }

        private Vector2 _direction;
        public Vector2 Direction
        {
            get => _direction;
            set => _direction = value;
        }

        private Vector2 _plane;
        public Vector2 Plane
        {
            get => _plane;
            set => _plane = value;
        }

        private int _health;
        public int Health
        {
            get => _health;
            protected set
            {
                if (_health > 0 && value <= 0)
                {
                    // TODO: Some sound?
                    //OnDie();
                }
                _health = value;

                // Non linear feels much better
                VisibleHealth = (int)(Math.Pow(Health / 100d, 2) * 100);
                if (Health > 0 && VisibleHealth == 0) VisibleHealth = 1;
            }
        }

        public int VisibleHealth { get; private set; }

        private RenderedScene _scene;

        public Player()
        {
            Components.Add(new ActionComponent(AutoAim));
            Components.Add(new ActionComponent(Control));

            Health = 100;

            if (MouseLookEnabled)
            {
                var center = (Configuration.Size.ToVector2() / 2).ToPoint();
                Mouse.SetPosition(center.X, center.Y);
            }
        }

        public void Update3dSoundEffect(SfxComponent sfx, Vector2 position)
        {
            const float lowSoundDistance = 150f;
            const float minVolume = 0.08f;

            var cameraAngle = Math.Atan2(Direction.Y, Direction.X);

            // Use camera as a center of coordinates
            position -= Position;
            
            var distance = position.LengthSquared();
            var volume = distance / lowSoundDistance;
            volume = 1 - volume;
            if (volume < minVolume) volume = minVolume;

            // Rotate around camera
            var angle = MathHelper.PiOver2 - cameraAngle;
            position = new Vector2(
                (float)(Math.Cos(angle) * position.X - Math.Sin(angle) * position.Y),
                (float)(Math.Sin(angle) * position.X + Math.Cos(angle) * position.Y));
            position.Normalize();
            
            if (position.Y < 0)
            {
                volume *= 0.75f;
            }

            sfx.Volume = volume;
            sfx.Pan = position.X;
        }

        public void Hurt(int damage)
        {
            var health = Health - damage;
            Health = health > 0 ? health : 0;
            MessageCenter.TriggerPlayerHurt(this, Health > 0 ? damage : 10);
        }

        public override void OnAttach(Scene scene)
        {
            _scene = scene as RenderedScene;
        }

        public override void OnDetach(Scene scene)
        {
            _scene = null;
        }

        #region IHaveSizeAndPosition
        Point IHaveSizeAndPosition.Position
        {
            get => Position.ToPoint();
            set => Position = value.ToVector2();
        }
        int IHaveSizeAndPosition.Width { get; set; }
        int IHaveSizeAndPosition.Height { get; set; }
        #endregion

        #region IHaveReadOnlySizeAndPosition
        Point IHaveReadOnlySizeAndPosition.Position => ((IHaveSizeAndPosition)this).Position;
        int IHaveReadOnlySizeAndPosition.Width => ((IHaveSizeAndPosition)this).Width;
        int IHaveReadOnlySizeAndPosition.Height => ((IHaveSizeAndPosition)this).Height;
        #endregion
    }
}