#nullable enable
using System;
using System.Collections.Generic;
namespace UVC.UIToolkit
{
///
/// UIToolkit 메뉴 시스템의 데이터 모델입니다.
/// 메뉴 아이템 컬렉션을 관리하고 검색 기능을 제공합니다.
///
///
/// 이 클래스는 메뉴 아이템을 효율적으로 관리하기 위해 Dictionary 캐싱을 사용합니다.
/// 검색 시간 복잡도: O(1) (Dictionary 사용)
///
///
///
/// // 모델 생성
/// var model = new UTKTopMenuModel();
///
/// // 메뉴 아이템 추가
/// var fileMenu = new UTKMenuItemData("file", "menu_file");
/// model.AddMenuItem(fileMenu);
///
/// // 메뉴 아이템 검색
/// var found = model.FindMenuItem("file");
///
/// // 메뉴 아이템 제거
/// model.RemoveMenuItem("file");
///
/// // 모든 메뉴 정리
/// model.ClearMenuItems();
///
/// // 사용 후 정리
/// model.Dispose();
///
///
public class UTKTopMenuModel : IDisposable
{
#region Fields
/// 빠른 검색을 위한 메뉴 아이템 인덱스 (ItemId -> MenuItemData)
private readonly Dictionary _menuItemIndex;
#endregion
#region Properties
/// 최상위 메뉴 아이템 리스트
public List MenuItems { get; private set; }
#endregion
#region Constructor
///
/// UTKTopMenuModel의 새 인스턴스를 초기화합니다.
///
public UTKTopMenuModel()
{
MenuItems = new List();
_menuItemIndex = new Dictionary(StringComparer.Ordinal);
}
#endregion
#region Methods
///
/// 메뉴 아이템을 추가합니다.
///
/// 추가할 메뉴 아이템
/// item이 null인 경우
/// 이미 정리된 객체에 접근하는 경우
public void AddMenuItem(UTKMenuItemData item)
{
if (_disposed)
throw new ObjectDisposedException(nameof(UTKTopMenuModel), "이미 정리된 모델에 메뉴 아이템을 추가할 수 없습니다.");
if (item == null)
throw new ArgumentNullException(nameof(item), "추가할 메뉴 아이템이 null입니다.");
MenuItems.Add(item);
// 인덱스에 추가 (재귀적으로 하위 메뉴도 인덱싱)
AddToIndex(item);
}
///
/// 메뉴 아이템을 제거합니다.
///
/// 제거할 메뉴 아이템의 ID
/// 제거 성공 시 true, 그렇지 않으면 false
/// 이미 정리된 객체에 접근하는 경우
///
/// 시간 복잡도: O(n) (n = MenuItems.Count)
/// 하위 메뉴도 함께 제거되며, 제거된 아이템은 Dispose됩니다.
///
public bool RemoveMenuItem(string itemId)
{
if (_disposed)
throw new ObjectDisposedException(nameof(UTKTopMenuModel), "이미 정리된 모델에서 메뉴 아이템을 제거할 수 없습니다.");
if (string.IsNullOrEmpty(itemId))
return false;
// 최상위 메뉴에서 제거 시도
for (int i = 0; i < MenuItems.Count; i++)
{
if (string.Equals(MenuItems[i].ItemId, itemId, StringComparison.Ordinal))
{
var item = MenuItems[i];
MenuItems.RemoveAt(i);
// 인덱스에서 제거 (재귀적으로 하위 메뉴도 제거)
RemoveFromIndex(item);
// 메모리 정리
item.Dispose();
return true;
}
}
// 최상위에서 찾지 못했으면 재귀적으로 하위 메뉴에서 검색
return RemoveMenuItemRecursive(MenuItems, itemId);
}
///
/// 재귀적으로 메뉴 아이템을 검색합니다.
///
/// 검색할 메뉴 아이템의 ID
/// 찾은 메뉴 아이템, 없으면 null
/// 이미 정리된 객체에 접근하는 경우
///
/// 시간 복잡도: O(1) (Dictionary 캐싱 사용)
///
public UTKMenuItemData? FindMenuItem(string itemId)
{
if (_disposed)
throw new ObjectDisposedException(nameof(UTKTopMenuModel), "이미 정리된 모델에서 메뉴 아이템을 검색할 수 없습니다.");
if (string.IsNullOrEmpty(itemId))
return null;
// Dictionary 캐싱으로 O(1) 검색
return _menuItemIndex.TryGetValue(itemId, out var item) ? item : null;
}
///
/// 모든 메뉴 아이템을 초기화합니다.
///
/// 이미 정리된 객체에 접근하는 경우
///
/// 모든 메뉴 아이템을 재귀적으로 Dispose합니다.
///
public void ClearMenuItems()
{
if (_disposed)
throw new ObjectDisposedException(nameof(UTKTopMenuModel), "이미 정리된 모델의 메뉴 아이템을 초기화할 수 없습니다.");
// 모든 메뉴 아이템 정리
foreach (var item in MenuItems)
{
item?.Dispose();
}
MenuItems.Clear();
_menuItemIndex.Clear();
}
#endregion
#region Private Methods
///
/// 메뉴 아이템과 그 하위 메뉴들을 인덱스에 추가합니다.
///
/// 인덱싱할 메뉴 아이템
private void AddToIndex(UTKMenuItemData item)
{
if (item == null)
return;
// 중복 체크 (성능 최적화: ContainsKey 대신 TryAdd 사용)
_menuItemIndex[item.ItemId] = item;
// 하위 메뉴 재귀적 인덱싱
if (item.SubMenuItems != null)
{
foreach (var subItem in item.SubMenuItems)
{
AddToIndex(subItem);
}
}
}
///
/// 메뉴 아이템과 그 하위 메뉴들을 인덱스에서 제거합니다.
///
/// 제거할 메뉴 아이템
private void RemoveFromIndex(UTKMenuItemData item)
{
if (item == null)
return;
_menuItemIndex.Remove(item.ItemId);
// 하위 메뉴 재귀적 제거
if (item.SubMenuItems != null)
{
foreach (var subItem in item.SubMenuItems)
{
RemoveFromIndex(subItem);
}
}
}
///
/// 재귀적으로 메뉴 아이템을 제거합니다.
///
/// 검색할 메뉴 아이템 리스트
/// 제거할 메뉴 아이템 ID
/// 제거 성공 시 true, 그렇지 않으면 false
private bool RemoveMenuItemRecursive(List items, string itemId)
{
if (items == null || string.IsNullOrEmpty(itemId))
return false;
foreach (var item in items)
{
if (item.SubMenuItems != null && item.SubMenuItems.Count > 0)
{
// 하위 메뉴에서 제거 시도
for (int i = 0; i < item.SubMenuItems.Count; i++)
{
if (string.Equals(item.SubMenuItems[i].ItemId, itemId, StringComparison.Ordinal))
{
var subItem = item.SubMenuItems[i];
item.SubMenuItems.RemoveAt(i);
// 인덱스에서 제거
RemoveFromIndex(subItem);
// 메모리 정리
subItem.Dispose();
return true;
}
}
// 더 깊은 하위 메뉴에서 재귀 검색
if (RemoveMenuItemRecursive(item.SubMenuItems, itemId))
return true;
}
}
return false;
}
#endregion
#region IDisposable
private bool _disposed;
///
/// 모든 메뉴 아이템을 정리합니다.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// 리소스를 정리합니다.
///
/// 관리되는 리소스를 정리할지 여부
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
// 모든 메뉴 아이템 재귀적으로 정리
if (MenuItems != null)
{
foreach (var item in MenuItems)
{
item?.Dispose();
}
MenuItems.Clear();
}
// 인덱스 정리
_menuItemIndex?.Clear();
}
_disposed = true;
}
///
/// 소멸자
///
~UTKTopMenuModel()
{
Dispose(false);
}
#endregion
}
}