/*
 * Copyright (c)2009-2025 DemiVision, LLC. All Rights Reserved. The information
 * herein is the CONFIDENTIAL and PROPRIETARY information of DemiVision, LLC.
 */

using System.Collections.ObjectModel;
using Syncfusion.Maui.Charts;

using DXLib.UI;
using DXLib.UI.Container;

using DXLib.Utils;

namespace iStatVball3;

/*
 * Renders a Trend Graph line graph using the SfCartesianChart library. The X axis is scope specific while the Y axis
 * plots the metric being analyzed.
 */
public class TrendView : DXContent
{
	/* Constants */

	// Color palette
	public static readonly Color[] DefaultPalette =
	[
		Color.FromArgb( "#FFFE2C54" ),		// Team
		Color.FromArgb( "#FF8E24AA" ),		// Players
		Color.FromArgb( "#FF6A1B9A" ),
		Color.FromArgb( "#FF5A66BD" ),
		Color.FromArgb( "#FF2196F3" ),
		Color.FromArgb( "#FF39BFD8" ),
		Color.FromArgb( "#FF00BFA5" ),
		Color.FromArgb( "#FF82E5CE" ),
		Color.FromArgb( "#FFB1F3BE" ),
		Color.FromArgb( "#FFFBC975" ),
		Color.FromArgb( "#FFF86E89" ),
		Color.FromArgb( "#FFCA268C" )
	];

	/* Properties */

	// External access required for PDF export
	public SfCartesianChart Chart { get; private set; }

	/* Fields */
	private JsonTrend jsonTrend;

	// Axes
	private CategoryAxis primaryAxis;
	private NumericalAxis secondaryAxis;
	
	// Custom palette
	private List<Brush> paletteBrushes;

	/* Methods */

	// Post construction initialization
	public void Init( JsonTrend json )
	{
		jsonTrend = json;

		Horizontal = LayoutOptions.Fill;
		Vertical = LayoutOptions.Fill;
		
		BackgroundColor = DXColors.Light4;
		
		// Create custom palette
		paletteBrushes = [];
		
		foreach ( Color color in DefaultPalette )
		{
			paletteBrushes.Add( new SolidColorBrush( color ) );
		}

		// Trend graph
		Chart = new SfCartesianChart
		{
			IsTransposed = false,

			// No title
			Title = null!,
			
			// Background
			Background = DXColors.Light4
		};

		// X,Y axes
		primaryAxis = InitPrimary();
		secondaryAxis = InitSecondary();

		Chart.XAxes.Add( primaryAxis );
		Chart.YAxes.Add( secondaryAxis );

		// Setup trackball
		InitTrackball( primaryAxis );

		// Metric specific formatting (MUST be last)
		InitDataType( secondaryAxis );

		Content = Chart;
	}

	// Initializes Trend Graph x-axis (primary)
	private static CategoryAxis InitPrimary()
	{
		// Title
		ChartAxisTitle title = new()
		{
			TextColor = DXColors.Dark1,

			Background = DXColors.Transparent,
			StrokeWidth = 0,
			Margin = 0,

			FontFamily = DXFonts.Roboto,
			FontAttributes = FontAttributes.Bold,
			FontSize = 14
		};

		// Axis line
		ChartLineStyle lineStyle = new()
		{
			Stroke = DXColors.Dark1,
			StrokeWidth = 2
		};

		// Tick labels
		ChartAxisLabelStyle labelStyle = new()
		{
			TextColor = DXColors.Dark1,

			LabelAlignment = ChartAxisLabelAlignment.Center,
			WrappedLabelAlignment = ChartAxisLabelAlignment.Center,

			Background = DXColors.Transparent,
			StrokeWidth = 0,
			Margin = 4,

			FontFamily = DXFonts.Roboto,
			FontAttributes = FontAttributes.None,
			FontSize = 10
		};

		// Tick marks
		ChartAxisTickStyle tickStyle = new()
		{
			TickSize = 7,
			Stroke = DXColors.Dark1,
			StrokeWidth = 1
		};

		// Grid lines
		ChartLineStyle gridStyle = new()
		{
			Stroke = DXColors.Light1,
			StrokeWidth = 1
		};

		// X axis
		CategoryAxis axis = new()
		{
			Title = title,

			IsVisible = true,
			IsInversed = false,

			// Group by X category label
			ArrangeByIndex = false,

			// Axis
			EdgeLabelsDrawingMode = EdgeLabelsDrawingMode.Center,
			AxisLineStyle = lineStyle,

			// Ticks
			Interval = 1,
			TickPosition = AxisElementPosition.Outside,
			MajorTickStyle = tickStyle,

			// Tick labels
			LabelsIntersectAction = AxisLabelsIntersectAction.None,
			LabelRotation = 0,
			LabelExtent = 10,
			LabelStyle = labelStyle,

			// End range margin
			PlotOffsetStart = 0,
			PlotOffsetEnd = 10,
			AxisLineOffset = 0,

			// Grid lines
			ShowMajorGridLines = true,
			MajorGridLineStyle = gridStyle,
		};

		return axis;
	}

