using System;
using System.Diagnostics;
using NUnit.Framework;
using OpenCvSharp;

namespace TIASshot.Tests {
    /// <summary>
    /// メモリ計測ベンチ（ConvertImageMemoryBenchmark / ContinuousShootMemoryBenchmark）が
    /// 共有する補助ロジック．
    ///
    /// - リーク版 ConvertImage の再現（ConvertImageLeaky）
    /// - 計測ユーティリティ（Sample / ReclaimNative / GB / MB / PrintRow）
    /// - 環境変数パースヘルパー（EnvInt / EnvDouble）
    ///
    /// 本番の ColorCorrector.ConvertImage(Mat,Mat) は通常ビルドでは修正後（中間 Mat を
    /// using 解放）であり，リーク挙動はこのクラスでのみ再現する．
    /// </summary>
    internal static class MemBenchSupport {

        // ---- リーク版 ConvertImage -----------------------------------------------

        /// <summary>
        /// 修正前（リーク版）の ConvertImage を忠実に再現したもの．
        /// 中間 Mat（flatten / extended / converted / convertedImage）を Dispose しない．
        /// （git 0003cc2^ の ColorCorrector.ConvertImage(Mat,Mat) と等価）
        ///
        /// 本番コードは無改造のため，リーク再現に必要な internal メソッド（ExtendMat）と
        /// 行列積のみを ColorCorrector インスタンス経由で利用する．
        /// </summary>
        internal static Mat ConvertImageLeaky(ColorCorrector corrector, Mat src, Mat conv) {
            if (src.Type() != MatType.CV_64FC3) {
                src.ConvertTo(src, MatType.CV_64FC3);
            }
            var flatten = src.Reshape(3, src.Height * src.Width);
            var extended = corrector.ExtendMat(flatten, conv.Rows);
            var converted = (extended * conv).ToMat();
            var convertedImage = converted.Reshape(3, src.Height);

            var convImg8 = new Mat();
            convertedImage.ConvertTo(convImg8, MatType.CV_8UC3);
            return convImg8; // flatten / extended / converted / convertedImage は放置（＝リーク）
        }

        // ---- 計測ユーティリティ -------------------------------------------------

        /// <summary>現在のプロセスのメモリ使用量をサンプリングする．</summary>
        internal static (long priv, long ws) Sample() {
            using (var p = Process.GetCurrentProcess()) {
                p.Refresh();
                return (p.PrivateMemorySize64, p.WorkingSet64);
            }
        }

        /// <summary>未参照 Mat のネイティブメモリを GC+ファイナライザで回収する．</summary>
        internal static void ReclaimNative() {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
        }

        internal static double GB(long bytes) => bytes / (1024.0 * 1024.0 * 1024.0);
        internal static double MB(long bytes) => bytes / (1024.0 * 1024.0);

        internal static void PrintRow(string a, string b, string c) {
            TestContext.WriteLine($"{a,-24} | {b,-22} | {c,-22}");
        }

        // ---- 環境変数パースヘルパー ---------------------------------------------

        internal static int EnvInt(string name, int fallback) {
            var v = Environment.GetEnvironmentVariable(name);
            return int.TryParse(v, out var n) ? n : fallback;
        }

        internal static double EnvDouble(string name, double fallback) {
            var v = Environment.GetEnvironmentVariable(name);
            return double.TryParse(v, out var n) ? n : fallback;
        }
    }
}
