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

using DXLib.UI;
using DXLib.UI.Layout;
using DXLib.UI.Control;
using DXLib.UI.Container;

using DXLib.UI.Form;
using DXLib.UI.Form.Control;

using DXLib.Data;
using DXLib.Utils;

namespace iStatVball3;

/*
 * Implements controls for entering or editing tally stats for an individual stat category. The cell includes a category
 * title and then a control for each stat type in the category. Each control uses either a numeric keypad for entering
 * or a +/- stepper for editing.
 */
public class EditTallyCell : DXGridLayout
{
	/* Constants */
	const string NewPassKey = "NewPass";

	/* Events */
	public Action TallyChanged { get; set; }

	/* Properties */
	public bool IsEditMode { get; set; }
	public bool IsDisabled { set => Opacity = value ? 0.3 : 1.0; }

	// Any control has been edited?
	public bool HasChanges { get; private set; }

	/* Fields */
	private readonly DXLabel titleLbl;

	// Underlying input controls
	private Dictionary<string,DXFormControl> controls;

	// Stat category ('pass', 'set', 'hit')
	private string categoryKey;

	// Map to StatSummary
	private string statKey;

	// Category keys
	private List<DXItem> items;

	// Underlying data
	private IDictionary<string,int> setSummary;
	private MetricsSkill setMetrics;

	/* Methods */
	public EditTallyCell()
	{
		bool ios = DXDevice.IsIOS;

		BackgroundColor = DXColors.Light4;

		// Layout
		Padding = 0;
		RowSpacing = 5;
		ColumnSpacing = ios ? 25 : 15;

		// Category title
		titleLbl = new DXLabel
		{
			TextColor = DXColors.Dark2,
			Font = DXFonts.Roboto,
			FontSize = 15,

			Vertical = LayoutOptions.Center
		};
	}

	// Post construction initialization
	public void Init( string key, string category )
	{
		categoryKey = key;

		IsDisabled = true;

		bool passing = (key == Stats.PassKey);

		// Used to save raw stats
		statKey = passing ? NewPassKey : category;

		// Title
		titleLbl.Text = category;

		// Passing uses 0-3 or 0-4
		string rating = passing ? (Shell.Settings.IsPass03 ? Settings.Pass03Key : Settings.Pass04Key) : null;

		// Get stat list for category
		string baseKey = passing ? $"legacy.{rating}" : $"legacy.{key}";
		items = DXString.GetLookupList( baseKey );

		controls = new Dictionary<string,DXFormControl>( items.Count );

		// Add control for each stat
		foreach ( DXItem item in items )
		{
			DXFormControl control;

			// Create stepper/keypad
			if ( IsEditMode )
			{
				control = CreateStepper( item );
			}
			else
			{
				control = CreateKeypad( item );
			}

			control.Init();
			control.SetState( DXFormControl.ControlState.Normal );

			string controlKey = GetKey( item );

			controls.Add( controlKey, control );
		}
	}

	// Creates new keypad control for entering stats
	private DXKeypadField CreateKeypad( DXItem item )
	{
		return new DXKeypadField
		{
			Key = item.Key,
			TitleRaw = item.Value,

			Number = 0,
			MinValue = 0,
			MaxValue = 99,

			Hint = DXFormControl.HintType.None,
			Help = null,

			NumberChanged = OnNumberChanged
		};
	}

	// Creates new +/- stepper control for editing stats
	private DXStepper CreateStepper( DXItem item )
	{
		return new DXStepper
		{
			Key = item.Key,
			TitleRaw = item.Value,

			MinValue = 0,
			MaxValue = 99,

			Step = 1,
			IsLooping = false,

			Hint = DXFormControl.HintType.None,
			Help = null,

			NumberChanged = OnNumberChanged,
			ControlTapped = OnStepperTapped
		};
	}

	// Populates each control with value from specified tally stats
	public void Populate( IDictionary<string,int> summary, MetricsSkill metrics )
	{
		setSummary = summary;
		setMetrics = metrics;

		foreach ( DXItem item in items )
		{
			string key = GetKey( item );
			DXFormControl control = controls[ key ];

			// Lookup value
			int summaryValue = (summary.TryGetValue( key, out var value ) ? value : 0);

			// Editing (add in accumulated metrics)
			if ( IsEditMode )
			{
				int metricsValue = StatSummary.GetMetricValue( metrics, key );
				
				// Android: Must ignore internal events
				if ( control is DXStepper stepper )
				{
					stepper.NumberChanged = null;
					stepper.Number = Math.Max( (summaryValue + metricsValue), 0 );
					stepper.NumberChanged = OnNumberChanged;
				}
			}
			// Entering from paper (take directly from summary)
			else
			{
				// Android: Must ignore internal events
				if ( control is DXKeypadField keypad )
				{
					keypad.NumberChanged = null;
					keypad.Number = summaryValue;
					keypad.NumberChanged = OnNumberChanged;
				}
			}
		}
	}