	// Initializes Trend Graph y-axis (secondary)
	private NumericalAxis InitSecondary()
	{
		// Title
		ChartAxisTitle title = new()
		{
			Text = jsonTrend.Label,
			TextColor = DXColors.Dark1,

			Background = DXColors.Transparent,
			StrokeWidth = 0,
			Margin = 5,

			FontFamily = DXFonts.Roboto,
			FontAttributes = FontAttributes.Bold,
			FontSize = 14
		};

		// Axis line
		ChartLineStyle lineStyle = new()
		{
			Stroke = DXColors.Dark1,
			StrokeWidth = 2
		};

		// Tick labels
		ChartAxisLabelStyle labelStyle = new()
		{
			TextColor = DXColors.Dark1,
			LabelAlignment = ChartAxisLabelAlignment.Center,

			Background = DXColors.Transparent,
			StrokeWidth = 0,
			Margin = 4,

			FontFamily = DXFonts.Roboto,
			FontAttributes = FontAttributes.None,
			FontSize = 10
		};

		// Major/minor tick marks
		ChartAxisTickStyle majorTickStyle = new()
		{
			TickSize = 7,
			Stroke = DXColors.Dark1,
			StrokeWidth = 1
		};

		ChartAxisTickStyle minorTickStyle = new()
		{
			TickSize = 4,
			Stroke = DXColors.Dark1,
			StrokeWidth = 1
		};

		// Grid lines
		ChartLineStyle gridStyle = new()
		{
			Stroke = DXColors.Light1,
			StrokeWidth = 1
		};

		// Y axis
		NumericalAxis axis = new()
		{
			Title = title,

			IsVisible = true,
			IsInversed = false,

			EdgeLabelsDrawingMode = EdgeLabelsDrawingMode.Center,
			EdgeLabelsVisibilityMode = EdgeLabelsVisibilityMode.AlwaysVisible,

			// Labels every other tick
			Interval = 1,
			MinorTicksPerInterval = 1,
			TickPosition = AxisElementPosition.Outside,

			MajorTickStyle = majorTickStyle,
			MinorTickStyle = minorTickStyle,

			// Tick labels
			LabelsIntersectAction = AxisLabelsIntersectAction.None,
			LabelStyle = labelStyle,
			LabelRotation = 0,
			LabelExtent = 10,

			// Small end range margin
			PlotOffsetStart = 0,
			PlotOffsetEnd = 10,
			
			AxisLineOffset = 0,
			AxisLineStyle = lineStyle,

			// Grid lines
			ShowMajorGridLines = true,
			ShowMinorGridLines = true,

			MajorGridLineStyle = gridStyle,
			MinorGridLineStyle = gridStyle
		};

		return axis;
	}

	// Initializes trackball (highlights Y values as user drags along X)
	private void InitTrackball( CategoryAxis axis )
	{
		// Marker label
		ChartLabelStyle labelStyle = new()
		{
			StrokeWidth = 0,
			TextColor = DXColors.Light4,

			FontFamily = DXFonts.Roboto,
			FontSize = 12,
			FontAttributes = FontAttributes.Bold,

			LabelFormat = GetFormat()
		};

		// Marker
		ChartMarkerSettings markerSettings = new()
		{
			Type = ShapeType.Diamond,
			StrokeWidth = 0
		};

		// Vertical line
		ChartLineStyle lineStyle = new()
		{
			StrokeWidth = 2,
			Stroke = DXColors.Dark2
		};

		// Axis label
		ChartLabelStyle axisStyle = new()
		{
			TextColor = DXColors.Light4,
			Background = DXColors.Dark2,
			StrokeWidth = 0,

			FontFamily = DXFonts.Roboto,
			FontSize = 12,
			FontAttributes = FontAttributes.Bold
		};

		// Trackball
		ChartTrackballBehavior trackball = new()
		{
			DisplayMode = LabelDisplayMode.FloatAllPoints,
			ActivationMode = ChartTrackballActivationMode.LongPress,

			ShowLabel = true,
			ShowMarkers = true,
			ShowLine = true,

			LabelStyle = labelStyle,
			LineStyle = lineStyle,
			MarkerSettings = markerSettings
		};

		// Config X axis
		axis.ShowTrackballLabel = true;
		axis.TrackballLabelStyle = axisStyle;

		Chart.TrackballBehavior = trackball;
	}

