using System.Linq;
using Sirenix.OdinInspector;
using Sirenix.Utilities;
using UnityEngine;
using UnityEngine.Rendering;
using VOID.Generic.Objects.Item;
using VOID.Generic.Trajectory;

namespace VOID.Generic.Player
{
    [DefaultExecutionOrder(-1)]
    public class DraggingVisualizer : MonoBehaviour
    {
        [ShowInInspector] private Transform _transformStart;
        [ShowInInspector] private Transform _transformEnd;
        
        private Vector3 _positionStart;
        private Vector3 _positionEnd;
        private Vector3 _lastPositionStart;
        private Vector3 _lastPositionEnd;

        private LineRenderer _lineRenderer;
        private ParabolicTrajectory _trajectory = new();

        public static DraggingVisualizer Create(Transform transformStart, Transform transformEnd, ItemObject item)
        {
            // Prepare for scene dragging
            var go = Instantiate(item.gameObject);
            go.name = "Dragging Visualizer Dummy - " + go.name;
            go.transform.localPosition = Vector3.zero;
            go.transform.rotation = Quaternion.Euler(0, go.transform.rotation.eulerAngles.y, 0);
            
            // Remove everything except rendering components
            go.GetComponentsInChildren<Component>()
                .Where(component => component is not Transform and not MeshFilter and not MeshRenderer)
                .ForEach(component => Destroy(component));
            
            // Make sure that our dummy don't copy highlight/outlines
            go.GetComponentsInChildren<Component>()
                .FilterCast<MeshRenderer>()
                .ForEach(meshRenderer =>
                    meshRenderer.sharedMaterials = meshRenderer.sharedMaterials
                        .Where(material => !HighlightManager.current.shaders.Contains(material.shader))
                        .ToArray());
            
            var draggingVisualizer = go.AddComponent<DraggingVisualizer>();
            draggingVisualizer._transformStart = transformStart;
            draggingVisualizer._transformEnd = transformEnd;
            
            // Nice trajectory how item move
            draggingVisualizer._lineRenderer = go.AddComponent<LineRenderer>();
            draggingVisualizer._lineRenderer.startWidth = 0.1f;
            draggingVisualizer._lineRenderer.endWidth = 0.1f;
            draggingVisualizer._lineRenderer.shadowCastingMode = ShadowCastingMode.Off;
            draggingVisualizer._trajectory.height = 1f;

            return draggingVisualizer;
        }

        private void FixedUpdate()
        {
            if (!_transformStart || !_transformEnd) return;

            _positionStart = _transformStart.position;
            _positionEnd = _transformEnd.position;
            
            if (_positionStart == _lastPositionStart && _positionEnd == _lastPositionEnd) return;

            _lastPositionStart = _positionStart;
            _lastPositionEnd = _positionEnd;

            transform.position = _positionEnd;
            if (gameObject.activeSelf) UpdateTrajectory();
        }

        private void UpdateTrajectory()
        {
            _trajectory.Init(_positionStart, _positionEnd);
            
            var pointsNeeded = Mathf.Max(1, Mathf.FloorToInt(_trajectory.trajectoryDistance)) * 10;
            var points = _trajectory.GetPositions(pointsNeeded);

            _lineRenderer.positionCount = points.Length;
            _lineRenderer.SetPositions(points);
        }

        public void Show()
        {
            gameObject.SetActive(true);
        }

        public void Hide()
        {
            gameObject.SetActive(false);
        }
    }
}