#nullable enable
using Godot;
using System;
using System.Globalization;
using System.Linq;
using Godot.Collections;

public partial class Main : Control
{
    private bool _moving = false;
    [Export]
    private Node3D _yawNode;
    [Export]
    private Node3D _pitchNode;
    [Export]
    private Camera3D _cameraNode;
    
    [Export] private float _moveSensitivity = 1f/500f;
    [Export] private float _zoomSensitivity = 1f;
    
    [Export] private MeshInstance3D _meshInstance;
    [Export] private Node3D World;
    [Export] private TextureRect _textureRect;

    private PlanetHelper.VertexData? _vertex = null;
    private PlanetHelper.PlateData? _plate = null;
    
    private PlanetHelper _planetHelper;
    public override void _Ready()
    {
        _planetHelper = new PlanetHelper(_meshInstance, _textureRect);
        UpdateStats();
    }
    
    private const float RayLength = 1000.0f;
    public override void _Input(InputEvent @event)
    {
        if (@event is InputEventMouseButton mouseEvent)
        {
            if (mouseEvent.ButtonIndex == MouseButton.Left)
            {
                _moving = mouseEvent.Pressed;
            }
            if (mouseEvent.ButtonIndex == MouseButton.WheelUp)
            {
                _cameraNode.Position += new Vector3(0, 0, _zoomSensitivity);
            }
            
            if (mouseEvent.ButtonIndex == MouseButton.WheelDown)
            {
                _cameraNode.Position -= new Vector3(0, 0, _zoomSensitivity);
            }
        }
        else if (@event is InputEventMouseMotion motionEvent && _moving)
        {
            _yawNode.RotateY(-motionEvent.ScreenRelative.X * _moveSensitivity);
            _pitchNode.RotateX(-motionEvent.ScreenRelative.Y * _moveSensitivity);
        }
    }

    public void Tab(int tab)
    {
        if (tab == 1)
        {
            Projector.GatherPoints(_planetHelper);
            _textureRect.Texture = Projector.Render(_planetHelper);
        }
    }

    public override void _Process(double delta)
    {
        if (Input.IsActionJustPressed("mouse_secondary"))
        {
            var from = _cameraNode.ProjectRayOrigin(GetViewport().GetMousePosition());
            var to = from + _cameraNode.ProjectRayNormal(GetViewport().GetMousePosition()) * RayLength;
            var result = World.GetWorld3D().DirectSpaceState.IntersectRay(PhysicsRayQueryParameters3D.Create(from, to));
            if (result.Count > 0)
            {
                Vector3? pos = result["position"].AsVector3();
                if (pos != null)
                {
                    GD.Print($"Hit: '{pos}'");
                    var closest = _planetHelper.Octree.SearchNearest(pos ?? Vector3.Zero)?.Id;
                    if (closest != null)
                    {
                        _vertex = _planetHelper.Vertices.Single(v => v.Id == closest);
                        if (_planetHelper.Plates.Count > 0 && _vertex.PlateId != -1)
                            _plate = _planetHelper.Plates[_vertex.PlateId];
                        else
                            _plate = null;
                        UpdateStats();
                    }
                }
            }
        }

        if (Input.IsActionJustPressed("spacebar"))
        {
            _planetHelper.Advance = true;
        }
        if (Input.IsActionJustPressed("enter"))
        {
            _planetHelper.AutoRun = true;
        }
        _planetHelper.Process();
    }
    public void UpdateStats()
    {
        if (_vertex != null)
        {
            var height = -9000f * (0.5f - _vertex.Height) * 2f;
            GetNode<Label>("%PointHeight").Text = $"{(height > 0 ? "+" : "")}{height:0000}M";
            GetNode<Label>("%PointId").Text = $"{_vertex.Id:0000000}";
        }
        else
        {
            GetNode<Label>("%PointHeight").Text = "0000M";
            GetNode<Label>("%PointId").Text = "0000000";
        }
        if (_plate != null)
        {
            GetNode<Label>("%PlateId").Text = $"{_plate.Id:00}";
            GetNode<Label>("%IsLandform").Text = $"{(_plate.IsLandform ? "Y" : "N")}";
            var area = (int)((float)_plate.Vertices.Count / _planetHelper.Vertices.Count * 100f);
            GetNode<Label>("%Area").Text = $"{area:00}%";
        }
        else
        {
            GetNode<Label>("%PlateId").Text = "00";
            GetNode<Label>("%IsLandform").Text = "U";
        }
    }

    public void MakeGo()
    {
        _planetHelper = new PlanetHelper(_meshInstance, _textureRect);
    }

}
