using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace Noctalk_client.Splash_screen;

public sealed class RouteAnimationController
{
    private static readonly Brush Node_blue_brush = new SolidColorBrush(Color.FromArgb(230, 73, 217, 255));
    private readonly FrameworkElement mapView;
    private readonly ScaleTransform mapScale;
    private readonly Canvas routeCanvas;
    private readonly TranslateTransform mapTransform;
    private readonly List<RouteNode> route;
    private readonly bool flagReviewAllPlaces;
    private readonly IReadOnlyList<RouteNode> allNodes = WorldMapNodeProvider.Nodes__get();
    private readonly DispatcherTimer timer = new() { Interval = TimeSpan.FromMilliseconds(16) };
    private DateTime startUtc;
    private const double Node_hold_seconds = 2.0;
    private const double Line_start_after_node_seconds = 0.5;
    private const double Line_travel_seconds = 1.6;
    private readonly double mapWidth;
    private readonly double mapHeight;
    private double translateX;
    private double translateY;
    private readonly double scale;

    public RouteAnimationController(FrameworkElement The_map_view, ScaleTransform The_map_scale, Canvas The_route_canvas, TranslateTransform The_map_transform, bool Flag_review_all_places = false)
    {
        mapView = The_map_view;
        mapScale = The_map_scale;
        routeCanvas = The_route_canvas;
        mapTransform = The_map_transform;
        flagReviewAllPlaces = Flag_review_all_places;
        mapWidth = WorldMapNodeProvider.Map_width;
        mapHeight = WorldMapNodeProvider.Map_height;
        double fitScale = Math.Min(mapView.Width / mapWidth, mapView.Height / mapHeight);
        scale = fitScale * 1.3;
        mapScale.ScaleX = scale;
        mapScale.ScaleY = scale;
        route = new RouteGenerator().Route__generate();
        Route__map_position__set(route[0]);
        timer.Tick += (_, _) => Animation__tick();
    }

    public void Animation__start()
    {
        routeCanvas.Children.Clear();
        startUtc = DateTime.UtcNow;
        timer.Start();
    }

    public void Animation__stop()
    {
        timer.Stop();
    }

    private void Animation__tick()
    {
        if (flagReviewAllPlaces)
        {
            Animation__review_all_places__tick((DateTime.UtcNow - startUtc).TotalSeconds);
            return;
        }

        double seconds = (DateTime.UtcNow - startUtc).TotalSeconds;
        double routeSegmentSeconds = Math.Max(Node_hold_seconds, Line_start_after_node_seconds + Line_travel_seconds);
        double cycleSeconds = (route.Count - 1) * routeSegmentSeconds + Node_hold_seconds;
        double wrappedSeconds = seconds % cycleSeconds;
        int activeIndex;
        double segmentProgress;

        if (wrappedSeconds >= (route.Count - 1) * routeSegmentSeconds)
        {
            activeIndex = route.Count - 1;
            segmentProgress = 0;
        }
        else
        {
            activeIndex = (int)(wrappedSeconds / routeSegmentSeconds);
            double segmentElapsed = wrappedSeconds - activeIndex * routeSegmentSeconds;
            segmentProgress = segmentElapsed <= Line_start_after_node_seconds
                ? 0
                : Math.Clamp((segmentElapsed - Line_start_after_node_seconds) / Line_travel_seconds, 0, 1);
        }

        Animation__route__redraw(activeIndex, segmentProgress, seconds);

        RouteNode target = route[Math.Min(route.Count - 1, activeIndex + (segmentProgress > 0.35 ? 1 : 0))];
        double desiredX = mapView.Width / 2.0 - target.X * scale;
        double desiredY = mapView.Height / 2.0 - target.Y * scale;
        double minX = mapView.Width - mapWidth * scale;
        double minY = mapView.Height - mapHeight * scale;
        desiredX = Math.Clamp(desiredX, minX, 0);
        desiredY = Math.Clamp(desiredY, minY, 0);
        translateX += (desiredX - translateX) * 0.018;
        translateY += (desiredY - translateY) * 0.018;
        mapTransform.X = translateX;
        mapTransform.Y = translateY;
    }

    private void Route__map_position__set(RouteNode The_target)
    {
        double desiredX = mapView.Width / 2.0 - The_target.X * scale;
        double desiredY = mapView.Height / 2.0 - The_target.Y * scale;
        double minX = mapView.Width - mapWidth * scale;
        double minY = mapView.Height - mapHeight * scale;
        translateX = Math.Clamp(desiredX, minX, 0);
        translateY = Math.Clamp(desiredY, minY, 0);
        mapTransform.X = translateX;
        mapTransform.Y = translateY;
    }

    private void Animation__review_all_places__tick(double Seconds)
    {
        routeCanvas.Children.Clear();
        foreach (RouteNode node in allNodes)
        {
            routeCanvas.Children.Add(Animation__node__create(node, true, Seconds + node.X * 0.007 + node.Y * 0.003));
            routeCanvas.Children.Add(Animation__review_node_name__create(node));
        }
    }

    private FrameworkElement Animation__review_node_name__create(RouteNode The_node)
    {
        TextBlock textBlock = new()
        {
            Text = The_node.Name,
            Foreground = new SolidColorBrush(Color.FromArgb(210, 185, 243, 255)),
            FontSize = 11,
            FontFamily = new FontFamily("Segoe UI"),
            FontWeight = FontWeights.SemiBold,
            IsHitTestVisible = false,
            Effect = new System.Windows.Media.Effects.DropShadowEffect
            {
                Color = Color.FromRgb(0, 0, 0),
                BlurRadius = 8,
                ShadowDepth = 0,
                Opacity = 0.95
            }
        };

        textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        Canvas.SetLeft(textBlock, Math.Clamp(The_node.X + 10, 2, mapWidth - textBlock.DesiredSize.Width - 2));
        Canvas.SetTop(textBlock, Math.Clamp(The_node.Y - 16, 2, mapHeight - textBlock.DesiredSize.Height - 2));
        return textBlock;
    }

