using System;
using System.Collections.Generic;
using KKAPI.Maker.UI;
using UniRx;
namespace KKAPI.Maker
{
///
/// A wrapper for custom controls used in accessory window (added by using ).
/// It abstracts away switching between accessory slots and provides a simple list of values for each accessory.
///
/// Type of the control to be wrapped. The control has to be added by using or results will be undefined.
/// Type of the control's value.
public class AccessoryControlWrapper where T : BaseEditableGuiEntry
{
///
/// Create a new wrapper.
///
/// Control to be wrapped. The control has to be added by using or results will be undefined.
public AccessoryControlWrapper(T control)
{
if (control == null) throw new ArgumentNullException(nameof(control));
Control = control;
_defaultValue = control.Value;
control.ValueChanged.Subscribe(val => SetValue(AccessoriesApi.SelectedMakerAccSlot, val));
AccessoriesApi.SelectedMakerAccSlotChanged += OnSelectedMakerAccSlotChanged;
AccessoriesApi.AccessoryKindChanged += OnAccessoryKindChanged;
#if KK
AccessoriesApi.AccessoriesCopied += OnAccessoriesCopied;
#endif
AccessoriesApi.AccessoryTransferred += OnAccessoryTransferred;
}
private void OnAccessoryTransferred(object sender, AccessoryTransferEventArgs e)
{
if (CheckDisposed()) return;
AccessoryTransferred?.Invoke(sender, e);
}
#if KK
private void OnAccessoriesCopied(object sender, AccessoryCopyEventArgs e)
{
if (CheckDisposed()) return;
AccessoriesCopied?.Invoke(sender, e);
}
#endif
private void OnAccessoryKindChanged(object sender, AccessorySlotEventArgs accessorySlotEventArgs)
{
if (CheckDisposed()) return;
AccessoryKindChanged?.Invoke(sender, accessorySlotEventArgs);
}
private void OnSelectedMakerAccSlotChanged(object o, AccessorySlotEventArgs accessorySlotEventArgs)
{
if (CheckDisposed()) return;
Control.SetValue(GetValue(accessorySlotEventArgs.SlotIndex), false);
VisibleIndexChanged?.Invoke(o, accessorySlotEventArgs);
}
///
/// The wrapped control.
///
public T Control { get; }
///
/// Get value of the control for the specified accessory.
///
public TVal GetValue(int accessoryIndex)
{
CheckDisposedThrow();
CheckIndexRangeThrow(accessoryIndex);
if (_values.TryGetValue(accessoryIndex, out var result))
return result;
return _defaultValue;
}
///
/// Get value of the control for the currently selected accessory.
///
public TVal GetSelectedValue()
{
return GetValue(CurrentlySelectedIndex);
}
///
/// Set value of the control for the specified accessory.
///
/// Index of the accessory to set the value for
/// Value to set
public void SetValue(int accessoryIndex, TVal value)
{
SetValue(accessoryIndex, value, true);
}
///
/// Set value of the control for the specified accessory.
///
/// Index of the accessory to set the value for
/// Value to set
/// Fire the event if the value actually changed.
public void SetValue(int accessoryIndex, TVal value, bool fireEvents)
{
CheckDisposedThrow();
CheckIndexRangeThrow(accessoryIndex);
_values[accessoryIndex] = value;
if (AccessoriesApi.SelectedMakerAccSlot == accessoryIndex)
Control.SetValue(value, false);
if (fireEvents)
ValueChanged?.Invoke(this, new AccessoryWindowControlValueChangedEventArgs(value, CurrentlySelectedIndex));
}
///
/// Set value of the control for the currently selected accessory.
///
/// Value to set
public void SetSelectedValue(TVal value)
{
SetSelectedValue(value, false);
}
///
/// Set value of the control for the currently selected accessory.
///
/// Value to set
/// Fire the event if the value actually changed.
public void SetSelectedValue(TVal value, bool fireEvents)
{
SetValue(CurrentlySelectedIndex, value, fireEvents);
}
///
/// Index of the currently selected accessory.
///
public int CurrentlySelectedIndex => AccessoriesApi.SelectedMakerAccSlot;
private readonly TVal _defaultValue;
private readonly Dictionary _values = new Dictionary();
private bool _isDisposed;
///
/// Fired when the value of this control changes for any of the accessories.
///
public event EventHandler> ValueChanged;
///
/// Fired when the currently visible accessory was changed by the user clicking on one of the slots.
///
public event EventHandler VisibleIndexChanged;
///
/// Fires when user selects a different accessory in the accessory window.
///
public event EventHandler AccessoryKindChanged;
#if KK
///
/// Fires after user copies accessories between coordinates by using the Copy window.
///
public static event EventHandler AccessoriesCopied;
#endif
///
/// Fires after user copies an accessory within a single coordinate by using the Transfer window.
///
public static event EventHandler AccessoryTransferred;
private static void CheckIndexRangeThrow(int accessoryIndex)
{
if (accessoryIndex < 0 || accessoryIndex >= AccessoriesApi.GetCvsAccessoryCount())
throw new IndexOutOfRangeException("accessoryIndex has to be between 0 and AccessoriesApi.GetCvsAccessoryCount() - 1");
}
private void CheckDisposedThrow()
{
if (CheckDisposed())
throw new ObjectDisposedException("The control has been disposed. Controls only live in Maker, and you need to create a new one every time maker starts");
}
private bool CheckDisposed()
{
if (_isDisposed) return true;
if (Control.IsDisposed)
{
_isDisposed = true;
_values.Clear();
ValueChanged = null;
VisibleIndexChanged = null;
AccessoryKindChanged = null;
AccessoryTransferred = null;
AccessoriesApi.SelectedMakerAccSlotChanged -= OnSelectedMakerAccSlotChanged;
AccessoriesApi.AccessoryKindChanged -= OnAccessoryKindChanged;
AccessoriesApi.AccessoryTransferred -= OnAccessoryTransferred;
#if KK
AccessoriesCopied = null;
AccessoriesApi.AccessoriesCopied -= OnAccessoriesCopied;
#endif
return true;
}
return false;
}
///
/// If true, the control has been disposed and can no longer be used, likely because the character maker exited.
/// A new control has to be created to be used again.
///
public bool IsDisposed => CheckDisposed();
}
}