using System.Globalization;
using SheetMe.Core.Models;
namespace SheetMe.Designer.Services;
/// 컨트롤 Id 생성 — 문서 전체에서 유일한 "TextBox1" 식 이름 부여(레거시 관행).
public static class IdGenerator
{
#region Methods
/// 문서의 사용 중 Id 집합 수집
public static HashSet CollectUsed(FormDocument document)
{
var used = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var page in document.Pages)
{
Collect(page.Root, used);
}
return used;
}
///
/// 타입 기반 유일 Id 생성 — 같은 타입의 최대 번호 + 1(레거시 clsNameCreationService 규약).
/// 발급한 Id 는 used 집합에 추가된다(연속 발급 안전).
///
/// 빈 번호를 메우지 않는 것이 핵심이다. TextBox3 을 지우고 새 TextBox 를 놓으면
/// 최소 빈칸 방식은 다시 TextBox3 을 발급하는데, 이름은 저장된 답변(E_SctDta.SctObjNam)이
/// 붙는 열쇠라서 지운 칸의 과거 입력이 새 칸에 그대로 나타난다. 액션 대상과
/// CalcBox 수식도 이름으로 서로를 가리키므로 같은 이름을 재사용하면 조용히 오배선된다.
///
public static string NextId(HashSet used, string type)
{
var next = 1;
foreach (var id in used)
{
if (id.Length <= type.Length || !id.StartsWith(type, StringComparison.OrdinalIgnoreCase))
{
continue;
}
// 접미사가 순수 숫자일 때만 번호로 본다("TextBox1a" 나 "TextBoxTotal" 은 무시)
if (int.TryParse(id.AsSpan(type.Length), NumberStyles.None, CultureInfo.InvariantCulture, out var number)
&& number >= next)
{
next = number + 1;
}
}
var candidate = type + next.ToString(CultureInfo.InvariantCulture);
// 방어: "TextBox0" 같은 값이 이미 있으면 next 가 1 로 남아 충돌할 수 있다
while (!used.Add(candidate))
{
next++;
candidate = type + next.ToString(CultureInfo.InvariantCulture);
}
return candidate;
}
/// 단건 발급 편의 — 문서 스캔 후 1개 생성
public static string NextId(FormDocument document, string type)
=> NextId(CollectUsed(document), type);
private static void Collect(ControlElement element, HashSet used)
{
if (element.Id.Length > 0)
{
used.Add(element.Id);
}
foreach (var child in element.Children)
{
Collect(child, used);
}
}
#endregion
}