using System.Collections.Generic;
using UnityEngine;

namespace ServiceSim
{
    /// <summary>席の種類</summary>
    public enum SeatKind
    {
        WallBooth,    // 壁際ブース（両側ベンチ）
        WallTable,    // 壁沿いの卓（壁側ベンチ＋通路側に椅子）
        IslandTable,  // 島の卓（両側に椅子）
        Counter,      // カウンター席（今回は使わないが置ける）
        LargeTable    // 大人数席（今回は使わないが置ける）
    }

    /// <summary>場所の種類</summary>
    public enum PlaceKind
    {
        Kitchen,      // 厨房（通れない）
        Pantry,       // パントリー（水・おしぼり・カトラリー）
        FoodPass,     // 料理の受け渡し口
        DishReturn,   // 下げ膳の返却口
        Entrance,     // 入口
        Vestibule,    // 風除室
        Waiting,      // 待合
        Register,     // レジ
        Toilet,       // トイレ
        DrinkBar,     // ドリンクバー
        Office,       // 事務所・更衣室
        RobotStandby, // ロボットの待機場所（受け渡し口の前をふさがないため）B2
        DrinkBarStand,// ドリンクバーの前に立つ点（機械1台に1つ）B4
        DrinkBarQueue,// ドリンクバーの並ぶ点 B4
        RegisterQueue,// レジの前に並ぶ点（会計に立つ人が1人ずつ並ぶ）
        StaffStandby, // 手の空いたスタッフが待つ点（1人に1つ。太い通路の通り道をふさがない）
        RegisterBack  // レジの内側（スタッフだけが入れる。太い通路からだけ入れる）
    }

    /// <summary>箱の見た目（StoreBuilder がどの prefab を使うかを決める）</summary>
    public enum BoxStyle
    {
        Floor, Wall, Divider, Table, Bench, Chair, Counter, AisleMark,

        /// <summary>
        /// ベンチの座面の見た目（高さ 0.45 m）。当たりを持たないので
        /// NavMesh にも、前のセンサーにも、左右の空きの測りにも効かない。見た目だけ。
        /// </summary>
        BenchSeat,

        /// <summary>
        /// ベンチの背もたれの見た目（机と反対側のふちに薄く・高さ 1.1 m）。当たりなし。
        /// </summary>
        BenchBack,

        /// <summary>
        /// ベンチの当たりの箱（大きさ・位置・高さはベンチのまま、**見た目を持たない**）。
        /// NavMesh と前のセンサーはこの箱だけを見る。見た目は BenchSeat と BenchBack が受け持つ。
        /// </summary>
        BenchBody
    }

    /// <summary>XZ 平面の長方形（y は持たない）</summary>
    public struct PlanRect
    {
        public float xMin, xMax, zMin, zMax;

        public PlanRect(float xMin, float zMin, float xMax, float zMax)
        {
            this.xMin = Mathf.Min(xMin, xMax);
            this.xMax = Mathf.Max(xMin, xMax);
            this.zMin = Mathf.Min(zMin, zMax);
            this.zMax = Mathf.Max(zMin, zMax);
        }

        public static PlanRect FromSize(float xMin, float zMin, float sizeX, float sizeZ)
        {
            return new PlanRect(xMin, zMin, xMin + sizeX, zMin + sizeZ);
        }

        public static PlanRect FromCenter(Vector3 center, float sizeX, float sizeZ)
        {
            return new PlanRect(center.x - sizeX * 0.5f, center.z - sizeZ * 0.5f,
                                center.x + sizeX * 0.5f, center.z + sizeZ * 0.5f);
        }

        public float SizeX { get { return xMax - xMin; } }
        public float SizeZ { get { return zMax - zMin; } }
        public float CenterX { get { return (xMin + xMax) * 0.5f; } }
        public float CenterZ { get { return (zMin + zMax) * 0.5f; } }
        public Vector3 Center { get { return new Vector3(CenterX, 0f, CenterZ); } }
        public bool IsEmpty { get { return SizeX <= 0.0005f || SizeZ <= 0.0005f; } }

        public bool Contains(Vector3 p)
        {
            return p.x >= xMin && p.x <= xMax && p.z >= zMin && p.z <= zMax;
        }

        public PlanRect Expand(float m)
        {
            return new PlanRect(xMin - m, zMin - m, xMax + m, zMax + m);
        }

        public Bounds ToBounds(float yMin, float yMax)
        {
            Bounds b = new Bounds();
            b.SetMinMax(new Vector3(xMin, yMin, zMin), new Vector3(xMax, yMax, zMax));
            return b;
        }

