using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using System.Windows.Input;
using InABox.Core;

namespace InABox.Avalonia.Components;

public class CircularCountdownTimer : Control
{
    private readonly DispatcherTimer _timer;
    private DateTime? _startTime;
    
    public static readonly StyledProperty<IImage?> ImageProperty =
        AvaloniaProperty.Register<CircularCountdownTimer, IImage?>(nameof(Image));

    public IImage? Image
    {
        get => GetValue(ImageProperty);
        set => SetValue(ImageProperty, value);
    }
    
    public static readonly StyledProperty<bool> IsActiveProperty =
        AvaloniaProperty.Register<CircularCountdownTimer, bool>(nameof(IsActive));

    public bool IsActive
    {
        get => GetValue(IsActiveProperty);
        set => SetValue(IsActiveProperty, value);
    }
    
    public static readonly StyledProperty<ICommand?> StartedProperty =
        AvaloniaProperty.Register<CircularCountdownTimer, ICommand?>(nameof(Started));

    public ICommand? Started
    {
        get => GetValue(StartedProperty);
        set => SetValue(StartedProperty, value);
    }
    
    public static readonly StyledProperty<ICommand?> StoppedProperty =
        AvaloniaProperty.Register<CircularCountdownTimer, ICommand?>(nameof(Stopped));

    public ICommand? Stopped
    {
        get => GetValue(StoppedProperty);
        set => SetValue(StoppedProperty, value);
    }
    
        
    public static readonly StyledProperty<IBrush> BackgroundProperty =
        AvaloniaProperty.Register<CircularCountdownTimer, IBrush>(nameof(Background), Brushes.Transparent);

    public IBrush Background
    {
        get => GetValue(BackgroundProperty);
        set => SetValue(BackgroundProperty, value);
    }
    
    public static readonly StyledProperty<double> StrokeThicknessProperty =
        AvaloniaProperty.Register<CircularCountdownTimer, double>(nameof(StrokeThickness), 10.0);

    public double StrokeThickness
    {
        get => GetValue(StrokeThicknessProperty);
        set => SetValue(StrokeThicknessProperty, value);
    }
    
    public static readonly StyledProperty<IBrush> ProgressBackgroundProperty =
        AvaloniaProperty.Register<CircularCountdownTimer, IBrush>(nameof(ProgressBackground), Brushes.Gray);

    public IBrush ProgressBackground
    {
        get => GetValue(ProgressBackgroundProperty);
        set => SetValue(ProgressBackgroundProperty, value);
    }
    
    public static readonly StyledProperty<IBrush> ProgressForegroundProperty =
        AvaloniaProperty.Register<CircularCountdownTimer, IBrush>(nameof(ProgressForeground), Brushes.WhiteSmoke);

    public IBrush ProgressForeground
    {
        get => GetValue(ProgressForegroundProperty);
        set => SetValue(ProgressForegroundProperty, value);
    }
    
    protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
    {
        base.OnPropertyChanged(change);
        if (change.Property == IsActiveProperty)
        {
            if (Equals(change.NewValue,true))
                Start();
            else
                Stop();
        }
    }

    public static readonly StyledProperty<double> DurationProperty =
        AvaloniaProperty.Register<CircularCountdownTimer, double>(nameof(Duration), 15.0);

    public double Duration
    {
        get => GetValue(DurationProperty);
        set
        {
            SetValue(DurationProperty, value);
            InvalidateVisual();
        }
    }
    
    public CircularCountdownTimer()
    {
        _timer = new DispatcherTimer(DispatcherPriority.MaxValue)
        {
            Interval = TimeSpan.FromMilliseconds(200),
        };
        _timer.Tick += TimerTick;
    }

    private void Start()
    {
        _startTime = DateTime.Now;
        _timer.Start();
        Started?.Execute(DataContext);
        InvalidateVisual();
    }
    
    private void Stop()
    {
        _timer.Stop();
        _startTime = null;
        Stopped?.Execute(DataContext);
        InvalidateVisual();
    }
    
    private TimeSpan RemainingTime()
    {
        if (!_startTime.HasValue)
            return TimeSpan.Zero;
        
        var _nowTime = DateTime.Now;
        var _endTime = _startTime.Value.AddSeconds(Duration);
        return _endTime > _nowTime 
            ? _endTime - _nowTime 
            : TimeSpan.Zero;
    }
    
    private void TimerTick(object? sender, EventArgs e)
    {
        if (RemainingTime() <= TimeSpan.Zero)
            IsActive = false;
        else
            InvalidateVisual();
    }
    
    public override void Render(DrawingContext context)
    {
        var boundsRect = new Rect(Bounds.Left+Margin.Left, Bounds.Top+Margin.Top, Bounds.Width-(Margin.Left+Margin.Right), Bounds.Height-(Margin.Top+Margin.Bottom));
        
        double centerX = boundsRect.Width / 2;
        double centerY = boundsRect.Height / 2;
        double radius = Math.Min(centerX, centerY) - StrokeThickness;

        var backgroundPen = new Pen(ProgressBackground, StrokeThickness);
        var progressPen = new Pen(ProgressForeground, StrokeThickness)
        {
            LineCap = PenLineCap.Flat // Smooth arc edges
        };
        context.FillRectangle(Background, boundsRect);

        // Draw background circle
        context.DrawEllipse(null, backgroundPen, new Point(centerX, centerY), radius, radius);

        if (Image != null)
        {
            var boxwidth = Math.Sqrt(2) * radius;
            Rect rect = new Rect(centerX - (boxwidth/2), centerY - (boxwidth/2), boxwidth, boxwidth);
            context.DrawImage(Image, rect);
        }

        double _remainingTime = RemainingTime().TotalSeconds;
        if (_remainingTime.IsEffectivelyGreaterThan(0.0))
        {
            // Calculate the sweep angle
            double sweepAngle = (_remainingTime / Duration) * 360;
            double startAngle = -90;
            double endAngle = startAngle + sweepAngle;

            // Convert angles to radians
            double startRad = Math.PI * startAngle / 180.0;
            double endRad = Math.PI * endAngle / 180.0;

            // Calculate points on the circle
            var startPoint = new Point(
                centerX + radius * Math.Cos(startRad),
                centerY + radius * Math.Sin(startRad)
            );

            var endPoint = new Point(
                centerX + radius * Math.Cos(endRad),
                centerY + radius * Math.Sin(endRad)
            );

            // Create an arc geometry
            var geometry = new StreamGeometry();
            using (var ctx = geometry.Open())
            {
                ctx.BeginFigure(startPoint, false);
                ctx.ArcTo(endPoint, new Size(radius, radius), 0, sweepAngle > 180, SweepDirection.Clockwise);
            }

            // Draw the progress arc
            context.DrawGeometry(null, progressPen, geometry);
        }

    }
}

