using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace JXHelper.Core;
///
/// Động cơ đối soát và bắt chính xác tùy chọn đối thoại NPC theo nội dung text (Text-Based Option Matcher).
/// Khắc phục hoàn toàn nhược điểm của việc chọn cố định theo thứ tự (Option 1 / Phím 1) khi server đổi menu hoặc chèn sự kiện.
/// Hỗ trợ cả chuỗi mã hóa TCVN3 (ABC), Unicode tiếng Việt chuẩn và văn bản không dấu.
///
public static class NpcDialogTextMatcher
{
// Từ khóa mặc định chuẩn cho các tác vụ phổ biến trong Võ Lâm 1
public const string DefaultRepairKeywords = "Sửa trang bị;Sửa chữa;Sửa đồ;Sửa vũ khí;Söa trang bÞ;Söa ch÷a;Söa";
public const string DefaultRepairSubMenuKeywords = "Sửa toàn bộ;Sửa tất cả;Toàn bộ trang bị;Toàn bộ;Toµn bé;TÊt c¶";
public const string DefaultMedicineKeywords = "Dược phẩm tân thủ;Thuốc tân thủ;Nhận hỗ trợ;Dược phẩm;Dîc phÈm;Tn thñ";
public const string DefaultTravelKeywords = "Những nơi đã đi qua;Khu vực luyện công;Điểm luyện công;Nh÷ng n¬i";
///
/// Chuẩn hóa chuỗi để so khớp: chuyển về chữ thường, giải mã TCVN3 nếu có, và loại bỏ dấu thanh.
///
public static string NormalizeForMatching(string input)
{
if (string.IsNullOrWhiteSpace(input)) return string.Empty;
// 1. Chuyển đổi TCVN3 sang Unicode nếu phát hiện ký tự đặc trưng
string unicode = Tcvn3Encoding.ToUnicode(input);
// 2. Chuyển về chữ thường
string lower = unicode.ToLowerInvariant().Trim();
// 3. Loại bỏ ký tự số thứ tự đầu dòng (ví dụ "1. ", "2. ", "[1] ", "(1) ")
lower = Regex.Replace(lower, @"^[\(\[]?\d+[\.\:\-\)\]]\s*", "");
// 4. Thu gọn nhiều khoảng trắng liên tiếp
lower = Regex.Replace(lower, @"\s+", " ").Trim();
return lower;
}
///
/// Loại bỏ toàn bộ dấu thanh tiếng Việt (phục vụ đối soát không dấu).
///
public static string RemoveDiacritics(string text)
{
if (string.IsNullOrWhiteSpace(text)) return string.Empty;
string normalizedString = text.Normalize(NormalizationForm.FormD);
var stringBuilder = new StringBuilder();
foreach (char c in normalizedString)
{
var unicodeCategory = System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c);
if (unicodeCategory != System.Globalization.UnicodeCategory.NonSpacingMark)
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString().Normalize(NormalizationForm.FormC).Replace('đ', 'd').Replace('Đ', 'D');
}
///
/// Kiểm tra xem một dòng tùy chọn có khớp với bất kỳ từ khóa nào trong chuỗi phân tách không.
///
public static bool MatchesKeywords(string optionText, string keywordString)
{
if (string.IsNullOrWhiteSpace(keywordString)) return false;
var keywords = keywordString
.Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(k => k.Trim())
.Where(k => !string.IsNullOrEmpty(k));
return MatchesKeywords(optionText, keywords);
}
///
/// Kiểm tra xem một dòng tùy chọn có khớp với bất kỳ từ khóa nào trong danh sách không.
///
public static bool MatchesKeywords(string optionText, IEnumerable keywords)
{
if (string.IsNullOrWhiteSpace(optionText)) return false;
string normOption = NormalizeForMatching(optionText);
string plainOption = RemoveDiacritics(normOption);
foreach (var kw in keywords)
{
if (string.IsNullOrWhiteSpace(kw)) continue;
string normKw = NormalizeForMatching(kw);
string plainKw = RemoveDiacritics(normKw);
// 1. So khớp Unicode gốc (sau chuẩn hóa TCVN3)
if (normOption.Contains(normKw, StringComparison.OrdinalIgnoreCase))
{
return true;
}
// 2. So khớp không dấu nếu từ khóa dài >= 3 ký tự
if (plainKw.Length >= 3 && plainOption.Contains(plainKw, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
///
/// Tìm vị trí dòng (1-based index) phù hợp nhất từ danh sách các tùy chọn có sẵn.
///
/// Danh sách các dòng tùy chọn hiện có trong bảng thoại NPC
/// Chuỗi từ khóa phân tách bằng dấu chấm phẩy hoặc dấu phẩy
/// Chỉ số dự phòng nếu không tìm thấy dòng nào khớp (mặc định 1)
/// Chuỗi text thực tế đã khớp (nếu tìm thấy)
/// Chỉ số 1-based (1, 2, 3...) của tùy chọn cần chọn
public static int ResolveOptionIndex(
IReadOnlyList options,
string keywordString,
int fallbackIndex,
out string? matchedText)
{
matchedText = null;
if (options == null || options.Count == 0)
{
return Math.Max(1, fallbackIndex);
}
var keywords = (keywordString ?? string.Empty)
.Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(k => k.Trim())
.Where(k => !string.IsNullOrEmpty(k))
.ToList();
if (keywords.Count == 0)
{
return Math.Max(1, fallbackIndex);
}
for (int i = 0; i < options.Count; i++)
{
string line = options[i];
if (MatchesKeywords(line, keywords))
{
matchedText = line;
return i + 1; // 1-based index
}
}
return Math.Max(1, fallbackIndex);
}
///
/// Tìm vị trí dòng (1-based index) phù hợp nhất từ danh sách các tùy chọn có sẵn (không cần out param).
///
public static int ResolveOptionIndex(
IReadOnlyList options,
string keywordString,
int fallbackIndex = 1)
{
return ResolveOptionIndex(options, keywordString, fallbackIndex, out _);
}
///
/// Tìm vị trí dòng (1-based index) với danh sách IEnumerable từ khóa.
///
public static int ResolveOptionIndex(
IReadOnlyList options,
IEnumerable keywords,
int fallbackIndex = 1)
{
string kwStr = string.Join(";", keywords);
return ResolveOptionIndex(options, kwStr, fallbackIndex, out _);
}
///
/// Tính toán mã Virtual Key (0x31 = '1', 0x32 = '2', ...) tương ứng với chỉ số 1-based.
///
public static int GetVirtualKeyForIndex(int oneBasedIndex)
{
int clamped = Math.Clamp(oneBasedIndex, 1, 9);
return 0x30 + clamped; // 0x31 = '1', 0x32 = '2'...
}
///
/// Tính toán ký tự char ('1', '2', ...) tương ứng với chỉ số 1-based.
///
public static char GetCharForIndex(int oneBasedIndex)
{
int clamped = Math.Clamp(oneBasedIndex, 1, 9);
return (char)('0' + clamped);
}
}