using System;

using Syntriax.Engine.Core;
using Syntriax.Engine.Physics2D;
using Syntriax.Engine.Physics2D.Abstract;

namespace Pong.Behaviours;

public class BallBehaviour : Behaviour2D
{
    public Vector2D StartDirection { get; private set; } = Vector2D.Zero;
    public float Speed { get; set; } = 500f;
    public float SpeedUpMultiplier { get; set; } = .0125f;

    private readonly Random random = new();
    private IRigidBody2D rigidBody = null!;

    protected override void OnFirstActiveFrame()
    {
        if (!BehaviourController.TryGetBehaviour(out IRigidBody2D? foundRigidBody))
            throw new Exception($"{nameof(IRigidBody2D)} is missing on {HierarchyObject.Name}.");
        if (!BehaviourController.TryGetBehaviour(out ICollider2D? foundCollider))
            throw new Exception($"{nameof(ICollider2D)} is missing on {HierarchyObject.Name}.");

        foundCollider.OnCollisionDetected += OnCollisionDetected;

        rigidBody = foundRigidBody;

        if (HierarchyObject.GameManager.TryFindBehaviour(out PongManagerBehaviour? pongManager))
        {
            pongManager.OnReset += ResetBall;
            pongManager.OnScored += ResetBall;
            pongManager.OnFinished += DisableBall;
        }
    }

    private void DisableBall(PongManagerBehaviour pongManager)
    {
        Transform.Position = Vector2D.Zero;
        rigidBody.Velocity = Vector2D.Zero;
    }

    private void ResetBall(PongManagerBehaviour pongManager)
    {
        StateEnable.Enabled = true;
        Transform.Position = Vector2D.Zero;
        rigidBody.Velocity = GetRandomDirection() * Speed;
    }

    private Vector2D GetRandomDirection()
    {
        const float AllowedRadians = 45f * Syntriax.Engine.Core.Math.DegreeToRadian;
        float rotation = (float)random.NextDouble() * 2f * AllowedRadians - AllowedRadians;
        bool isBackwards = (random.Next() % 2) == 1;
        return Vector2D.Right.Rotate(isBackwards ? rotation + Syntriax.Engine.Core.Math.PI : rotation);
    }

    protected override void OnUpdate()
    {
        if (rigidBody.Velocity.MagnitudeSquared <= 0.01f)
            return;

        Vector2D speedUp = rigidBody.Velocity.Normalized * GameManager.Time.DeltaTime;
        rigidBody.Velocity += speedUp * SpeedUpMultiplier;
    }

    private void OnCollisionDetected(ICollider2D collider2D, CollisionDetectionInformation information)
    {
        if (Syntriax.Engine.Core.Math.Abs(information.Normal.Dot(Vector2D.Right)) > .25)
            rigidBody.Velocity = information.Detected.Transform.Position.FromTo(information.Detector.Transform.Position).Normalized * rigidBody.Velocity.Magnitude;
        else
            rigidBody.Velocity = rigidBody.Velocity.Reflect(information.Normal);
    }

    public BallBehaviour() { }
    public BallBehaviour(Vector2D startDirection, float speed)
    {
        StartDirection = Vector2D.Normalize(startDirection);
        Speed = speed;
    }
}