using System;
using System.Linq;
using BingoBrain.Core;
using UnityEngine;
using BingoBrain.HotFix;
using System.Collections.Generic;

namespace BingoBrain
{
    public class CallSystem : BaseSystem
    {
        private bool isDeBug = false;

        private System.Random random;

        public CallSystem(bool isAutoInit = true)
        {
            if (isAutoInit)
            {
                Init();
            }
        }

        public override void Init()
        {
            base.Init();
            validNumPool = new List<int>();
            invalidNumPool = new List<int>();
            AddListener();
        }

        private void AddListener()
        {
            GameDispatcher.Instance.AddListener(BingoInfo.StartCallNum, StartCall);
            GameDispatcher.Instance.AddListener(BingoInfo.EndBingoGame, EndCallNumber);
            GameDispatcher.Instance.AddListener(BingoInfo.ShowGameOver, EndCallNumber);
            GameDispatcher.Instance.AddListener(BingoInfo.StartBingoGame, OnStartBingoGame);
        }

        private void OnStartBingoGame(object obj)
        {
            BingoCell.AddCallSum = 0;
            BingoCell.calledList.Clear();
            BingoCell.MaxAddCallSum = GameHelper.GetCommonModel().MaxAddCallSum;

            callIntervalTime = GameHelper.GetCommonModel().NumCD[0];
            updataTime = callIntervalTime;
            BingoCell.MaxCallSum = GameHelper.GetCommonModel().NumCD[1];
            callSum = 0;
        }

        #region 叫号

        #region updata

        private bool isStartCall;
        private float updataTime;

        public override void Update()
        {
            if (!isStartCall)
            {
                return;
            }


            if (BingoCell.IsPauseGame)
            {
                return;
            }


            if (UI.Instance.HasUIInLayer(UILayerType.Popup))
            {
                return;
            }

            updataTime += Time.deltaTime * BingoCell.CallSpeed;
            if (updataTime >= callIntervalTime)
            {
                updataTime = 0;
                if (callSum >= BingoCell.MaxCallSum && !BingoCell.isPropRewarding && !BingoCell.isVictory)
                {
                    GameDispatcher.Instance.Dispatch(BingoInfo.CallNumFinish);
                    return;
                }

                if (BingoCell.isVictory)
                {
                    return;
                }

                CallNumber();
            }
        }

        #endregion


        private int callIntervalTime;


        private int callSum;


        private List<int> validNumPool;


        private List<int> invalidNumPool;


        private void SetNumPool()
        {
            invalidNumPool.Clear();
            validNumPool.Clear();
            foreach (var num in BingoCell.cardBoardList.SelectMany(item =>
                         item.data.cardNumList.Where(num => !validNumPool.Contains(num))))
            {
                validNumPool.Add(num);
            }

            for (var i = 1; i <= 75; i++)
            {
                if (!validNumPool.Contains(i))
                {
                    invalidNumPool.Add(i);
                }
            }
        }


        private void StartCall(object o)
        {
            random = isDeBug
                ? new System.Random(123456789)
                : new System.Random((int)DateTimeBoardk.Instance.GetServerCurrTimestamp());

            SetNumPool();

            if (BingoCell.IsInGame)
            {
                isStartCall = true;
            }
        }


        private void CallNumber()
        {
            if (callSum >= BingoCell.MaxCallSum)
            {
                return;
            }

            callSum++;
            var num = GetCallNumber();
            if (BingoCell.calledList.Contains(num))
            {
            }

            BingoCell.calledList.Add(num);
            Audio.Instance.PlayDynamicEffect("Call." + num);
            GameDispatcher.Instance.Dispatch(BingoInfo.CallNum, num);
        }


        private void EndCallNumber(object o)
        {
            if (isStartCall)
            {
                validNumPool.Clear();
                invalidNumPool.Clear();
                isStartCall = false;
            }
        }


        public int GetDaubCountInBingoList(List<int> bingoList, CardBoardEntity cardBoardEntity)
        {
            return (from index in bingoList
                let cardEntity = cardBoardEntity.cardList[index]
                where cardBoardEntity.data.selectList.Contains(index) ||
                      BingoCell.calledList.Contains(cardEntity.data.num)
                select index).Count();
        }


        public int GetCallNumber()
        {
            if (BingoCell.isFirstGame)
            {
                if (BingoCell.FixedCallIndexList.Count != 0)
                {
                    var index = BingoCell.FixedCallIndexList[0];
                    var num = BingoCell.cardBoardList[0].data.cardNumList[index];
                    BingoCell.FixedCallIndexList.RemoveAt(0);
                    return num;
                }
            }

            var vo = GameHelper.GetConfig<BingoProbModel>().GetData(PreferencesMgr.Instance.CardBoardIndex + 1);
            var daubCount = BingoCell.cardBoardList.Sum(cardBoard => cardBoard.data.selectList.Count);

            float chance = vo.probability;
            var isNeedAdvance = false;
            for (var i = 0; i < vo.daubNum.Length; i++)
            {
                if (daubCount >= vo.daubNum[i])
                {
                    chance = vo.bingoprob[i];
                    isNeedAdvance = true;
                }
            }

            var isSucceed = GlobalHarmony.IsChance(chance / 100f);

            if (isNeedAdvance)
            {
                if (isSucceed)
                {
                    var maxMatchCount = 1;
                    var bingoIndexAndCardBoardList = new List<Tuple<CardBoardEntity, int>>();
                    foreach (var bingoKv in BingoCell.bingoDic)
                    {
                        foreach (var cardBoard in BingoCell.cardBoardList)
                        {
                            if (cardBoard.isFinish)
                            {
                                continue;
                            }

                            var count = GetDaubCountInBingoList(bingoKv.Value, cardBoard);
                            if (count == 5)
                            {
                                continue;
                            }

                            if (count > maxMatchCount)
                            {
                                maxMatchCount = count;
                                bingoIndexAndCardBoardList.Clear();
                                bingoIndexAndCardBoardList.Add(new Tuple<CardBoardEntity, int>(cardBoard, bingoKv.Key));
                            }
                            else if (count == maxMatchCount)
                            {
                                bingoIndexAndCardBoardList.Add(new Tuple<CardBoardEntity, int>(cardBoard, bingoKv.Key));
                            }
                        }
                    }

                    var finalCardNum = GlobalHarmony.GetRandomList(bingoIndexAndCardBoardList);
                    if (finalCardNum != null)
                    {
                        foreach (var num in from index in BingoCell.bingoDic[finalCardNum.Item2]
                                 let num = finalCardNum.Item1.data.cardNumList[index]
                                 where !finalCardNum.Item1.data.selectList.Contains(index) &&
                                       !BingoCell.calledList.Contains(num)
                                 select num)
                        {
                            return num;
                        }
                    }
                    else
                    {
                    }
                }
            }


            var sum = 0;
            List<int> list;

            if (isSucceed)
            {
                if (validNumPool.Count > 0)
                {
                    list = validNumPool;
                }
                else
                {
                    list = invalidNumPool;
                }
            }
            else
            {
                list = invalidNumPool.Count > 0 ? invalidNumPool : validNumPool;
            }


            if (list.Count > 0)
            {
                var index = random.Next(0, list.Count);
                sum = list[index];
                list.RemoveAt(index);
            }

            if (sum == 0)
            {
                while (true)
                {
                    sum = random.Next(0, 76);
                    if (!BingoCell.calledList.Contains(sum))
                    {
                        break;
                    }

                    if (BingoCell.calledList.Count >= 75)
                    {
                        break;
                    }
                }
            }

            return sum;
        }

        #endregion
    }
}