-- DLZCalculator.lua
-- 动态发射区间 DLZ/NEZ 计算器模块
-- 依赖 TOFCalculator.lua：必须通过 dofile 加载

local TOFCalc = dofile(LockOn_Options.script_path .. "Systems/TOFCalculator.lua")

-------------------------------------------------------------------------------
-- 1. 静态武器参数表（与 weapon_system.tof.lua 中 cfg 对齐）
-------------------------------------------------------------------------------
local cfg = {
    ["{PLAAF_PL-10E}"] = {
        noLoft          = true,
        static_min_m    = 200,
        vel_host        =   9.0,
        vel_tgt         = -13.0,
        vel_rate        =  -2.1,
        a_miss_G        =  55,
        a_tgt_G         =   8,
        alt_max         =18000.0,
        points = {
            { alt=1000,  head=10500, tail=3500 },
            { alt=5000,  head=15500, tail=5500 },
            { alt=10000, head=27000, tail=10000 },
        },
    },
    ["DIS_PL-12"] = {
        static_min_m      =1000,
        vel_host          =  21.0,
        vel_tgt           = -23.0,
        vel_rate          =  -3.0,
        a_miss_G          =  30,
        a_tgt_G           =   8,
        alt_max           =18000.0,
        points = {
            { alt=1000,  head=25000, tail=8500 },
            { alt=5000,  head=37000, tail=14000 },
            { alt=10000, head=80000, tail=25000 },
        },
        pk                = { d50=8.0, alpha=2.5, pk_thresh=0.8 },
        loft_threshold_km = 18,
        loft_factor       = 1.6,
        loft_delta_km     = 12,
    },
    ["{J20A_PL15}"] = {
        static_min_m      =1000,
        vel_host          =  21,
        vel_tgt           = -23,
        vel_rate          =  -3,
        a_miss_G          =  30,
        a_tgt_G           =   8,
        alt_max           =25000,
        points = {
            { alt=1000,  head=44000, tail=18500 },
            { alt=5000,  head=69000, tail=28000 },
            { alt=10000, head=105000, tail=44000 },
        },
        pk                = { d50=10.0, alpha=3.0, pk_thresh=0.8 },
        loft_threshold_km = 18,
        loft_factor       = 2.1,
        loft_delta_km     = 12,
    },
    ["{J20A_PL17}"] = {
        static_min_m      = 500,
        vel_host          =  50,
        vel_tgt           = -45,
        vel_rate          =  -7,
        a_miss_G          =  45,
        a_tgt_G           =   8,
        alt_max           =200000,
        points = {
            { alt=1000,  head=41000, tail=20000 },
            { alt=5000,  head=72500, tail=25500 },
            { alt=10000, head=120000, tail=46500 },
        },
        pk                = { d50=12.0, alpha=3.0, pk_thresh=0.8 },
        loft_threshold_km = 18,
        loft_factor       = 1.8,
        loft_delta_km     = 12,
    },
}
-- 变体映射
cfg["{BayArm_PLAAF_PL-10E_x4}"] = cfg["{PLAAF_PL-10E}"]
cfg["DIS_PL-12_DUAL_L"]       = cfg["DIS_PL-12"]
cfg["BayArm_J20_PL-15_DUAL"]  = cfg["{J20A_PL15}"]
cfg["J20_PL-15_DUAL"]         = cfg["{J20A_PL15}"]
cfg["{BayArm_J20_PL-17_x4}"]   = cfg["{J20A_PL17}"]

-------------------------------------------------------------------------------
-- 2. 工具函数
-------------------------------------------------------------------------------
local function clamp(x, lo, hi)
    if x < lo then return lo
    elseif x > hi then return hi
    else return x end
end

local function lerp(a, b, t)
    return a + (b - a) * t
end

-- 从静态 points 取最大 head，用作 Rsearch 上限
local function max_head_from(p)
    local mh = 0
    for _,pt in ipairs(p.points) do
        if pt.head > mh then mh = pt.head end
    end
    return mh
end

-------------------------------------------------------------------------------
-- 3. 动态 DLZ 计算器类
-------------------------------------------------------------------------------
local DLZCalc = {}
DLZCalc.__index = DLZCalc

