IEnumerator RecurveOutBreak(BacteriaPlace place)
    {
        var left  = place.LeftSide;
        var right = place.RightSide;

        yield return(null);

        if (!left.OutBreakInCurrRound)
        {
            for (int i = 0; i < 2; i++)
            {
                left.AddToken(bacteriaSrc.DrawByColor(left.Color));
                if (left.OutBreak())
                {
                    left.OutBreakInCurrRound = true;
                    yield return(Observable.FromCoroutine(_ => RecurveOutBreak(left)).ToYieldInstruction());
                }
            }
        }

        if (!right.OutBreakInCurrRound)
        {
            for (int i = 0; i < 2; i++)
            {
                right.AddToken(bacteriaSrc.DrawByColor(right.Color));
                if (right.OutBreak())
                {
                    right.OutBreakInCurrRound = true;
                    yield return(Observable.FromCoroutine(_ => RecurveOutBreak(right)).ToYieldInstruction());
                }
            }
        }
    }
    // Use this for initialization
    void Start()
    {
        // View 初期化
        _handViews = new List <HandView>(NumberOfPlayers);
        for (int i = 0; i < NumberOfPlayers; i++)
        {
            _handViews.Add(Instantiate(_handPrefab).GetComponent <HandView>());
            _handViews[i].transform.position = new Vector3(-22, (i + 1) * -21, 0);
        }

        _commonRes = GameObject.Find("ComRes").GetComponent <CommonResourceView>();

        _bacterias = new List <BacteriaPlaceView>();
        _bacterias.Add(GameObject.Find("bacteria_place_red").GetComponent <BacteriaPlaceView>());
        _bacterias.Add(GameObject.Find("bacteria_place_blue").GetComponent <BacteriaPlaceView>());
        _bacterias.Add(GameObject.Find("bacteria_place_yellow").GetComponent <BacteriaPlaceView>());
        _bacterias.Add(GameObject.Find("bacteria_place_green").GetComponent <BacteriaPlaceView>());
        _bacterias.Add(GameObject.Find("bacteria_place_purple").GetComponent <BacteriaPlaceView>());

        // Model 初期化
        deckModel    = CreateDeck();
        bacteriaSrc  = new BacteriaSource();
        playerModels = new List <Player>();
        for (int index = 0; index < NumberOfPlayers; index++)
        {
            playerModels.Add(new Player());
        }
        resourceModel  = new CommonResource(CommonResourcesCapacity);
        bacteriaPlaces = new List <BacteriaPlace>();
        foreach (ColorElement color in Enum.GetValues(typeof(ColorElement)))
        {
            var place = new BacteriaPlace(color);
            bacteriaPlaces.Add(place);
        }

        bacteriaPlaces[0].LeftSide = bacteriaPlaces[4];
        for (int i = 0; i < bacteriaPlaces.Count; i++)
        {
            bacteriaPlaces[i].RightSide = bacteriaPlaces[i % bacteriaPlaces.Count];
            bacteriaPlaces[i % bacteriaPlaces.Count].LeftSide = bacteriaPlaces[i].RightSide;
            _bacterias[i].bacteriaPlace = bacteriaPlaces[i];
        }

        _ripenerVeiws = new List <List <RipenerView> >();
        for (int i = 0; i < NumberOfPlayers; i++)
        {
            var ripener = new List <RipenerView>();
            for (int j = 0; j < NumberOfRipeners; j++)
            {
                var view = Instantiate(_ripenerPrefab).GetComponent <RipenerView>();
                view.ModelID = playerModels[i].Ripeners[j].ID;
                view.ripener = playerModels[i].Ripeners[j];
                ripener.Add(view);
                ripener[j].transform.position = new Vector3((i + 1) * 32.5f, 7 - (j * 20), 0);
            }
            _ripenerVeiws.Add(ripener);
        }



        Observable.FromCoroutine(PhaseControlMain).Subscribe().AddTo(gameObject);
    }
    IEnumerator PhaseControlMain()
    {
        _passButton.Enabled = false;

        // 山札シャッフル
        deckModel.Shuffle();

        foreach (var player in playerModels)
        {
            player.Hand.ObserveCountChanged().Subscribe(count => _handViews[player.Index].CardCount = count).AddTo(_handViews[player.Index]);
        }
        resourceModel.Cards.ObserveCountChanged().Subscribe(count => _commonRes.CardCount = count).AddTo(_commonRes);


        // 各プレイヤーに8枚ずつ肉カードを配る
        for (int n = 0; n < InitialHand; n++)
        {
            foreach (var playerModel in playerModels)
            {
                var cardModel = deckModel.Open();
                var cardView  = mainDeck.CreateCard(cardModel.Type, cardModel.Color, cardModel.ID);
                cardView.transform.parent = _handViews[playerModel.Index].transform;
                cardVM[cardView]          = cardModel;
                cardView.isHand           = true;

                yield return(mainDeck.OpenAnimation(cardView).ToYieldInstruction());

                playerModel.AddHand(cardModel);

                if (n == InitialHand - 1 && playerModel.Index == NumberOfPlayers - 1)
                {
                    yield return(_handViews[playerModel.Index].AddHandAnimation(cardView).ToYieldInstruction());
                }
                else
                {
                    _handViews[playerModel.Index].AddHandAnimation(cardView).Subscribe();
                }
            }
        }

        // 共通リソース置き場に肉を配置
        GameObject ComRes = GameObject.Find("ComRes");

        for (int n = 0; n < CommonResourcesCapacity; n++)
        {
            var cardModel = deckModel.Open();
            var cardView  = mainDeck.CreateCard(cardModel.Type, cardModel.Color, cardModel.ID);
            cardVM[cardView]             = cardModel;
            cardView.transform.parent    = ComRes.transform;
            cardView.commonResourceIndex = n + 1;

            yield return(mainDeck.OpenAnimation(cardView).ToYieldInstruction());

            resourceModel.AddCard(cardModel);
            yield return(_commonRes.AddResourceAnimation(cardView).ToYieldInstruction());
        }

        // 初期熟成
        while (playerModels.SelectMany(player => player.Ripeners).Select(r => r.Empty.Value).Where(b => b == true).Count() != 0)
        {
            for (int index = 0; index < NumberOfPlayers; index++)
            {
                while (true)
                {
                    var playerModel = playerModels[index];
                    var handView    = _handViews[index];
                    var cardViews   = playerModel.Hand.Select(model => handView.GetCardView(model.ID));

                    // カード選択
                    var cardSelection = Observable.Amb(cardViews.Select(_ => _.OnTouchAsObservabale)).First().ToYieldInstruction();
                    yield return(cardSelection);

                    if (!cardSelection.HasResult)
                    {
                        continue;
                    }

                    var selectedCard      = cardSelection.Result as CardControl;
                    var selectedCardModel = cardVM[selectedCard];

                    // みんなに選択された肉の情報を伝える
                    curentSelectedMeat = selectedCardModel;

                    yield return(selectedCard.SelectedAnimation().ToYieldInstruction());

                    // 熟成器選択
                    var ripenersModel = playerModel.Ripeners;
                    var ripenersViews = ripenersModel.Where(r => r.Empty.Value).Select(model => _ripenerVeiws[index].FirstOrDefault(v => v.ModelID == model.ID));

                    var ripenerSelection = Observable.Amb(ripenersViews.Select(_ => _.OnTouchAsObservabale)).First().ToYieldInstruction();
                    yield return(ripenerSelection);

                    if (!ripenerSelection.HasResult)
                    {
                        continue;
                    }

                    // みんなに肉の選択が終わったことを伝える
                    curentSelectedMeat = null;

                    var selectedRipener = ripenerSelection.Result as RipenerView;

                    var selectedRipenerModel = ripenersModel.FirstOrDefault(m => m.ID == selectedRipener.ModelID);

                    playerModel.RemoveHand(selectedCardModel);
                    handView.RemoveHand(selectedCard);
                    selectedRipenerModel.AddCard(selectedCardModel);
                    yield return(selectedRipener.AddCardAnimation(selectedCard).ToYieldInstruction());

                    // カード削除アニメーション
                    yield return(selectedCard.RemovedAnimation().ToYieldInstruction());

                    // カード整頓
                    yield return(_handViews[playerModel.Index].ArrangeHandAnimation().ToYieldInstruction());

                    break;
                }
            }
        }

        foreach (var ripener in playerModels.SelectMany(player => player.Ripeners))
        {
            ripener.AddDay();
        }

        var cardTop        = deckModel.Open();
        var tmpCardTopView = mainDeck.CreateCard(cardTop.Type, cardTop.Color, cardTop.ID);

        tmpCardTopView.isBack  = true;
        cardVM[tmpCardTopView] = cardTop;

        yield return(null); // オープンしたカードのViewが有効になるのを待つために1フレームずらす

        for (int round = 0; round < int.MaxValue; round++)
        {
            Debug.Log("Round : " + (round + 1));

            int startPlayer = round % NumberOfPlayers;

            for (int i = 0; i < NumberOfPlayers; i++)
            {
                int index = (round + i) % NumberOfPlayers;
                Debug.Log("プレイヤー" + index);
                int action = 0;
                while (true)
                {
                    Debug.Log("アクション : " + (action + 1));

                    // 手札、共通リソース、山札の選択
                    var playerModel      = playerModels[index];
                    var handView         = _handViews[index];
                    var handCardControls = playerModel.Hand.Select(model => handView.GetCardView(model.ID));
                    var resCardControls  = resourceModel.Availables.Select(c => cardVM.First(_ => _.Key.ModelID == c.ID).Key);
                    var cardTopView      = cardVM.Where(c => c.Key.ModelID == cardTop.ID).Select(kv => kv.Key);
                    var cardViews        = handCardControls.Concat(resCardControls).Concat(cardTopView);

                    Debug.Log(cardViews.Count());

                    // カード選択
                    var cardSelection = Observable.Amb(cardViews.Select(_ => _.OnTouchAsObservabale)).First().ToYieldInstruction();
                    yield return(cardSelection);

                    if (!cardSelection.HasResult)
                    {
                        continue;
                    }

                    var selectedCardControl = cardSelection.Result as CardControl;
                    yield return(selectedCardControl.SelectedAnimation().ToYieldInstruction());

                    var selectedCardModel = cardVM[selectedCardControl];
                    if (handCardControls.Contains(selectedCardControl))
                    {
                        Debug.Log("手札がクリックされた");

                        // みんなに選択された肉の情報を伝える
                        curentSelectedMeat = selectedCardModel;

                        var touchables    = _bacterias.Select(b => b.OnTouchAsObservabale);
                        var ripenersViews = playerModel.Ripeners.Where(r => r.CanAdd(selectedCardModel)).Select(model => _ripenerVeiws[index].FirstOrDefault(v => v.ModelID == model.ID));
                        touchables = touchables.Concat(ripenersViews.Select(r => r.OnTouchAsObservabale));

                        var selection = Observable.Amb(touchables).First().ToYieldInstruction();
                        yield return(selection);

                        if (!selection.HasResult)
                        {
                            continue;
                        }

                        var touched = selection.Result;
                        if (touched is RipenerView)
                        {
                            Debug.Log("熟成器に追加");
                            playerModel.RemoveHand(selectedCardModel);
                            playerModel.Ripeners.Find(r => r.ID == (touched as RipenerView).ModelID).AddCard(selectedCardModel);
                            handView.RemoveHand(selectedCardControl);
                            yield return((touched as RipenerView).AddCardAnimation(selectedCardControl).ToYieldInstruction());
                        }
                        else if (touched is BacteriaPlaceView)
                        {
                            Debug.Log("菌トークンを除去");
                            playerModel.RemoveHand(selectedCardModel);

                            // todo: 除去できるできない判定
                            BacteriaPlace bp = (touched as BacteriaPlaceView).bacteriaPlace;
                            int           removeBacteriaCount = bp.Removable(selectedCardModel);
                            bp.Remove(selectedCardModel);
                            Debug.Log("Removable End");
                            handView.RemoveHand(selectedCardControl);
                            yield return((touched as BacteriaPlaceView).AddCardAnimation(selectedCardControl, removeBacteriaCount).ToYieldInstruction());
                        }
                        // みんなに肉の選択が終わったことを伝える
                        curentSelectedMeat = null;

                        // カード削除アニメーション
                        yield return(selectedCardControl.RemovedAnimation().ToYieldInstruction());

                        // カード整頓
                        yield return(_handViews[playerModel.Index].ArrangeHandAnimation().ToYieldInstruction());
                    }
                    else if (resCardControls.Contains(selectedCardControl))
                    {
                        Debug.Log("共通リソースがクリックされた");
                        playerModel.AddHand(resourceModel.GetCard(selectedCardModel));
                        selectedCardControl.isHand = true;
                        yield return(_handViews[playerModel.Index].AddHandAnimation(selectedCardControl).ToYieldInstruction());

                        var cardModel = deckModel.Open();
                        var cardView  = mainDeck.CreateCard(cardModel.Type, cardModel.Color, cardModel.ID);
                        cardVM[cardView]          = cardModel;
                        cardView.transform.parent = ComRes.transform;
                        yield return(mainDeck.OpenAnimation(cardView).ToYieldInstruction());

                        resourceModel.AddCard(cardModel);
                        yield return(_commonRes.AddResourceAnimation(cardView, selectedCardControl.commonResourceIndex).ToYieldInstruction());

                        // 親を手札オブジェクトに変えたほうがよさそう
                    }
                    else if (cardTopView.Contains(selectedCardControl))
                    {
                        Debug.Log("山札がクリックされた");
                        playerModel.AddHand(selectedCardModel);
                        selectedCardControl.ShowFront();

                        yield return(_handViews[playerModel.Index].AddHandAnimation(selectedCardControl).ToYieldInstruction());

                        cardTop = deckModel.Open();
                        var cardView = mainDeck.CreateCard(cardTop.Type, cardTop.Color, cardTop.ID);
                        cardVM[cardView] = cardTop;
                        cardView.isBack  = true;
                        // 親を手札オブジェクトに変えたほうがよさそう
                        yield return(null); // オープンしたカードのViewが有効になるのを待つために1フレームずらす
                    }
                    else
                    {
                        Debug.Log("クリックされないはず");
                    }

                    action++;
                    if (action >= 2)
                    {
                        break;
                    }
                }
            }

            foreach (var place in bacteriaPlaces)
            {
                place.OutBreakInCurrRound = false;
            }

            // 腐敗フェイズ
            Debug.Log("腐敗フェイズ start");
            for (int i = 0; i < NumberOfBacterias; i++)
            {
                var token = bacteriaSrc.Draw();
                Debug.Log("Token : " + token.Color);

                // トークンが出てくるアニメーションと待ち合わせ

                var tagetPlace = bacteriaPlaces.Find(bp => bp.Color == token.Color);
                var targetBacteriaPlaceView = _bacterias.Find(bpv => bpv._color == token.Color);
                yield return(targetBacteriaPlaceView.AddBacteriaAnimation().ToYieldInstruction());


                if (!tagetPlace.OutBreakInCurrRound)
                {
                    tagetPlace.AddToken(token);

                    // トークンを配置するアニメーションと待ち合わせ

                    if (tagetPlace.OutBreak())
                    {
                        // アウトブレイクするアニメーションと待ち合わせ

                        foreach (var rp in playerModels.SelectMany(pm => pm.Ripeners))
                        {
                            if (rp.Colors.Contains(tagetPlace.Color))
                            {
                                // この熟成器はバースト
                                rp.Reset();
                            }
                        }

                        tagetPlace.OutBreakInCurrRound = true;
                        yield return(Observable.FromCoroutine(_ => RecurveOutBreak(tagetPlace)).ToYieldInstruction());
                    }
                }
            }
            Debug.Log("腐敗フェイズ end");


            // 売却フェイズ
            Debug.Log("売却フェイズ start");
            _passButton.Enabled = true;
            for (int i = 0; i < NumberOfPlayers; i++)
            {
                int index = (round + i) % NumberOfPlayers;
                while (true)
                {
                    var playerModel = playerModels[index];

                    // 熟成器選択
                    var ripenersModel = playerModel.Ripeners;

                    // このプレイヤーの熟成器に何も入っていなかったら飛ばす
                    if (ripenersModel.Where(r => !r.Empty.Value).Count() == 0)
                    {
                        Debug.Log("自動スキップ");
                        break;
                    }

                    var ripenersViews = ripenersModel.Where(r => !r.Empty.Value).Select(model => _ripenerVeiws[index].FirstOrDefault(v => v.ModelID == model.ID));

                    var ripenerSelection = Observable.Amb(ripenersViews.Select(_ => _.OnTouchAsObservabale).Concat(_passButton.OnTouchAsObservabale.AsEnumerable())).First().ToYieldInstruction();

                    yield return(ripenerSelection);

                    if (!ripenerSelection.HasResult)
                    {
                        continue;
                    }

                    if (ripenerSelection.Result is RipenerView)
                    {
                        var selectedRipener = ripenerSelection.Result as RipenerView;
                        Debug.Log("熟成機がタッチされた");

                        var selectedRipenerModel = ripenersModel.FirstOrDefault(m => m.ID == selectedRipener.ModelID);

                        var cashout = selectedRipenerModel.AgingPeriod.Value * selectedRipenerModel.Maturity;
                        playerModel.Point += cashout;
                        selectedRipenerModel.Reset();

                        yield return(selectedRipener.ResetAnimation().ToYieldInstruction());
                    }
                    else if (ripenerSelection.Result is PassButtonView)
                    {
                        Debug.Log("パスされた");
                        break;
                    }
                }
            }
            _passButton.Enabled = false;
            Debug.Log("売却フェイズ end");

            // 仕込みフェイズ
            Debug.Log("仕込みフェイズ start");
            _passButton.Enabled = true;
            for (int i = 0; i < NumberOfPlayers; i++)
            {
                int index = (round + i) % NumberOfPlayers;
                while (true)
                {
                    var playerModel = playerModels[index];
                    var handView    = _handViews[index];
                    var cardViews   = playerModel.Hand.Select(model => handView.GetCardView(model.ID));

                    if (playerModel.Hand.Count == 0)
                    {
                        Debug.Log("手札がないので仕込みはスキップ");
                        break;
                    }
                    else if (playerModel.Ripeners.Where(r => r.Empty.Value).Count() < 1)
                    {
                        Debug.Log("空いている熟成器がないので仕込みはスキップ");
                        break;
                    }

                    // カード選択
                    var cardSelection = Observable.Amb(cardViews.Select(_ => _.OnTouchAsObservabale).Concat(_passButton.OnTouchAsObservabale.AsEnumerable())).First().ToYieldInstruction();
                    yield return(cardSelection);

                    if (!cardSelection.HasResult)
                    {
                        continue;
                    }

                    if (cardSelection.Result is PassButtonView)
                    {
                        Debug.Log("パスされた");
                        break;
                    }

                    var selectedCard = cardSelection.Result as CardControl;

                    yield return(selectedCard.SelectedAnimation().ToYieldInstruction());

                    // 熟成器選択
                    var ripenersModel = playerModel.Ripeners;
                    var ripenersViews = ripenersModel.Where(r => r.Empty.Value).Select(model => _ripenerVeiws[index].FirstOrDefault(v => v.ModelID == model.ID));

                    var ripenerSelection = Observable.Amb(ripenersViews.Select(_ => _.OnTouchAsObservabale)).First().ToYieldInstruction();
                    yield return(ripenerSelection);

                    if (!ripenerSelection.HasResult)
                    {
                        continue;
                    }

                    var selectedRipener = ripenerSelection.Result as RipenerView;

                    var selectedRipenerModel = ripenersModel.FirstOrDefault(m => m.ID == selectedRipener.ModelID);
                    var selectedCardModel    = cardVM[selectedCard];
                    playerModel.RemoveHand(selectedCardModel);
                    handView.RemoveHand(selectedCard);
                    selectedRipenerModel.AddCard(selectedCardModel);
                    yield return(selectedRipener.AddCardAnimation(selectedCard).ToYieldInstruction());

                    // カード削除アニメーション
                    yield return(selectedCard.RemovedAnimation().ToYieldInstruction());

                    // カード整頓
                    yield return(_handViews[playerModel.Index].ArrangeHandAnimation().ToYieldInstruction());

                    break;
                }
            }
            _passButton.Enabled = false;
            Debug.Log("仕込みフェイズ end");

            // 熟成フェイズ
            Debug.Log("熟成フェイズ start");
            foreach (var ripener in playerModels.SelectMany(player => player.Ripeners))
            {
                if (!ripener.Empty.Value)
                {
                    ripener.AddDay();
                }
            }
            Debug.Log("熟成フェイズ end");
        }
    }