#if SELFTEST
using System.Collections.Generic;
using System.IO;
using System.Xml;

namespace TIASshot {

    /// <summary>
    /// セルフテスト・結合ロジックテスト（層②）用の一時設定ファイルを作る（SELFTEST 限定）．
    /// SelfTestRunner と TIASshot.Tests の共通ハーネスの両方から使う．
    /// 同梱 config.xml を複製し，保存先（File/SaveFolder）を作業フォルダ配下に，参照 CSV を基準フォルダの絶対パスに，
    /// 効果音を存在しないパス（再生しない）に差し替えて作業フォルダへ書き出し，Config に読み込む．
    /// 元の config.xml は変更しない．
    /// </summary>
    internal static class SelfTestConfig {

        /// <summary>作業フォルダに書き出す一時設定ファイルの名前</summary>
        public const string FileName = "config.selftest.xml";

        /// <summary>作業フォルダ内の保存先フォルダの名前（File/SaveFolder の差し替え先）</summary>
        public const string SaveFolderName = "data";

        /// <summary>準備した一時設定の情報</summary>
        internal sealed class Prepared {
            /// <summary>書き出した一時設定ファイルのパス</summary>
            public string ConfigPath;
            /// <summary>一時設定の File/SaveFolder（作業フォルダ配下）</summary>
            public string SaveRoot;
            /// <summary>元の config.xml の File/SaveFolder（書き込まないことの確認用）</summary>
            public string UserSaveFolder;
        }

        /// <summary>
        /// 一時設定ファイルを作り，Config.Load で読み込む．
        /// </summary>
        /// <param name="sourceConfig">複製元の config.xml のパス（読むだけ）</param>
        /// <param name="refBaseDir">相対パスで書かれた参照 CSV（File/TccSrgbRef・File/TccXyzRef）の基準フォルダ</param>
        /// <param name="workDir">作業フォルダ（無ければ作る．一時設定と保存先はこの配下に置く）</param>
        /// <param name="overrides">
        /// 上書きする設定（キーは "Calib/Frames" のような Config のパス，値は InnerText）．null 可．
        /// 値が null の項目は要素ごと削除する（設定欠落の注入用）．標準の差し替えより後に適用する．
        /// 存在しないキーに値を設定しようとした場合は InvalidDataException（キーの打ち間違いの検出）．
        /// </param>
        /// <returns>準備した一時設定の情報</returns>
        /// <exception cref="IOException">ファイル・フォルダの読み書きに失敗した場合</exception>
        /// <exception cref="XmlException">複製元が XML として読めない場合</exception>
        /// <exception cref="InvalidDataException">必要な要素が無い場合</exception>
        public static Prepared Prepare(string sourceConfig, string refBaseDir, string workDir,
                IDictionary<string, string> overrides = null) {
            Directory.CreateDirectory(workDir);
            var doc = new XmlDocument();
            doc.Load(sourceConfig);

            var result = new Prepared {
                UserSaveFolder = GetNode(doc, "File/SaveFolder").InnerText,
                SaveRoot = Path.Combine(workDir, SaveFolderName),
            };
            GetNode(doc, "File/SaveFolder").InnerText = result.SaveRoot;
            foreach (var key in new[] { "File/TccSrgbRef", "File/TccXyzRef" }) {
                var node = GetNode(doc, key);
                if (!Path.IsPathRooted(node.InnerText)) node.InnerText = Path.Combine(refBaseDir, node.InnerText);
            }
            var sounds = doc.SelectSingleNode("//Config/Sound");
            if (sounds != null) {
                foreach (XmlNode node in sounds.ChildNodes) {
                    if (node.NodeType == XmlNodeType.Element) node.InnerText = Path.Combine(workDir, "selftest-no-sound.wav");
                }
            }

            if (overrides != null) {
                foreach (var kv in overrides) {
                    var node = GetNode(doc, kv.Key);
                    if (kv.Value == null) {
                        node.ParentNode.RemoveChild(node);
                    } else {
                        node.InnerText = kv.Value;
                    }
                }
            }

            result.ConfigPath = Path.Combine(workDir, FileName);
            doc.Save(result.ConfigPath);
            Config.Load(result.ConfigPath);
            return result;
        }

        private static XmlNode GetNode(XmlDocument doc, string path) {
            var node = doc.SelectSingleNode($"//Config/{path}");
            if (node == null) throw new InvalidDataException($"config.xml に {path} がありません");
            return node;
        }
    }
}
#endif
