using HPJ.Simulation;
using HPJ.Simulation.Enums;
using HPJ.Simulation.Map;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace HPJ.Presentation.Obstacle
{
    // A Simple Obstacle based on a sphere or circle
    public class NavigationObstacleSphere : NavigationObstacle
    {
        #region Unity

        // Start is called before the first frame update
        public override void Start()
        {
            if (!NavigationManager.Instance.Initialized)
            {
                Invoke(nameof(Start), 01f);
                return;
            }
            MapTileRelationships = new Dictionary<MapSet, MapObstacleRelationshipData>();
            _previousPosition = new Vector3(-1111111, -112321321321312, 233414212);
            base.Start();
        }

        public virtual void OnDrawGizmosSelected()
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireSphere(transform.position, _radius);

            Gizmos.color = Color.blue;
            Gizmos.DrawLine(transform.position, transform.position - Vector3.up * _checkMapHeight);
        }

        #endregion

        #region Obstacle

        [SerializeField]
        protected float _radius = 1f;
        public float Radius { get { return _radius; } set { _radius = value; } }

        private Vector3 _previousPosition;

        public override void UpdatePosition(Vector3 NewPosition, bool ForceCheck = false)
        {
            if (!NavigationManager.Instance.Initialized)
            {
                return;
            }

            // No need to update if no tiles need updating
            if (PositionChanged(NewPosition))
            {
                _previousPosition = NewPosition;

                foreach (MapObstacleRelationshipData Data in MapTileRelationships.Values)
                {
                    Data.UnvalidateTiles();
                }

                foreach (MapSet Set in NavigationManager.Instance.MapSets)
                {
                    if (transform.position.y - Set.SetSettings.MapSettings.Offset.y / 100f > _checkMapHeight)
                    {
                        //Debug.Log("Too high or Too low");
                        continue;
                    }

                    if (!NavigationManager.Instance.CircleOnMap(Set, NewPosition, Radius))
                    {
                        //Debug.Log("Not on Map");
                        continue;
                    }

                    MapObstacleRelationshipData RelationshipData;
                    if (MapTileRelationships.ContainsKey(Set))
                    {
                        RelationshipData = MapTileRelationships[Set];
                    }
                    else
                    {
                        RelationshipData = new MapObstacleRelationshipData(Set, this, _obstacleTileType);
                        MapTileRelationships.Add(Set, RelationshipData);
                    }

                    Vector3 RadiusVector = new Vector3(Radius, 0, Radius);
                    IntVector2 Center = Set.GetMapTileIndex(NavigationManager.Instance.GetTileWorldPosition(NewPosition));
                    IntVector2 HigherBound = Set.GetMapTileIndex(NavigationManager.Instance.GetTileWorldPosition(NewPosition + RadiusVector));
                    IntVector2 LowerBound = Set.GetMapTileIndex(NavigationManager.Instance.GetTileWorldPosition(NewPosition - RadiusVector));
                    float TileRadiusSqr = (HigherBound.y - LowerBound.y) / 2f;
                    TileRadiusSqr *= TileRadiusSqr;

                    for (int x = LowerBound.x; x <= HigherBound.x; x++)
                    {
                        for (int y = LowerBound.y; y <= HigherBound.y; y++)
                        {
                            int X_Distance = Center.x - x;
                            int Y_Distance = Center.y - y;
                            if (X_Distance * X_Distance + Y_Distance * Y_Distance > TileRadiusSqr)
                            {
                                //Debug.Log($"({X_Distance}, {Y_Distance}) Not In Bounds");
                                continue;
                            }

                            // Make sure its close enough to the ground to count as an obstacle
                            if (Set.GetTileType(x, y) != TileTypes.OutOfBounds)
                            {
                                //Debug.Log("Change Tile");
                                IntVector3 TileKey = new IntVector3(x, y, Set.InstanceID);
                                RelationshipData.AddOrValidateTile(TileKey);
                            }
                        }
                    }
                }

                foreach (MapObstacleRelationshipData Data in MapTileRelationships.Values)
                {
                    ChangeMapJob GeneratedMapChangeJob = Data.GenerateMapChangeJob();
                    if (GeneratedMapChangeJob != null)
                    {
                        //Debug.Log("Change Map From Job");
                        Data.Map.ChangeMap(GeneratedMapChangeJob);
                    }
                    Data.SortRelatedTileData();
                }
            }
            else
            {
                RevalidateTiles();
            }
        }

        public override void RevalidateTiles()
        {
            foreach (MapObstacleRelationshipData Data in MapTileRelationships.Values)
            {
                Data.Revalidate();
            }
        }

        protected override bool PositionChanged(Vector3 NewPosition)
        {
            return _previousPosition != NewPosition;
        }

        /// <summary>
        /// Rotates the obstacle based on the desired Rotation. Basic NavigationObstacle will only swap the object size x and y values. Inherit to ovverride this behaviour
        /// </summary>
        /// <param name="Rotation"></param>
        public override void Rotate(Quaternion Rotation)
        {
        }

        #endregion
    }
}