    private void Animation__route__redraw(int Active_index, double Segment_progress, double Seconds)
    {
        routeCanvas.Children.Clear();

        int visibleNodeCount = Active_index + 1;
        for (int index = 0; index < visibleNodeCount && index < route.Count; index++)
        {
            FrameworkElement nodeElement = Animation__node__create(route[index], true, Seconds + index * 0.42);
            nodeElement.Opacity = 1.0;
            routeCanvas.Children.Add(nodeElement);
        }

        for (int index = 0; index < Active_index; index++)
        {
            routeCanvas.Children.Add(Animation__line__create(route[index], route[index + 1], 1.0));
        }

        if (Segment_progress > 0 && Active_index < route.Count - 1)
        {
            routeCanvas.Children.Add(Animation__line__create(route[Active_index], route[Active_index + 1], Segment_progress));
        }

        for (int index = 0; index < visibleNodeCount && index < route.Count; index++)
        {
            routeCanvas.Children.Add(Animation__node_name__create(route[index], Seconds));
        }
    }

    private FrameworkElement Animation__node__create(RouteNode The_node, bool Flag_animated, double Seconds)
    {
        double x = The_node.X;
        double y = The_node.Y;
        double pulse = Flag_animated ? 1.0 + Math.Sin(Seconds * 8.0) * 0.16 : 1.0;
        Grid grid = new() { Width = 40, Height = 40, IsHitTestVisible = false };
        grid.RenderTransform = new ScaleTransform(pulse, pulse, 20, 20);
        Canvas.SetLeft(grid, x - 20);
        Canvas.SetTop(grid, y - 20);

        Ellipse glow = new() { Width = 40, Height = 40, Fill = Node_blue_brush, Opacity = 0.34 };
        Ellipse dot = new()
        {
            Width = 10,
            Height = 10,
            Fill = Node_blue_brush,
            Stroke = Brushes.White,
            StrokeThickness = 1,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center,
            Opacity = 1.0
        };

        grid.Children.Add(glow);
        grid.Children.Add(dot);
        return grid;
    }

    private FrameworkElement Animation__node_name__create(RouteNode The_node, double Seconds)
    {
        FrameworkElement cityName = Animation__node_name_border__create(
            Animation__node_name_text__create(The_node.Name, 18, Color.FromRgb(185, 243, 255)),
            new SolidColorBrush(Color.FromArgb(178, 3, 12, 26)),
            new SolidColorBrush(Color.FromArgb(210, 73, 217, 255)));

        FrameworkElement content = cityName;
        if (The_node.Flag__rendezvous)
        {
            byte redAlpha = (byte)(120 + (Math.Sin(Seconds * 8.0) + 1.0) * 48.0);
            FrameworkElement rendezvous = Animation__node_name_border__create(
                Animation__node_name_text__create("Rendezvous", 14, Color.FromRgb(255, 230, 235)),
                new SolidColorBrush(Color.FromArgb(redAlpha, 165, 12, 30)),
                new SolidColorBrush(Color.FromArgb(235, 255, 82, 104)));

            content = new StackPanel
            {
                Children =
                {
                    rendezvous,
                    cityName
                }
            };
        }

        content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        Canvas.SetLeft(content, Math.Clamp(The_node.X + 16, 8, mapWidth - content.DesiredSize.Width - 8));
        Canvas.SetTop(content, Math.Clamp(The_node.Y - 36 - (The_node.Flag__rendezvous ? 26 : 0), 8, mapHeight - content.DesiredSize.Height - 8));
        return content;
    }

    private static Border Animation__node_name_border__create(FrameworkElement Content, Brush Background, Brush Border_brush)
    {
        return new Border
        {
            Background = Background,
            BorderBrush = Border_brush,
            BorderThickness = new Thickness(1),
            Padding = new Thickness(8, 3, 8, 4),
            IsHitTestVisible = false,
            Child = Content,
            Margin = new Thickness(0, 0, 0, 3)
        };
    }

    private static TextBlock Animation__node_name_text__create(string Text, double Font_size, Color Foreground_color)
    {
        return new TextBlock
        {
            Text = Text,
            Foreground = new SolidColorBrush(Foreground_color),
            FontSize = Font_size,
            FontWeight = FontWeights.SemiBold,
            FontFamily = new FontFamily("Segoe UI"),
            HorizontalAlignment = HorizontalAlignment.Center,
            Effect = new System.Windows.Media.Effects.DropShadowEffect
            {
                Color = Color.FromRgb(73, 217, 255),
                BlurRadius = 12,
                ShadowDepth = 0,
                Opacity = 0.8
            }
        };
    }

    private Shape Animation__line__create(RouteNode From, RouteNode To, double Progress)
    {
        double x1 = From.X;
        double y1 = From.Y;
        double x2 = To.X;
        double y2 = To.Y;

        return new Line
        {
            X1 = x1,
            Y1 = y1,
            X2 = x1 + (x2 - x1) * Math.Clamp(Progress, 0, 1),
            Y2 = y1 + (y2 - y1) * Math.Clamp(Progress, 0, 1),
            Stroke = new SolidColorBrush(Color.FromRgb(67, 164, 255)),
            StrokeThickness = 2.4,
            StrokeStartLineCap = PenLineCap.Round,
            StrokeEndLineCap = PenLineCap.Round,
            Opacity = 0.82,
            Effect = new System.Windows.Media.Effects.BlurEffect { Radius = 0.4 },
            IsHitTestVisible = false
        };
    }
}
