using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Decal.Adapter;
using Decal.Adapter.Wrappers;

namespace MosswartMassacre
{
    public class NavWaypoint
    {
        public double NS { get; set; }
        public double EW { get; set; }
        public double Z { get; set; }
        public int Type { get; set; }
        public NavWaypoint Previous { get; set; }
    }

    public class NavRoute : IDisposable
    {
        private bool disposed = false;
        private List<NavWaypoint> waypoints = new List<NavWaypoint>();
        private List<D3DObj> lineObjects = new List<D3DObj>();
        private Color routeColor;
        private bool isVisible = false;

        public string FilePath { get; private set; }
        public string FileName => Path.GetFileNameWithoutExtension(FilePath);
        public bool IsVisible => isVisible;
        public int WaypointCount => waypoints.Count;

        public NavRoute(string filePath, Color color)
        {
            FilePath = filePath;
            routeColor = color;
        }

        public bool LoadFromFile()
        {
            try
            {
                ClearRoute();
                waypoints.Clear();

                if (!File.Exists(FilePath))
                {
                    PluginCore.WriteToChat($"Nav file not found: {FilePath}");
                    return false;
                }

                PluginCore.WriteToChat($"Navigation: Loading {FileName}...");

                using (StreamReader sr = File.OpenText(FilePath))
                {
                    // Read header
                    string header = sr.ReadLine();
                    if (string.IsNullOrEmpty(header) || !header.StartsWith("uTank2 NAV"))
                    {
                        PluginCore.WriteToChat($"Navigation: Invalid file format - {FileName}");
                        return false;
                    }

                    // Read nav type
                    string navTypeLine = sr.ReadLine();
                    if (string.IsNullOrEmpty(navTypeLine) || !int.TryParse(navTypeLine.Trim(), out int navType))
                    {
                        PluginCore.WriteToChat($"Navigation: Failed to parse route type - {FileName}");
                        return false;
                    }

                    string navTypeDescription = "";
                    switch (navType)
                    {
                        case 0:
                            navTypeDescription = "Linear";
                            break;
                        case 1:
                            navTypeDescription = "Circular";
                            break;
                        case 2:
                            navTypeDescription = "Linear";
                            break;
                        case 3:
                            navTypeDescription = "Target (follow player/object)";
                            break;
                        case 4:
                            navTypeDescription = "Once";
                            break;
                        default:
                            navTypeDescription = $"Unknown ({navType})";
                            PluginCore.WriteToChat($"Navigation: Unknown route type {navType} in {FileName}");
                            break;
                    }

                    // Handle target nav (type 3) - follows a specific player/object
                    if (navType == 3)
                    {
                        if (sr.EndOfStream)
                        {
                            PluginCore.WriteToChat($"Navigation: Target route file is empty - {FileName}");
                            return false;
                        }
                        
                        string targetName = sr.ReadLine();
                        if (sr.EndOfStream)
                        {
                            PluginCore.WriteToChat($"Navigation: Target route missing target ID - {FileName}");
                            return false;
                        }
                        
                        string targetIdLine = sr.ReadLine();
                        
                        PluginCore.WriteToChat($"Navigation: Target route '{targetName}' cannot be visualized");
                        return true; // Successfully loaded but can't visualize
                    }

                    // Read record count
                    string recordCountLine = sr.ReadLine();
                    if (string.IsNullOrEmpty(recordCountLine) || !int.TryParse(recordCountLine.Trim(), out int recordCount))
                    {
                        PluginCore.WriteToChat($"Navigation: Failed to parse waypoint count - {FileName}");
                        return false;
                    }

                    if (recordCount <= 0 || recordCount > 10000) // Sanity check
                    {
                        PluginCore.WriteToChat($"Navigation: Invalid waypoint count {recordCount} - {FileName}");
                        return false;
                    }

                    NavWaypoint previous = null;
                    int waypointsRead = 0;

                    while (!sr.EndOfStream && waypointsRead < recordCount)
                    {
                        // Read waypoint type
                        string waypointTypeLine = sr.ReadLine();
                        
                        if (string.IsNullOrEmpty(waypointTypeLine) || !int.TryParse(waypointTypeLine.Trim(), out int waypointType))
                        {
                            PluginCore.WriteToChat($"Navigation: Failed to parse waypoint {waypointsRead + 1} in {FileName}");
                            break; // Skip this waypoint, don't fail entirely
                        }

                        // Read coordinates (all waypoint types have EW, NS, Z, Unknown)
                        string ewLine = sr.ReadLine();
                        string nsLine = sr.ReadLine();
                        string zLine = sr.ReadLine();
                        string unknownLine = sr.ReadLine(); // Unknown value (always 0)

                        if (string.IsNullOrEmpty(ewLine) || string.IsNullOrEmpty(nsLine) || string.IsNullOrEmpty(zLine) || string.IsNullOrEmpty(unknownLine))
                        {
                            PluginCore.WriteToChat($"Navigation: Missing coordinates at waypoint {waypointsRead + 1} in {FileName}");
                            break;
                        }

                        if (!double.TryParse(ewLine.Trim(), out double ew) ||
                            !double.TryParse(nsLine.Trim(), out double ns) ||
                            !double.TryParse(zLine.Trim(), out double z))
                        {
                            PluginCore.WriteToChat($"Navigation: Invalid coordinates at waypoint {waypointsRead + 1} in {FileName}");
                            break; // Skip this waypoint
                        }

                        var waypoint = new NavWaypoint
                        {
                            NS = ns,
                            EW = ew,
                            Z = z,
                            Type = waypointType,
                            Previous = previous
                        };

                        waypoints.Add(waypoint);
                        previous = waypoint;
                        waypointsRead++;

                        // Skip additional data based on waypoint type
                        if (!SkipWaypointData(sr, waypointType))
                        {
                            PluginCore.WriteToChat($"Navigation: Failed to parse waypoint {waypointsRead + 1} data in {FileName}");
                            break; // Don't continue if we can't parse properly
                        }
                    }

                    if (waypoints.Count > 0)
                    {
                        PluginCore.WriteToChat($"Navigation: Loaded {FileName} ({waypoints.Count} waypoints)");
                    }
                    else
                    {
                        PluginCore.WriteToChat($"Navigation: No valid waypoints found in {FileName}");
                    }
                    
                    return waypoints.Count > 0;
                }
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"Navigation: Error loading {FileName} - {ex.Message}");
                return false;
            }
        }