        public override string ToString()
        {
            return "x " + xMin.ToString("0.##") + ".." + xMax.ToString("0.##") +
                   " / z " + zMin.ToString("0.##") + ".." + zMax.ToString("0.##");
        }
    }

    /// <summary>置く箱1つ</summary>
    public class PlanBox
    {
        public string name;
        public BoxStyle style;
        public PlanRect rect;
        public float height;
        /// <summary>下の面の高さ（m）。ふつうは 0</summary>
        public float baseY;

        public PlanBox(string name, BoxStyle style, PlanRect rect, float height)
        {
            this.name = name;
            this.style = style;
            this.rect = rect;
            this.height = height;
            this.baseY = 0f;
        }
    }

    /// <summary>椅子1脚（引けるものと引けないもの）</summary>
    public class PlanChair
    {
        public PlanRect rect;
        public float height;
        /// <summary>引けるか。ブースのベンチは false</summary>
        public bool pullable;
        /// <summary>引く向き（通路の側へ）。XZ の単位ベクトル</summary>
        public Vector3 pullDirection;
    }

    /// <summary>
    /// 座る場所1つぶんの「人が立つ点」。
    /// 通路からここまで人が歩けるかを Build Store が確かめる（席に入れない卓を見つけるため）。
    /// </summary>
    public struct PlanSeatSpot
    {
        /// <summary>どの席か（報告に出す。例「壁側」「通路側」「南ベンチ」）</summary>
        public string name;
        /// <summary>その席に着くために人が立つ点</summary>
        public Vector3 point;

        public PlanSeatSpot(string name, Vector3 point)
        {
            this.name = name;
            this.point = point;
        }
    }

    /// <summary>卓1つ</summary>
    public class PlanSeat
    {
        public int tableId;
        public SeatKind kind;
        public PlanRect tableRect;
        public float tableHeight;

        /// <summary>何人かけの卓か（2人席・4人席）。組の座らせ方に使う</summary>
        public int seatCount = 4;

        /// <summary>ロボットが料理を置くために止まる点（通路の上）</summary>
        public Vector3 deliveryPoint;
        /// <summary>この卓に行くときに使う細い通路の番号（譲り合いの区画に使う）</summary>
        public int aisleIndex = -1;
        public readonly List<PlanChair> chairs = new List<PlanChair>();

        /// <summary>席ごとの「人が立つ点」（Build Store の席への入りやすさの確かめに使う）</summary>
        public readonly List<PlanSeatSpot> spots = new List<PlanSeatSpot>();

        /// <summary>
        /// 人が実際に座る位置（座面・椅子の真ん中）。seatCount 個ある。
        /// 座っている客の形を出す場所に使う（見た目だけ。当たりも NavMesh も持たない）。
        /// </summary>
        public readonly List<Vector3> seatPoints = new List<Vector3>();
    }

    /// <summary>通路1本（譲り合いの区画と、入る前に待つ点）</summary>
    public class PlanAisle
    {
        public string name;
        public PlanRect rect;
        /// <summary>細い通路か（幅を切り替える対象か）</summary>
        public bool narrow;

        // v5b: 「通りにくい通路」の印（hardToPass）はやめた。
        // 狭い道を使うかどうかは、サイトの routes の use（normal / narrow）が正。
        /// <summary>この通路に入る前に待つ点</summary>
        public Vector3 entrance;

        public Bounds Zone(float yMin, float yMax) { return rect.ToBounds(yMin, yMax); }
    }

    /// <summary>場所1つ（厨房・パントリー・受け渡し口など）</summary>
    public class PlanPlace
    {
        public PlaceKind kind;
        public string name;
        public PlanRect rect;
        public float height;
        /// <summary>人が立つ・ロボットが止まる点</summary>
        public Vector3 point;
        /// <summary>通れない箱として置くか</summary>
        public bool solid;
    }

    /// <summary>
    /// 店の「部品をどこに置くか」の一覧。
    /// StoreLayout（の種類ごとの実装）がこれを作り、StoreBuilder がこれだけを見て並べる。
    /// StoreBuilder は格子や帯の組み方を知らない。
    /// </summary>
    public class StorePlan
    {
        /// <summary>床の全体の広がり（カメラの画角とベイク範囲に使う）</summary>
        public PlanRect floor;

