/* 
 * 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.UI.Control;

using DXLib.Utils;

namespace iStatVball3;

/*
 * Renders a Bar Chart using the SfCartesianChart library. Each bar displays a player/rotation label and corresponding
 * metric specific value.
 */
public class BarView : DXContent
{
	/* Constants */

	// Color palette
	private 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 JsonBar jsonBar;

	// Axes
	private CategoryAxis primaryAxis;
	private NumericalAxis secondaryAxis;

	// Custom palette
	private List<Brush> paletteBrushes;
	
	/* Methods */

	// Post construction initialization
	public void Init( JsonBar json )
	{
		jsonBar = json;

		Horizontal = LayoutOptions.Fill;
		Vertical = LayoutOptions.Fill;
		
		BackgroundColor = DXColors.Light4;
		
		// Create custom palette
		paletteBrushes = [];
		
		foreach ( Color color in DefaultPalette )
		{
			paletteBrushes.Add( new SolidColorBrush( color ) );
		}
		
		// Bar chart
		Chart = new SfCartesianChart
		{
			// Horizontal bars
			IsTransposed = true,

			// No title
			Title = null!,

			// Background
			PlotAreaBackgroundView = new DXFill
			{
				Color = DXColors.Light2
			}
		};
			
		// X,Y axes
		primaryAxis = InitPrimary();
		secondaryAxis = InitSecondary();

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

		// Metric specific formatting (MUST be last)
		InitDataType( secondaryAxis );
		
		Content = Chart;
	}

	// Initializes Bar Chart y-axis (primary)
	private static CategoryAxis InitPrimary()
	{
		// Title
		ChartAxisTitle title = new()
		{
			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,
			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
		};

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

			IsVisible = true,
			IsInversed = true,

			// Group by X category label
			ArrangeByIndex = false,

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

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

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

			// Leave room for last axis label
			PlotOffsetStart = 0,
			PlotOffsetEnd = 10,
			AxisLineOffset = 0,

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

		// Register for events
		axis.LabelCreated += OnLabelCreated;
		
		return axis;
	}

	// Initializes Bar Chart x-axis (secondary)
	private NumericalAxis InitSecondary()
	{
		// Title
		ChartAxisTitle title = new()
		{
			Text = jsonBar.Label,
			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,

			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
		};

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

			IsVisible = true,
			IsInversed = false,

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

			CrossesAt = 0,
			RenderNextToCrossingValue = false,

			// 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,

			// Leave room for last axis label
			PlotOffsetStart = 0,
			PlotOffsetEnd = 15,
			
			AxisLineOffset = 0,
			AxisLineStyle = lineStyle,

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

			MajorGridLineStyle = gridStyle,
			MinorGridLineStyle = gridStyle
		};

		return axis;
	}
	
	// Initializes data type specific config
	private void InitDataType( NumericalAxis axis )
	{
		switch ( jsonBar.Type )
		{
			// 5
			case AnalyzeKeys.IntKey:
			{
				axis.Interval = 1;
				axis.Maximum = null;
				axis.Minimum = null;
				break;
			}
			// 5.5
			case AnalyzeKeys.FloatKey:
			{
				axis.Interval = 1.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;

				InitZeroLine();
				break;
			}
			// 55.5%
			case AnalyzeKeys.PercentKey:
			{
				axis.Interval = 0.1;
				axis.Maximum = 1.0;
				axis.Minimum = 0.0;
				break;
			}
			// +5
			case AnalyzeKeys.DeltaKey:
			{
				axis.Interval = 1;
				axis.Maximum = null;
				axis.Minimum = null;

				InitZeroLine();
				break;
			}
		}

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

	// Initializes Y axis line at X0 for fixed/delta data types
	private void InitZeroLine()
	{
		HorizontalLineAnnotation axisLine = new()
		{
			IsVisible = true,
			CoordinateUnit = ChartCoordinateUnit.Axis,

			X1 = -1.0,
			Y1 = 0.0,

			Y2 = 0.0,

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

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

		Chart.Annotations.Add( axisLine );
	}

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

			_ => null,
		};
	}

	/* Series */

	// Sets bar chart data series (only 1 series)
	public void SetSeries( List<BarDatum> data, string label )
	{
		// No selections
		DataPointSelectionBehavior selection = new()
		{
			Type = ChartSelectionType.None
		};
		
		// Label style
		ChartDataLabelStyle style = new()
		{
			Margin = 5,
			LabelPadding = 7,

			StrokeWidth = 1,
			Stroke = DXColors.Dark1,
			
			TextColor = DXColors.Light4,
			
			FontFamily = DXFonts.Roboto,
			FontAttributes = FontAttributes.Bold,
			FontSize = 12,
			
			LabelFormat = GetFormat()
		};

		// Series labels
		CartesianDataLabelSettings settings = new()
		{
			UseSeriesPalette = false,
			LabelStyle = style,
			
			BarAlignment = DataLabelAlignment.Top,
			LabelPlacement = DataLabelPlacement.Auto
		};
		
		// Data series
		ColumnSeries series = new()
		{
			IsVisible = true,

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

			// Bar
			Opacity = 1.0,
			StrokeWidth = 1,
			Stroke = DXColors.Dark1,
			CornerRadius = new CornerRadius( 0 ),
			
			// Markers
			ShowDataLabels = true,
			DataLabelSettings = settings,

			// Animation
			EnableAnimation = true,

			// Selection
			SelectionBehavior = selection,
			EnableTooltip = false,

			// Custom palette
			PaletteBrushes = paletteBrushes,
			
			// Data
			ItemsSource = new ObservableCollection<BarDatum>( data )
		};

		Chart.Series.Add( series );

		// Set dimension specific axis label
		primaryAxis.Title.Text = label;
	}

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

	/* Event Callbacks */
	
	// Sets special font style for first label (TEAM)
	private static void OnLabelCreated( object sender, ChartAxisLabelEventArgs args )
	{
		if ( args.Position == 0 )
		{
			args.LabelStyle = new ChartAxisLabelStyle
			{
				FontFamily = DXFonts.Roboto,
				FontSize = 10,
				FontAttributes = FontAttributes.Bold
			};
		}
	}
	
	/* Layout */

	// Defines platform/orientation specific layout sizing
	public override void UpdateLayout( LayoutType 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));
		}
	}
}

//
