using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using System.Collections.ObjectModel;

namespace InABox.Avalonia.Components;

public partial class InkCanvas : UserControl
{
    public static readonly StyledProperty<double> LineThicknessProperty =
        AvaloniaProperty.Register<InkCanvas, double>(nameof(LineThickness), 3.0);

    public static readonly StyledProperty<Color> LineColourProperty =
        AvaloniaProperty.Register<InkCanvas, Color>(nameof(LineColour), Colors.Black);

    public double LineThickness
    {
        get => GetValue(LineThicknessProperty);
        set => SetValue(LineThicknessProperty, value);
    }

    public Color LineColour
    {
        get => GetValue(LineColourProperty);
        set => SetValue(LineColourProperty, value);
    }

    private ObservableCollection<Polyline> Lines = new();

    private Polyline? CurrentLine;

    public event EventHandler? Changed;

    public InkCanvas()
    {
        InitializeComponent();

        Lines.CollectionChanged += Lines_CollectionChanged;
    }

    public void Clear()
    {
        Canvas.Children.Clear();
        Lines.Clear();
        Changed?.Invoke(this, EventArgs.Empty);
    }

    public byte[] SaveImage()
    {
        var width = (int)Math.Floor(Canvas.Bounds.Width);
        var height = (int)Math.Floor(Canvas.Bounds.Height);

        var renderBitmap = new RenderTargetBitmap(new PixelSize(width, height));
        using var context = renderBitmap.CreateDrawingContext();
        foreach(var line in Lines)
        {
            line.Render(context);
        }

        using var stream = new MemoryStream();
        renderBitmap.Save(stream);
        return stream.ToArray();
    }

    private void Lines_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        Canvas.Children.Clear();
        foreach(var line in Lines)
        {
            if (!Canvas.Children.Contains(line))
            {
                Canvas.Children.Add(line);
            }
        }
    }

    Point ConvertToImageCoordinates(Point canvasCoordinates)
    {
        return canvasCoordinates;
    }

    private void Canvas_PointerPressed(object? sender, PointerPressedEventArgs e)
    {
        var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
        CurrentLine = new Polyline
        {
            Points = new ObservableCollection<Point>(new Point[] { position }),
            StrokeThickness = LineThickness,
            Stroke = new SolidColorBrush(LineColour),
            StrokeJoin = PenLineJoin.Round,
            StrokeLineCap = PenLineCap.Round
        };
        Lines.Add(CurrentLine);
    }

    private void Canvas_PointerMoved(object? sender, PointerEventArgs e)
    {
        if(CurrentLine is not null)
        {
            var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
            CurrentLine.Points.Add(position);
        }
    }

    private void Canvas_PointerReleased(object? sender, PointerReleasedEventArgs e)
    {
        if(CurrentLine is not null)
        {
            var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
            CurrentLine.Points.Add(position);
            CurrentLine = null;
            Changed?.Invoke(this, new());
        }
    }
}