using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using NixExampleWinUI.Views;
using NixExampleWinUI.Helpers;
using NixUniversalSDK;
using Windows.ApplicationModel.Resources;
using Windows.UI;

namespace NixExampleWinUI.ViewModels;

/// <summary>
/// View model for <see cref="ColorSwatch"/>
/// </summary>
public class ColorSwatchViewModel : ObservableRecipient
{
    /// <summary>
    /// Fallback <see cref="ReferenceWhite"/> value (<see cref="ReferenceWhite.D50_2"/> is provided by all devices so is used by default)
    /// </summary>
    private const ReferenceWhite DefaultReference = ReferenceWhite.D50_2;

    /// <summary>
    /// Resource loader used to fetch string resources
    /// </summary>
    private static readonly ResourceLoader Loader = ResourceLoader.GetForViewIndependentUse();

    /// <summary>
    /// Brush used to draw stripes on the swatch when no color is available
    /// </summary>
    private static readonly Brush StripesBrush = new TiledBrush() 
    { 
        TextureUri = new Uri("ms-appx:///Assets/StripeTileMirror.png")
    };

    public ColorSwatchViewModel()
    {
    }

    private IMeasurementData? _MeasurementData = null;

    /// <summary>
    /// <see cref="IMeasurementData"/> instance
    /// </summary>
    public IMeasurementData? MeasurementData
    {
        get => _MeasurementData;
        set
        {
            // Hold value of old reference setting, falling back to the
            // default if null
            var currentReference = SelectedReference ?? DefaultReference;

            // Update measurement value
            SetProperty(ref _MeasurementData, value);
            OnPropertyChanged(nameof(IsDataAvailable));

            // Re-select reference white value (triggers display update). If
            // the old selected reference is not available on this measurement,
            // fall back to the default
            SelectedReference = (value?.ProvidesColor(currentReference) == true) ? 
                currentReference : 
                DefaultReference;
        }
    }
    public bool IsDataAvailable => MeasurementData is not null;

    private ReferenceWhite? _SelectedReference;
    
    /// <summary>
    /// Selected <see cref="ReferenceWhite"/> used to get <see cref="IColorData"/> from the measurement
    /// </summary>
    public ReferenceWhite? SelectedReference
    {
        get => _SelectedReference;
        set
        {
            // Update value
            SetProperty(ref _SelectedReference, value);
            
            // Update computed properties
            UpdateDisplayedColor();
        }
    }

    /// <summary>
    /// Forces color swatch UI bound properties to update
    /// </summary>
    private void UpdateDisplayedColor()
    {
        // Update computed properties
        OnPropertyChanged(nameof(FillBrush));
        OnPropertyChanged(nameof(ColorTheme));
        OnPropertyChanged(nameof(LabHeaderText));
        OnPropertyChanged(nameof(LabValueText));
    }

    /// <summary>
    /// Computed <see cref="IColorData"/> based off of the current reference white selection
    /// </summary>
    private IColorData? ColorData => MeasurementData?.ToColorData(SelectedReference ?? ReferenceWhite.D50_2);

    /// <summary>
    /// <see cref="Color"/> representation of <see cref="ColorData"/>
    /// </summary>
    public Color? UIColor => ColorData?.GetUiColor();

    /// <summary>
    /// Sets control to use Dark or Light theme based on the rendered color
    /// </summary>
    public ElementTheme ColorTheme => UIColor.IsDark() == true ? ElementTheme.Dark : ElementTheme.Light;

    /// <summary>
    /// Brush used to draw background of the swatch (solid color or stripes)
    /// </summary>
    public Brush FillBrush
    {
        get
        {
            if (UIColor is Color safeColor)
            {
                return new SolidColorBrush(safeColor);
            }
            else
            {
                return StripesBrush;
            }
        }    
    }

    /// <summary>
    /// Header text indicating reference white (eg - `CIELAB (D50/2°)`)
    /// </summary>
    public string LabHeaderText => (ColorData is not null) ? 
        string.Format(Loader.GetString("LabelCielabTitle"), ColorData.Reference.GetFullName()) : 
        string.Empty;

    /// <summary>
    /// Formatted text label for CIELAB value
    /// </summary>
    public string LabValueText
    {
        get
        {
            var labArray = ColorData?.ConvertTo(ColorType.CIELAB).Value;
            if (labArray is not null)
            {
                return string.Format(
                    Loader.GetString("LabelCielabFormat"), 
                    labArray[0], 
                    labArray[1], 
                    labArray[2]);
            }
            else
            {
                return string.Empty;
            }
        }
    }
}