        private bool SkipWaypointData(StreamReader sr, int waypointType)
        {
            try
            {
                // Skip additional lines based on waypoint type (base 4 lines already read)
                switch (waypointType)
                {
                    case 0: // Point - no additional data (4 lines total)
                        break;
                    case 1: // Portal - 5 additional lines (9 lines total)
                        sr.ReadLine(); // Name
                        sr.ReadLine(); // ObjectClass
                        sr.ReadLine(); // "true"
                        sr.ReadLine(); // PortalNS
                        sr.ReadLine(); // PortalEW
                        sr.ReadLine(); // PortalZ
                        break;
                    case 2: // Recall - 1 additional line (5 lines total)
                        sr.ReadLine(); // RecallSpellId
                        break;
                    case 3: // Pause - 1 additional line (5 lines total)
                        sr.ReadLine(); // Pause milliseconds
                        break;
                    case 4: // ChatCommand - 1 additional line (5 lines total)
                        sr.ReadLine(); // Message
                        break;
                    case 5: // OpenVendor - 2 additional lines (6 lines total)
                        sr.ReadLine(); // Id
                        sr.ReadLine(); // Name
                        break;
                    case 6: // Portal2 - same as Portal (9 lines total)
                        sr.ReadLine(); // Name
                        sr.ReadLine(); // ObjectClass
                        sr.ReadLine(); // "true"
                        sr.ReadLine(); // PortalNS
                        sr.ReadLine(); // PortalEW
                        sr.ReadLine(); // PortalZ
                        break;
                    case 7: // UseNPC - 5 additional lines (9 lines total)
                        sr.ReadLine(); // Name
                        sr.ReadLine(); // ObjectClass
                        sr.ReadLine(); // "true"
                        sr.ReadLine(); // NpcEW
                        sr.ReadLine(); // NpcNS
                        sr.ReadLine(); // NpcZ
                        break;
                    case 8: // Checkpoint - no additional data (4 lines total)
                        break;
                    case 9: // Jump - 3 additional lines (7 lines total)
                        sr.ReadLine(); // Heading
                        sr.ReadLine(); // ShiftJump
                        sr.ReadLine(); // Milliseconds
                        break;
                    default:
                        // Unknown waypoint type - skip silently
                        break;
                }
                return true;
            }
            catch
            {
                // Silently handle parsing errors
                return false;
            }
        }