	// Updates specified set summary with all stats from this cell
	public void UpdateStats( IDictionary<string,int> summary, MetricsSkill metrics )
	{
		foreach ( DXItem item in items )
		{
			string key = GetKey( item );
			DXFormControl control = controls[ key ];

			// Field may not exist yet
			if ( !summary.TryGetValue( key, out int value ) )
			{
                value = 0;
                summary.Add( key, value );
			}

			// Editing (must account for summary and metrics)
			if ( IsEditMode )
			{
				if ( control is DXStepper stepper )
				{
					int controlValue = stepper.Number;
					
					// Lookup values
					int summaryValue = value;
					int metricValue = StatSummary.GetMetricValue( metrics, key );

					// Determine how much control changed from previous total
					int delta = (controlValue - metricValue - summaryValue);

					// Adjust summary value
					summary[key] += delta;
				}
			}
            // Entering (take directly from control)
            else
			{
				if ( control is DXKeypadField keypad )
				{
					summary[ key ] = (int) keypad.Number!;
				}
			}
		}
	}

	// Generates canonical key for specified control item
	private string GetKey( DXItem item )
	{
		// Some items must map between v2 and v3, or between rating scales
		string key = statKey switch
		{
			// Map set assist
			"Setting" => item.Key switch
			{
				Stats.AssistKey => "asst",
					
				_ => item.Key,
			},
			// Map block, block assist
			"Blocking" => item.Key switch
			{
				Stats.BlockKey => "blk",
				Stats.BlockAssistKey => "asst",
				
				_ => item.Key,
			},
			
			_ => item.Key
		};

		// 'HittingATT'
		return $"{statKey}{key.ToUpper()}";
	}

	// Returns control item for specified key
	private DXItem GetItem( string key )
	{
		return items.FirstOrDefault( item => item.Key == key );
	}

	// Determines if any control currently has non-zero value
	public bool HasValue()
	{
		foreach ( DXFormControl control in controls.Values )
		{
			// Stepper
			if ( IsEditMode )
			{
				if ( control is DXStepper { Number: > 0 } )
				{
					return true;
				}
			}
			// Keypad
			else
			{
				if ( control is DXKeypadField { Number: > 0 } )
				{
					return true;
				}
			}
		}

		return false;
	}

	/* Event Callbacks */

	// Called anytime tally control has been edited
	private void OnNumberChanged() 
	{
		HasChanges = true;

		// Notify listener
		TallyChanged?.Invoke();
	}

	// User tapped individual control in cell
	private void OnStepperTapped( DXStepper stepper )
	{
		DXItem item = GetItem( stepper.Key );
		string fullKey = GetKey( item );

		// Get component values
		int metricsValue = StatSummary.GetMetricValue( setMetrics, fullKey );
		int summaryValue = setSummary.TryGetValue( fullKey, out var value ) ? value : 0;

		// Create debug popup
		EditTallyDebug debug = [];
		debug.Init( metricsValue, summaryValue );

		DXPopup popup = new( debug )
		{
			ViewWidth = 120,
			ViewHeight = 100
		};

		// Show anchored to control
		popup.ShowFromView( stepper, 30, 0 );
	}

	/* Layout */

	// Redraws cell and controls
	public override void UpdateLayout( LayoutType type )
	{
		// Do NOT call base here
		ClearAll();

		// Dynamic layout based on device/category
		bool mobile = DXDevice.IsMobile;
		bool portrait = (type == LayoutType.Portrait);

		bool pass = (categoryKey == Stats.PassKey);
		bool block = (categoryKey == Stats.BlockKey);

		// Split controls across 2 rows?
		bool split = mobile && (pass || block);

		// 2 or 3 rows
		AddFixedRow( 20 );				// 0: title
		AddStarRow();					// 1: controls
		if ( split ) AddStarRow();		// 2: controls

		int count = controls.Count;

		// Equally distributed width
		if ( !mobile && portrait && pass )
		{
			AddStarColumns( count );
		}
		// Fixed width columns
		else
		{
			AddFixedColumns( count, 90 );
		}

		// Title
		Add( titleLbl, 0, 0, count, 1 );

		// Controls
		for ( int i = 0; i < count; i++ )
		{
			string key = GetKey( items[i] );

			// Max controls per row
			int max = (int) Math.Round( (count / 2.0), MidpointRounding.AwayFromZero );

			// Passing split across 2 rows on mobile
			bool secondRow = split && (i > (max - 1));

			int column = secondRow ? (i - max) : i;
			int row = secondRow ? ((i / max) + 1) : 1;

			// Add control to layout
			Add( controls[ key ], column, row );
		}
	}
}

//