-- 默认动态模型参数（如不传入则用此）
local defaultDyn = {
    N                = 4,     -- PN 制导常数
    theta_max_deg    = 45,    -- 视场/云台限角 (°)
    R_w              = 15,    -- 致命半径 (m)
    R_min_seek       = 0,     -- 寻的启动距离 (m)
    loft = {
        h_max       = 200e3,  -- 高度衰减参考 (m)
        R1          = 10000,  -- loft 启动距离 (m)
        R2          = 18000,  -- loft 最大仰角距离 (m)
        delta_max   = 30,     -- loft 仰角 (°)
    },
}

-- 构造：传入动态参数表
-- params = {
--   Vh, Vt, h,
--   a_m_G, a_t_G,
--   N, theta_max_deg, R_w,
--   R_min_hw, R_min_seek,
--   loft={h_max,R1,R2,delta_max}
-- }
function DLZCalc.new(params)
    params = params or defaultDyn
    local self = setmetatable({}, DLZCalc)
    -- 平台/目标
    self.Vh         = params.Vh
    self.Vt         = params.Vt
    self.h          = params.h
    -- 机动过载
    self.a_m        = params.a_m_G * 9.81 *
                      clamp(1 - self.h/params.loft.h_max, 0, 1)
    self.a_t        = params.a_t_G * 9.81
    -- PN 制导与视觉限
    self.N          = params.N
    self.theta      = math.rad(params.theta_max_deg)
    self.R_w        = params.R_w
    -- 下界先验
    self.R_min_hw   = params.R_min_hw
    self.R_min_seek = params.R_min_seek
    -- loft 曲线
    self.R1         = params.loft.R1
    self.R2         = params.loft.R2
    self.delta_max  = math.rad(params.loft.delta_max)
    -- TOF 缓存
    self.tof_cache  = {}
    return self
end

-- 抛射仰角 δ(R)
function DLZCalc:loft_delta(R)
    if R <= self.R1 then
        return 0
    elseif R >= self.R2 then
        return self.delta_max
    end
    return self.delta_max * (R - self.R1)/(self.R2 - self.R1)
end

-- 水平闭合速度 Vc_h(R)
function DLZCalc:Vc_h(R)
    local delta = self:loft_delta(R)
    return self.Vh * math.cos(delta) + math.abs(self.Vt)
end

-- TOF 调用 + 缓存
function DLZCalc:tof(R)
    local delta = self:loft_delta(R)
    local key   = string.format("%.1f:%.3f", R, delta)
    if self.tof_cache[key] then
        return self.tof_cache[key]
    end
    local launch = { vel = self.Vh, alt = self.h, loft_delta = delta }
    local target = { range = R,    rdot = self.Vt }
    local tof, _ = TOFCalc.calculateTOF(launch, target)
    self.tof_cache[key] = tof
    return tof
end

-- PN 制导 NEZ
function DLZCalc:calc_PN_NEZ()
    local Vc   = self.Vh + math.abs(self.Vt)
    local diff = self.a_m - self.a_t
    if diff <= 0 then return 0 end
    return self.N * Vc*Vc / diff
end

-- 视场/云台限角 NEZ（二分×30）
function DLZCalc:calc_viewfield_NEZ(Rmax)
    local lo, hi = 0, Rmax
    local tol    = 1e-3
    for i=1,30 do
        local mid = 0.5*(lo + hi)
        local T   = self:tof(mid)
        local lhs = mid * math.sin(self.theta)
        local rhs = math.abs(self.Vt)*T + 0.5*self.a_t*T*T + self.R_w
        if lhs >= rhs then hi = mid else lo = mid end
        if hi - lo < tol then break end
    end
    return hi
end

-- 动态最大射程 R_max（二分×30）
function DLZCalc:calc_R_max(Rmax)
    local lo, hi = 0, Rmax
    local tol    = 1e-3
    for i=1,30 do
        local mid = 0.5*(lo + hi)
        local T   = self:tof(mid)
        if T * self:Vc_h(mid) <= mid then lo = mid else hi = mid end
        if hi - lo < tol then break end
    end
    return lo
end