        /// <summary>
        /// 実際に床を敷く長方形。建物の外に床が出ないように分けて持つ。
        /// 空なら floor を1枚だけ敷く。
        /// </summary>
        public readonly List<PlanRect> floorPieces = new List<PlanRect>();

        public float wallHeight = 2.4f;
        public float wallThickness = 0.15f;
        public float floorThickness = 0.2f;

        /// <summary>この plan を作ったときの細い通路の幅（m）</summary>
        public float aisleWidth;

        /// <summary>
        /// 人がロボットをよけるときに下がれる場所（通路の外）。
        /// 壁ぞいの卓と卓のすき間など。店の形が入れる。
        /// </summary>
        public readonly List<Vector3> refuges = new List<Vector3>();

        /// <summary>
        /// ロボットの道順（v5b）。サイトが決めた曲がり角の点の並び。
        /// 無ければ null（そのときは今までどおり Unity の道探しで行き先まで行く）。
        /// </summary>
        public RouteBook routes;

        public readonly List<PlanBox> boxes = new List<PlanBox>();
        public readonly List<PlanSeat> seats = new List<PlanSeat>();
        public readonly List<PlanAisle> aisles = new List<PlanAisle>();
        public readonly List<PlanPlace> places = new List<PlanPlace>();

        // ---- 組み立てを助ける小さな道具 ----

        /// <summary>床を1枚足す。floor の広がりも合わせて広げる</summary>
        public void AddFloor(PlanRect rect)
        {
            if (rect.IsEmpty) return;
            if (floorPieces.Count == 0) floor = rect;
            else
            {
                floor = new PlanRect(
                    Mathf.Min(floor.xMin, rect.xMin), Mathf.Min(floor.zMin, rect.zMin),
                    Mathf.Max(floor.xMax, rect.xMax), Mathf.Max(floor.zMax, rect.zMax));
            }
            floorPieces.Add(rect);
        }

        public PlanBox AddBox(string name, BoxStyle style, PlanRect rect, float height)
        {
            if (rect.IsEmpty || height <= 0.0005f) return null;
            PlanBox b = new PlanBox(name, style, rect, height);
            boxes.Add(b);
            return b;
        }

        public PlanAisle AddAisle(string name, PlanRect rect, bool narrow, Vector3 entrance)
        {
            PlanAisle a = new PlanAisle();
            a.name = name;
            a.rect = rect;
            a.narrow = narrow;
            a.entrance = entrance;
            aisles.Add(a);
            return a;
        }

        public PlanPlace AddPlace(PlaceKind kind, string name, PlanRect rect, float height, Vector3 point, bool solid)
        {
            PlanPlace p = new PlanPlace();
            p.kind = kind;
            p.name = name;
            p.rect = rect;
            p.height = height;
            p.point = point;
            p.solid = solid;
            places.Add(p);
            return p;
        }

        /// <summary>場所の点を引く。無ければ fallback</summary>
        public Vector3 PointOf(PlaceKind kind, Vector3 fallback)
        {
            for (int i = 0; i < places.Count; i++)
                if (places[i].kind == kind) return places[i].point;
            return fallback;
        }

        /// <summary>
        /// 同じ種類の場所の点をすべて集める（出てきた順）。
        /// ロボットの待機場所やドリンクバーの立つ点のように、1種類に点がいくつもあるときに使う。
        /// </summary>
        public List<Vector3> PointsOf(PlaceKind kind)
        {
            List<Vector3> list = new List<Vector3>();
            for (int i = 0; i < places.Count; i++)
                if (places[i].kind == kind) list.Add(places[i].point);
            return list;
        }

        public bool HasPlace(PlaceKind kind)
        {
            for (int i = 0; i < places.Count; i++)
                if (places[i].kind == kind) return true;
            return false;
        }

        public PlanRect RectOf(PlaceKind kind)
        {
            for (int i = 0; i < places.Count; i++)
                if (places[i].kind == kind) return places[i].rect;
            return new PlanRect(0f, 0f, 0f, 0f);
        }

        public PlanAisle AisleAt(int index)
        {
            if (index < 0 || index >= aisles.Count) return null;
            return aisles[index];
        }

        /// <summary>細い通路の数</summary>
        public int NarrowAisleCount
        {
            get
            {
                int n = 0;
                for (int i = 0; i < aisles.Count; i++) if (aisles[i].narrow) n++;
                return n;
            }
        }

        public Bounds NavBounds(float padding)
        {
            PlanRect r = floor.Expand(padding);
            return r.ToBounds(-1f, wallHeight + 1f);
        }
    }
}
