package org.kne.debug;

import java.util.ArrayList;
import java.util.List;

/**
 * 高性能插桩计时器
 * 用于在代码中插入时间测量点，输出各阶段耗时
 * 
 * 用法:
 *   private static final boolean DEBUG = false;  // 生产环境关掉
 *   TimeDebugger td = new TimeDebugger("编码流程", DEBUG);
 *   td.mark("读取图片");
 *   // ... 读取图片代码 ...
 *   td.mark("预测");
 *   // ... 预测代码 ...
 *   td.mark("压缩");
 *   // ... 压缩代码 ...
 *   td.print();
 * 
 * 当 DEBUG = false 时，所有方法调用被 JIT 优化为空操作，零开销
 */
public final class TimeDebugger {

    private final boolean enabled;
    private final String name;
    private final List<TimingPoint> points;
    private long lastNanoTime;
    private long startNanoTime;

    /**
     * 创建一个计时器
     * @param name 本次测量的名称（用于输出标题）
     * @param enabled true=启用计时，false=所有操作跳过（零开销）
     */
    public TimeDebugger(String name, boolean enabled) {
        this.enabled = enabled;
        this.name = name;
        // enabled=false 时不分配 ArrayList 内存
        this.points = enabled ? new ArrayList<>() : null;
        if (enabled) {
            this.lastNanoTime = System.nanoTime();
            this.startNanoTime = this.lastNanoTime;
        } else {
            this.lastNanoTime = 0;
            this.startNanoTime = 0;
        }
    }

    /**
     * 标记一个时间点
     * @param label 当前阶段的名称
     */
    public void mark(String label) {
        if (!enabled) return;
        long now = System.nanoTime();
        long durationNs = now - lastNanoTime;
        lastNanoTime = now;
        points.add(new TimingPoint(label, durationNs));
    }

    /**
     * 标记一个时间点，并打印到控制台（实时输出，方便跟踪长流程）
     * @param label 当前阶段的名称
     */
    public void markAndPrint(String label) {
        if (!enabled) return;
        mark(label);
        System.out.printf("  ⏱️ %s: %.3fms\n", label, getLastDurationMs());
    }

    /**
     * 获取上一个阶段的耗时（毫秒）
     */
    public double getLastDurationMs() {
        if (!enabled || points.isEmpty()) return 0;
        return points.get(points.size() - 1).durationMs();
    }

    /**
     * 获取总耗时（毫秒）
     */
    public double getTotalMs() {
        if (!enabled) return 0;
        return (lastNanoTime - startNanoTime) / 1_000_000.0;
    }

    /**
     * 打印所有计时结果
     */
    public void print() {
        if (!enabled) return;
        StringBuilder sb = new StringBuilder();
        sb.append("═══════════════════════════════════════════════\n");
        sb.append("  ⏱️  ").append(name).append("\n");
        sb.append("═══════════════════════════════════════════════\n");
        
        double total = 0;
        for (int i = 0; i < points.size(); i++) {
            TimingPoint p = points.get(i);
            double ms = p.durationMs();
            total += ms;
            sb.append(String.format("  %-20s: %8.3fms  (累计: %8.3fms)\n", 
                p.label, ms, total));
        }
        
        sb.append("───────────────────────────────────────────────\n");
        sb.append(String.format("  %-20s: %8.3fms\n", "总计", total));
        sb.append("═══════════════════════════════════════════════");
        System.err.println(sb.toString());
    }

    /**
     * 重置计时器（不清除已有记录，用于分段测量）
     */
    public void reset() {
        if (!enabled) return;
        this.lastNanoTime = System.nanoTime();
        this.startNanoTime = this.lastNanoTime;
        if (points != null) {
            this.points.clear();
        }
    }

    /**
     * 检查是否启用
     */
    public boolean isEnabled() {
        return enabled;
    }

    /**
     * 内部计时点
     */
    private static final class TimingPoint {
        final String label;
        final long durationNs;

        TimingPoint(String label, long durationNs) {
            this.label = label;
            this.durationNs = durationNs;
        }

        double durationMs() {
            return durationNs / 1_000_000.0;
        }
    }

    // ==================== 静态方法：全局默认实例 ====================

    private static TimeDebugger defaultDebugger;

    /**
     * 获取全局默认计时器（简化用法，不需要 new）
     * 默认启用，如需关闭请使用 get(boolean)
     */
    public static TimeDebugger get() {
        return get(true);
    }

    /**
     * 获取全局默认计时器（可指定开关）
     * @param enabled true=启用，false=关闭
     */
    public static TimeDebugger get(boolean enabled) {
        if (defaultDebugger == null || defaultDebugger.isEnabled() != enabled) {
            defaultDebugger = new TimeDebugger("默认流程", enabled);
        }
        return defaultDebugger;
    }

    /**
     * 重置全局默认计时器
     */
    public static void resetDefault() {
        if (defaultDebugger != null) {
            defaultDebugger.reset();
        }
    }
}