using System.Collections.Generic;
using System.Globalization;
using System.Text;
using UnityEngine;

namespace ServiceSim
{
    /// <summary>
    /// ロボット1台ずつの位置と状態を、**店の中の時間で5秒おきに** 残す
    /// （2026-10-27・エイイチの指示）。出力は robot_track.csv。
    ///
    /// なぜ要るか
    ///   10/27 の夜の 1回（③2台・すれ違い入・種0）が 720 分終わらなかった。
    ///   ロボットが皿を持ったまま 600 秒の打ち切りを 56 回くり返したのだが、
    ///   **そのときロボットがどこで何をしていたかが記録に無かった**。
    ///   stops.csv は「止まった」ときだけ、trips.csv は往復ごとなので、
    ///   「ずっと同じ所に居た」ことが後から言えなかった。
    ///
    /// なぜ 5 秒おき・店の中の時間なのか
    ///   ・1コマ（0.05 秒）ごとに書くと、書く重さで Mac の忙しさが変わり、
    ///     数字そのものが動いてしまう（busycheck で、忙しさが
    ///     stuck_count・robot_share を動かすと分かっている）。
    ///   ・刻みを **ops.NowS（店の中の時間）** で決めるので、
    ///     実時間が速くても遅くても同じ行が出る（取り違えない）。
    ///   ・5 秒おきなら 1回（店の中 200 分）で 2 台 × 2400 = 4800 行ほど。
    ///     上限の 720 分でも 17,280 行で、軽い。
    ///
    /// det_frames.csv（1コマごと・位置の合計）との違い
    ///   det_frames は切り分けの試し用で、ロボット位置を **合計値** でしか
    ///   持たないので1台ずつ追えない。こちらは常に書き、1台ずつ追える。
    /// </summary>
    public class RobotTrackLogger
    {
        public struct Row
        {
            public float tS;
            public int robotId;
            public float x, z;
            public string phase;      // いまの仕事（卓へ / 受け渡し口へ / 立ち往生 など）
            public bool passBy;       // すれ違いの走り方で動いている
            public bool backingOff;   // すれ違いをあきらめて後ずさりしている
            public bool narrow;       // すれ違う幅が足りないと見た
            public int tableId;       // 向かっている卓（無ければ -1）
        }

        readonly List<Row> _rows = new List<Row>();

        public int RowCount { get { return _rows.Count; } }
        public IReadOnlyList<Row> Rows { get { return _rows; } }
        public void Clear() { _rows.Clear(); }

        /// <summary>いまの仕事を日本語で（trips.csv などと言葉をそろえる）</summary>
        public static string PhaseName(ServingRobotController.Phase p)
        {
            switch (p)
            {
                case ServingRobotController.Phase.Idle: return "待機";
                case ServingRobotController.Phase.ToTable: return "卓へ";
                case ServingRobotController.Phase.Dwell: return "卓で止まる";
                case ServingRobotController.Phase.ToPass: return "受け渡し口へ";
                case ServingRobotController.Phase.StuckWait: return "立ち往生";
                case ServingRobotController.Phase.Done: return "往復おわり";
            }
            return "分からない";
        }

        /// <summary>
        /// その時刻のロボットを1台ずつ1行に。
        /// null の枠は飛ばす（ロボット0台の条件では1行も出ない）
        /// </summary>
        public void Sample(float tS, IList<ServingRobotController> robots)
        {
            if (robots == null) return;
            for (int i = 0; i < robots.Count; i++)
            {
                ServingRobotController r = robots[i];
                if (r == null) continue;
                Vector3 p = r.transform.position;
                Row row = new Row();
                row.tS = tS;
                row.robotId = r.robotId;
                row.x = p.x;
                row.z = p.z;
                row.phase = PhaseName(r.CurrentPhase);
                row.passBy = r.PassByMoving;
                row.backingOff = r.PassByBackingOff;
                row.narrow = r.PassByNarrow;
                row.tableId = r.CurrentPhase == ServingRobotController.Phase.Idle ? -1 : r.TableId;
                _rows.Add(row);
            }
        }

        public const string Header =
            "condition_id,seed,t_s,robot_id,x,z,phase,pass_by,backing_off,narrow,table_id";

        public void Write(string path, string conditionId, int seed)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(Header).Append('\n');
            for (int i = 0; i < _rows.Count; i++)
            {
                Row r = _rows[i];
                sb.Append(conditionId).Append(',').Append(seed.ToString(CultureInfo.InvariantCulture)).Append(',')
                  .Append(F(r.tS)).Append(',')
                  .Append(r.robotId.ToString(CultureInfo.InvariantCulture)).Append(',')
                  .Append(F(r.x)).Append(',').Append(F(r.z)).Append(',')
                  .Append(r.phase).Append(',')
                  .Append(r.passBy ? 1 : 0).Append(',')
                  .Append(r.backingOff ? 1 : 0).Append(',')
                  .Append(r.narrow ? 1 : 0).Append(',')
                  .Append(r.tableId.ToString(CultureInfo.InvariantCulture)).Append('\n');
            }
            System.IO.File.WriteAllText(path, sb.ToString(), new UTF8Encoding(false));
        }

        static string F(float v)
        {
            return v.ToString("0.####", CultureInfo.InvariantCulture);
        }
    }
}
