using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace DrillTools
{
///
/// DRL 钻带参数替换:将 A01-A14 参数文件中的 H/Z/S/F/U 参数替换到 .drl 文件的刀径定义行。
/// 与 ROU 不同,DRL 刀径行通常已有参数,此处为整体替换;对无参数段的刀径行则追加参数段。
///
internal static class DrlParameterReplacer
{
private const int Gb2312CodePage = 936;
private const double DiameterTolerance = 0.001;
///
/// 匹配参数文件行:T00C<直径>H<参数...>
/// 例:T00C0.749H02500Z+0.000S105.00F105.0U1000.0
///
private static readonly Regex ParamLinePattern = new(
@"^T00C(?\d+(?:\.\d+)?)(?H.+)$",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
///
/// 匹配 .drl 刀径定义行:T<刀号>C<直径>,参数段(H...)可选
/// 例:T01C0.799H05000Z+0.000S060.00F105.0U0700.0 → prefix="T01C0.799"
/// T01C0.799(无参数段)→ prefix="T01C0.799"
/// 匹配后统一重建为 prefix + 参数文件参数段:已有参数段整体替换,无参数段则追加。
///
private static readonly Regex ToolLinePattern = new(
@"^\s*(?T\d+C\d+(?:\.\d+)?)(?:H.+)?$",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
///
/// 匹配 .drl 刀径行直径部分,用于提取直径数值
///
private static readonly Regex ToolLineDiameterPattern = new(
@"^T\d+C(?\d+(?:\.\d+)?)",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
///
/// 匹配参数文件第一行(描述注释行):;<描述>
///
private static readonly Regex DescriptionLinePattern = new(
@"^;(.+)$",
RegexOptions.Compiled);
///
/// 执行参数替换。
///
/// .drl 文件完整路径
/// A01-A14 参数文件完整路径
/// 未在参数文件中找到的直径列表
public static void ReplaceParameters(string drlFilePath, string paramFilePath, out List unmatchedDiameters)
{
// 1. 读取参数文件,提取第一行描述和直径→参数字符串映射
string paramContent = CommandTypeFileReader.ReadAllText(paramFilePath);
var paramLines = DrillTapeLineParser.SplitLines(paramContent);
string paramDescription = ExtractDescription(paramLines);
if (string.IsNullOrEmpty(paramDescription))
throw new InvalidOperationException("参数文件第一行未找到有效的描述信息(格式:;<描述>)");
var paramDict = LoadParameterDictionary(paramLines);
if (paramDict.Count == 0)
throw new InvalidOperationException("参数文件中未找到有效的参数行(格式:T00C<直径>H...)");
// 2. 读取 .drl 文件
string drlContent = CommandTypeFileReader.ReadAllText(drlFilePath);
// 3. 执行替换(同时收集未匹配直径)
var unmatchedSet = new HashSet();
string replacedContent = ReplaceParametersInContent(drlContent, paramDict, paramDescription, unmatchedSet);
// 4. 备份原文件
string backupFilePath = drlFilePath + ".bak";
File.Copy(drlFilePath, backupFilePath, true);
// 5. 写回文件
using var writer = new StreamWriter(drlFilePath, false, Encoding.GetEncoding(Gb2312CodePage));
writer.Write(replacedContent);
unmatchedDiameters = new List(unmatchedSet);
}
///
/// 从参数文件行中提取第一行描述(去掉前导分号)。
///
private static string ExtractDescription(IReadOnlyList paramLines)
{
foreach (var line in paramLines)
{
string trimmed = line.Text.Trim();
var match = DescriptionLinePattern.Match(trimmed);
if (match.Success)
return match.Groups[1].Value.Trim();
}
return string.Empty;
}
///
/// 从参数文件加载直径→参数字符串(从 H 开始到行尾)的映射。
///
private static Dictionary LoadParameterDictionary(IReadOnlyList paramLines)
{
var dict = new Dictionary();
foreach (var line in paramLines)
{
string trimmed = line.Text.Trim();
var match = ParamLinePattern.Match(trimmed);
if (!match.Success)
continue;
if (!double.TryParse(match.Groups["diameter"].Value,
NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter))
continue;
diameter = Math.Round(diameter, 3);
string paramString = match.Groups["params"].Value;
// 同一直径后面覆盖前面
dict[diameter] = paramString;
}
return dict;
}
///
/// 在 .drl 文件内容中执行参数替换:
/// 1. M48 后一行若是注释行则替换为参数文件的描述;否则原样保留并在 M48 后插入描述行
/// 2. M48~% 区域内按直径匹配替换刀径行的参数段(无参数段的刀径行追加参数段)
///
private static string ReplaceParametersInContent(
string drlContent,
Dictionary paramDict,
string paramDescription,
HashSet unmatchedDiameters)
{
var lines = DrillTapeLineParser.SplitLines(drlContent);
// 找到头部边界
int headerStart = -1; // M48 所在行
int headerEnd = -1; // % 所在行
for (int i = 0; i < lines.Count; i++)
{
string trimmed = lines[i].Text.Trim();
if (headerStart < 0 && trimmed == "M48")
headerStart = i;
else if (headerStart >= 0 && trimmed == "%")
{
headerEnd = i;
break;
}
}
if (headerStart < 0 || headerEnd < 0)
throw new InvalidOperationException("DRL 文件格式异常:未找到 M48 或 % 头部标记");
var builder = new StringBuilder(drlContent.Length + 1024);
for (int i = 0; i < lines.Count; i++)
{
if (i == headerStart + 1)
{
// M48 下一行:若是注释行则替换为参数文件的描述;
// 否则(如工具定义行)原样保留,并在其前(M48 后)插入描述行,不破坏数据;
// 该行本身仍按刀径行规则处理(替换/追加/未匹配)。
string trimmed = lines[i].Text.Trim();
if (trimmed.StartsWith(";"))
{
string trailing = GetTrailingWhitespace(lines[i]);
builder.Append(';');
builder.Append(paramDescription);
builder.Append(trailing);
builder.Append(lines[i].LineEnding);
}
else
{
builder.Append(';');
builder.Append(paramDescription);
builder.Append(lines[i].LineEnding);
AppendToolLine(builder, lines[i], trimmed, paramDict, unmatchedDiameters);
}
}
else if (i > headerStart && i < headerEnd)
{
// 头部区域(M48 和 % 之间,排除已处理的第2行)
string trimmed = lines[i].Text.Trim();
AppendToolLine(builder, lines[i], trimmed, paramDict, unmatchedDiameters);
}
else
{
// % 之后或其他区域原样保留
builder.Append(lines[i].Text);
builder.Append(lines[i].LineEnding);
}
}
return builder.ToString();
}
///
/// 按刀径行规则追加一行:匹配直径则重建为 prefix + 参数文件参数段(
/// 已有参数段整体替换,无参数段则追加);未匹配或非刀径行保留原行,
/// 未匹配直径计入 unmatchedDiameters。
///
private static void AppendToolLine(
StringBuilder builder,
DrillTapeLine line,
string trimmed,
Dictionary paramDict,
HashSet unmatchedDiameters)
{
var match = ToolLinePattern.Match(trimmed);
if (!match.Success)
{
// 非刀径行(如其他注释等)原样保留
builder.Append(line.Text);
builder.Append(line.LineEnding);
return;
}
string prefix = match.Groups["prefix"].Value;
string trailing = GetTrailingWhitespace(line);
// 从 prefix 中提取直径
var diaMatch = ToolLineDiameterPattern.Match(prefix);
if (!diaMatch.Success ||
!double.TryParse(diaMatch.Groups["diameter"].Value,
NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter))
{
// 无法提取直径,保留原行
builder.Append(line.Text);
builder.Append(line.LineEnding);
return;
}
double roundedDiameter = Math.Round(diameter, 3);
if (paramDict.TryGetValue(roundedDiameter, out string? newParams))
{
builder.Append(prefix);
builder.Append(newParams);
builder.Append(trailing);
builder.Append(line.LineEnding);
}
else
{
// 未匹配:保留原行
unmatchedDiameters.Add(roundedDiameter);
builder.Append(line.Text);
builder.Append(line.LineEnding);
}
}
///
/// 获取行尾空白字符。
///
private static string GetTrailingWhitespace(DrillTapeLine line)
{
string original = line.Text;
int trimmedLength = original.TrimEnd().Length;
return trimmedLength < original.Length ? original[trimmedLength..] : string.Empty;
}
}
}