        public void Show()
        {
            if (isVisible) return;
            
            if (waypoints.Count == 0) 
            {
                PluginCore.WriteToChat($"Navigation: No waypoints to visualize in {FileName}");
                return;
            }

            CreateLineObjects();
            isVisible = true;
            PluginCore.WriteToChat($"Navigation: Showing {FileName} ({waypoints.Count} waypoints)");
        }

        public void Hide()
        {
            if (!isVisible) return;

            ClearRoute();
            isVisible = false;
            PluginCore.WriteToChat($"Navigation: Hidden {FileName}");
        }

        private void CreateLineObjects()
        {
            try
            {
                // Check D3DService availability
                if (CoreManager.Current?.D3DService == null)
                {
                    PluginCore.WriteToChat($"Navigation: 3D service unavailable");
                    return;
                }
                
                // Limit the number of lines to prevent lag
                int maxLines = Math.Min(waypoints.Count - 1, 500); // Max 500 lines
                
                int linesCreated = 0;
                for (int i = 1; i <= maxLines; i++)
                {
                    var current = waypoints[i];
                    var previous = waypoints[i - 1];
                    
                    if (CreateLineBetweenWaypoints(previous, current))
                    {
                        linesCreated++;
                    }
                    
                    // Add small delay every 50 lines to prevent UI freezing
                    if (i % 50 == 0)
                    {
                        System.Threading.Thread.Sleep(1);
                    }
                }
                
                if (waypoints.Count > 501)
                {
                    PluginCore.WriteToChat($"Navigation: Large route - showing {maxLines} of {waypoints.Count} segments");
                }
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"Navigation: Error creating visualization - {ex.Message}");
            }
        }

        private bool CreateLineBetweenWaypoints(NavWaypoint from, NavWaypoint to)
        {
            try
            {
                // Calculate distance
                double distance = Math.Sqrt(
                    Math.Pow((to.NS - from.NS) * 240, 2) +
                    Math.Pow((to.EW - from.EW) * 240, 2) +
                    Math.Pow((to.Z - from.Z) * 240, 2)
                );

                if (distance <= 0) return false;
                
                // Create D3D line object
                var lineObj = CoreManager.Current.D3DService.NewD3DObj();
                if (lineObj == null) return false;
                
                lineObj.SetShape(D3DShape.Cube);
                lineObj.Color = routeColor.ToArgb();

                // Position at midpoint between waypoints
                float midNS = (float)(from.NS + to.NS) / 2;
                float midEW = (float)(from.EW + to.EW) / 2;
                float midZ = (float)((from.Z + to.Z) * 120) + 0.1f; // Slightly higher than UtilityBelt

                lineObj.Anchor(midNS, midEW, midZ);

                // Orient toward destination
                float orientNS = (float)from.NS;
                float orientEW = (float)from.EW;
                float orientZ = (float)(from.Z * 240) + 0.1f;
                
                lineObj.OrientToCoords(orientNS, orientEW, orientZ, true);

                // Scale to create line effect
                lineObj.ScaleX = 0.3f; // Slightly thicker than UtilityBelt
                lineObj.ScaleZ = 0.3f;
                lineObj.ScaleY = (float)distance;

                lineObj.Visible = true;
                lineObjects.Add(lineObj);
                
                return true;
            }
            catch
            {
                return false;
            }
        }

        private void ClearRoute()
        {
            foreach (var obj in lineObjects)
            {
                try
                {
                    obj.Visible = false;
                    obj.Dispose();
                }
                catch { }
            }
            lineObjects.Clear();
        }

        public void Dispose()
        {
            if (!disposed)
            {
                ClearRoute();
                waypoints.Clear();
                disposed = true;
            }
        }
    }
}