	// Initializes data type specific config
	private void InitDataType( NumericalAxis axis )
	{
		switch ( jsonTrend.Type )
		{
			// 5
			case AnalyzeKeys.IntKey:
			{
				axis.Interval = 2.0;
				axis.Maximum = null;
				axis.Minimum = null;
				break;
			}
			// 5.5
			case AnalyzeKeys.FloatKey:
			{
				axis.Interval = 2.0;
				axis.Maximum = null;
				axis.Minimum = null;
				break;
			}
			// 0.555
			case AnalyzeKeys.FixedKey:
			{
				axis.Interval = 0.200;
				axis.Maximum = 1.00;
				axis.Minimum = -1.00;

				InitAxisLine();
				break;
			}
			// 55.5%
			case AnalyzeKeys.PercentKey:
			{
				axis.Interval = 0.1;
				axis.Maximum = 1.0;
				axis.Minimum = 0.0;
				break;
			}
		}

		// Format string
		axis.LabelStyle.LabelFormat = GetFormat();
	}

	// Returns data type format string for current metric
	private string GetFormat()
	{
		return jsonTrend.Type switch
		{
			AnalyzeKeys.IntKey => "0",			// 5
			AnalyzeKeys.FloatKey => "0.0",		// 5.5
			AnalyzeKeys.FixedKey => "0.000",	// 0.555
			AnalyzeKeys.PercentKey => "0.0%",	// 55.5%

			_ => null,
		};
	}

	// Draws X axis line at Y0
	private void InitAxisLine()
	{
		HorizontalLineAnnotation axisLine = new()
		{
			IsVisible = true,
			CoordinateUnit = ChartCoordinateUnit.Axis,

			X1 = 0,
			Y1 = 0,

			// No labels
			LineCap = ChartLineCap.None,
			ShowAxisLabel = false,

			// Thickness
			Stroke = DXColors.Dark1,
			StrokeWidth = 2
		};

		Chart.Annotations.Add( axisLine );
	}

	/* Series */

	// Adds one data series (line) to graph
	public void AddSeries( List<TrendDatum> data, bool team )
	{
		// No selections
		DataPointSelectionBehavior selection = new()
		{
			Type = ChartSelectionType.None
		};

		// Data series
		LineSeries series = new()
		{
			IsVisible = true,

			XBindingPath = "Label",
			YBindingPath = "Value",

			// Line
			Opacity = 1.0,
			StrokeWidth = team ? 6 : 3,

			// Animation
			EnableAnimation = true,

			// Selection
			SelectionBehavior = selection,
			EnableTooltip = false,

			// Custom palette
			PaletteBrushes = paletteBrushes,

			// Data
			ItemsSource = new ObservableCollection<TrendDatum>( data )
		};

		Chart.Series.Add( series );
	}

	// Clears all data from graph
	public void Clear()
	{
		Chart.Series.Clear();
	}

	/* Update */

	// Updates scope specific graph configuration
	public void UpdateScope( string scope )
	{
		// Special handling for season scope
		if ( scope == DataConfig.SeasonScope )
		{
			primaryAxis.LabelRotation = 45;
			primaryAxis.PlotOffsetEnd = 20;
		}

		// X axis title
		primaryAxis.Title.Text = DXString.Get( GetTitle( scope ) );
	}

	// Returns scope specific X axis title
	private static string GetTitle( string scope )
	{
		return scope switch
		{
			DataConfig.MatchScope => "set.singular",
			DataConfig.TournamentDayScope => "opponent.singular",
			DataConfig.TournamentScope => "opponent.singular",
			DataConfig.SeasonScope => "opponent.singular",
			DataConfig.TeamScope => "season.singular",

			_ => null,
		};
	}

	/* Layout */

	// Defines platform/orientation specific layout sizing
	public override void UpdateLayout( LayoutType type )
	{
		base.UpdateLayout( type );
		
		double wd = DXDevice.GetScreenWd();
		double ht = DXDevice.GetScreenHt();

		double padWd = (ReportLayout.BasePadding * 2) + Margin.HorizontalThickness;
		const double stripeWd = ReportCard.StripeWd;

		// Dynamically size based on orientation
		if ( DXDevice.IsMobile )
		{
			WidthRequest = (type == LayoutType.MobilePortrait) ? (wd * 1.75) : (wd - DXDevice.SafeArea().Left - padWd - stripeWd);
			HeightRequest = (type == LayoutType.MobileLandscape) ? 462 : 375;
		}
		else
		{
			WidthRequest = (wd - padWd - stripeWd);
			HeightRequest = (ht * ((type == LayoutType.Landscape) ? 0.614 : 0.366));
		}
	}
}

//