-- 最佳射程 R_best（黄金分割×30）
function DLZCalc:calc_R_best(Rmin, Rmax)
    local phi = (math.sqrt(5) - 1)*0.5
    local a, b = Rmin, Rmax
    local c, d = b - phi*(b - a), a + phi*(b - a)
    local fc   = c - self:Vc_h(c)*self:tof(c)
    local fd   = d - self:Vc_h(d)*self:tof(d)
    for i=1,30 do
        if fc > fd then
            b, d, fd = d, c, fc
            c        = b - phi*(b - a)
            fc       = c - self:Vc_h(c)*self:tof(c)
        else
            a, c, fc = c, d, fd
            d        = a + phi*(b - a)
            fd       = d - self:Vc_h(d)*self:tof(d)
        end
    end
    return (fc > fd) and c or d
end

-- 一次性计算 DLZ 指标
-- 返回表 {R_min, NEZ_PN, NEZ_view, R_max, R_best}
function DLZCalc:compute(R_search_max)
    local nez_pn   = self:calc_PN_NEZ()
    local nez_view = self:calc_viewfield_NEZ(R_search_max)
    local R_min    = math.max(self.R_min_hw, self.R_min_seek, nez_pn, nez_view)
    local R_max    = self:calc_R_max(R_search_max)
    local R_best   = self:calc_R_best(R_min, R_max)
	-- 回退：如果 R_best 距离 R_max 小于 eps，则拉回去
    local eps = 5000                -- 单位：米，比如 1 km
    if R_best > R_max - eps then
        R_best = R_max - eps
    end
    -- 计算最佳射程处的拦截余量 M = R_best - Vc_h*Tof
    local tof_best = self:tof(R_best)
    local margin_m = R_best*1000 - self:Vc_h(R_best)*tof_best    -- 单位：米
    local M_best   = margin_m * 0.001                           -- 转换成 km

    return {
        R_min    = R_min,
        NEZ_PN   = nez_pn,
        NEZ_view = nez_view,
        R_max    = R_max,
        R_best   = R_best,
        M_best   = M_best,
    }
end

-------------------------------------------------------------------------------
-- 4. 对外兼容接口：calculateDLZ(clsid, hostVel, targVel, alt)
-------------------------------------------------------------------------------
local M = {}

function M.calculateDLZ(clsid, hostVel_mps, targVel_mps, alt_m)
    local p = cfg[clsid]
    if not p then
        return 0,0,0
    end

    -- 多点插值 head/mid/tail（米）
    local lo, hi = p.points[1], p.points[#p.points]
    for i=2,#p.points do
        if alt_m <= p.points[i].alt then
            lo, hi = p.points[i-1], p.points[i]
            break
        end
    end
    local t    = clamp((alt_m - lo.alt)/(hi.alt - lo.alt), 0, 1)
    local head = lerp(lo.head, hi.head, t)
               + hostVel_mps * p.vel_host
               + targVel_mps  * p.vel_tgt
               + targVel_mps^2 * p.vel_rate
    local tail = lerp(lo.tail, hi.tail, t)
               + hostVel_mps * p.vel_host
               + targVel_mps  * p.vel_tgt
               + targVel_mps^2 * p.vel_rate

    -- PN/视场 NEZ 模型参数集
    local dynParams = {
        Vh          = hostVel_mps,
        Vt          = targVel_mps,
        h           = alt_m,
        a_m_G       = p.a_miss_G,
        a_t_G       = p.a_tgt_G,
        N           = defaultDyn.N,
        theta_max_deg = defaultDyn.theta_max_deg,
        R_w         = defaultDyn.R_w,
        R_min_hw    = p.static_min_m,
        R_min_seek  = defaultDyn.R_min_seek,
        loft        = {
            h_max     = defaultDyn.loft.h_max,
            R1        = (p.loft_threshold_km or defaultDyn.loft.R1/1000)*1000,
            R2        = (p.loft_threshold_km or defaultDyn.loft.R2/1000)*1000,
            delta_max = p.loft_delta_km or defaultDyn.loft.delta_max,
        },
    }

    -- 构建计算器并运行
    local calc   = DLZCalc.new(dynParams)
    local Rsearch= max_head_from(p)
    local res    = calc:compute(Rsearch)
    local meter_to_km     = 0.001

    -- 四值返回：最小/最佳/最大（km）+ 最大余量 M_best（km）
    return res.R_min * meter_to_km,
           res.R_best * meter_to_km,
           res.R_max * meter_to_km,
           res.M_best * meter_to_km  -- 本次发射在最佳距离处的拦截余量
end

return M
