/// <summary>
        /// 퀴즈를 시작합니다.
        /// </summary>
        /// <param name="quizType">퀴즈의 유형</param>
        /// <param name="subjects">주제 목록</param>
        /// <param name="requestQuizCount">요청하는 퀴즈의 총 개수</param>
        /// <param name="minQuizCount">퀴즈 최소 개수</param>
        /// <param name="quizTimeLimit">퀴즈의 제한시간</param>
        /// <param name="bonusExperience">퀴즈 정답 시 획득 경험치</param>
        /// <param name="bonusMoney">퀴즈 정답 시 획득 머니</param>
        /// <param name="idleTimeLimit">퀴즈의 잠수 제한시간</param>
        /// <param name="showSubject">주제 표시 여부</param>
        protected void StartQuiz(Quiz.TypeOption quizType, string[] subjects, int requestQuizCount, int minQuizCount, int quizTimeLimit, int bonusExperience, int bonusMoney, int idleTimeLimit = 180, bool showSubject = true)
        {
            IsQuizTaskRunning = true;
            Thread.Sleep(1000);

            List <Quiz.Data> quizDataList = GetQuizDataList(quizType, subjects, requestQuizCount);

            if (quizDataList.Count < requestQuizCount)
            {
                requestQuizCount = quizDataList.Count;
            }
            if (requestQuizCount < minQuizCount)
            {
                Thread.Sleep(SendMessageInterval);
                OnQuizCountInvalid(minQuizCount, requestQuizCount);
                IsQuizTaskRunning = false;
                return;
            }

            string[] notices = GetQuizNoticesFromFile();
            Thread.Sleep(SendMessageInterval);
            SendMessage($"{notices[BotRandom.Next(notices.Length)]}");
            Thread.Sleep(2000);

            bool isRandom = subjects.Length > 1 ? true : false;

            OnQuizReady();
            RunQuiz(quizType, subjects, requestQuizCount, quizTimeLimit, bonusExperience, bonusMoney, idleTimeLimit, showSubject, isRandom, quizDataList);

            if (IsQuizTaskRunning)
            {
                IsQuizTaskRunning = false;
            }
            Thread.Sleep(2000);
            KakaoTalk.Message[] messages;
            if (IsMainTaskRunning)
            {
                while ((messages = Window.GetMessagesUsingClipboard()) == null)
                {
                    Thread.Sleep(GetMessageInterval);
                }
                LastMessageIndex = (messages.Length - 1) + 1;
                OnQuizFinish();
            }
        }
        /// <summary>
        /// 퀴즈 실행부입니다.
        /// </summary>
        /// <param name="quizType">퀴즈의 유형</param>
        /// <param name="subjects">주제 목록</param>
        /// <param name="requestQuizCount">요청하는 퀴즈의 총 개수</param>
        /// <param name="quizTimeLimit">퀴즈의 제한시간</param>
        /// <param name="bonusExperience">퀴즈 정답 시 획득 경험치</param>
        /// <param name="bonusMoney">퀴즈 정답 시 획득 머니</param>
        /// <param name="idleTimeLimit">퀴즈의 잠수 제한시간</param>
        /// <param name="showSubject">주제 표시 여부</param>
        /// <param name="isRandom">주제 랜덤 여부</param>
        /// <param name="quizDataList">퀴즈 데이터 목록</param>
        protected void RunQuiz(Quiz.TypeOption quizType, string[] subjects, int requestQuizCount, int quizTimeLimit, int bonusExperience, int bonusMoney, int idleTimeLimit, bool showSubject, bool isRandom, List <Quiz.Data> quizDataList)
        {
            KakaoTalk.Message[]   messages;
            KakaoTalk.MessageType messageType;
            string   userName;
            string   content;
            DateTime sendTime;
            QuizUser user;
            int      lastInputTick = Environment.TickCount;

            for (int i = 0; i < requestQuizCount; i++) // == quizDataList.Count
            {
                int currentQuiz = i + 1;

                var    quizData = quizDataList[i];
                string subject  = quizData.MainSubject;
                if (quizData.ChildSubject != null)
                {
                    subject += $"-{quizData.ChildSubject}";
                }
                string question        = quizData.Question;
                string answer          = quizData.Answer;
                string explanation     = quizData.Explanation;
                string beforeImagePath = quizData.BeforeImagePath;
                string afterImagePath  = quizData.AfterImagePath;
                bool   isCaseSensitive = quizData.IsCaseSensitive;

                while ((messages = Window.GetMessagesUsingClipboard()) == null)
                {
                    Thread.Sleep(GetMessageInterval);
                }
                LastMessageIndex = (messages.Length - 1) + 1; // 뒤에 바로 SendMessage를 하므로, +1 해서 초기화
                OnQuizQuestionSend(isRandom, showSubject, subject, currentQuiz, requestQuizCount, question);

                if (beforeImagePath != null)
                {
                    OnQuizBeforeImageSend(beforeImagePath);
                }

                if (quizData.Choices != null)
                {
                    OnQuizChoicesSend(quizData.Choices);
                }

                int  beginTick      = Environment.TickCount;
                bool shouldContinue = true;
                while (IsQuizTaskRunning && shouldContinue)
                {
                    Thread.Sleep(QuizScanInterval);
                    if (Environment.TickCount > lastInputTick + (idleTimeLimit * 1000))
                    {
                        IsQuizTaskRunning = false;
                        OnQuizIdleLimitExceed();
                        break;
                    }
                    else if (Environment.TickCount > beginTick + (quizTimeLimit * 1000)) // 시간 제한 초과
                    {
                        OnQuizTimeLimitExceed(answer);
                        shouldContinue = false;
                    }
                    else
                    {
                        while ((messages = Window.GetMessagesUsingClipboard()) == null)
                        {
                            Thread.Sleep(GetMessageInterval);
                        }
                        sendTime = DateTime.Now;
                        for (int j = LastMessageIndex; j < messages.Length; j++)
                        {
                            messageType = messages[j].Type;
                            userName    = messages[j].UserName;
                            content     = messages[j].Content;

                            LastMessageIndex++;
                            user = FindUserByNickname(userName);
                            if (user == null)
                            {
                                AddNewUser(userName);
                                user = FindUserByNickname(userName);
                            }

                            if (!isCaseSensitive)
                            {
                                content = content.ToLower(); answer = answer.ToLower();
                            }

                            if (IsQuizTaskRunning) // 다른 곳에서 StopQuiz 요청에 의해 IsQuizTaskRunning은 false가 될 수 있음. 따라서 검사 시마다 확인.
                            {
                                if (messageType == KakaoTalk.MessageType.Unknown)
                                {
                                    continue;
                                }
                                else if (messageType == KakaoTalk.MessageType.DateChange)
                                {
                                    SendDateChangeNotice(content, sendTime);
                                }
                                else if (messageType == KakaoTalk.MessageType.UserJoin)
                                {
                                    SendUserJoinNotice(userName, sendTime);
                                }
                                else if (messageType == KakaoTalk.MessageType.UserLeave)
                                {
                                    SendUserLeaveNotice(userName, sendTime);
                                }
                                else if (messageType == KakaoTalk.MessageType.Talk)
                                {
                                    if (content == answer)
                                    {
                                        lastInputTick = Environment.TickCount;
                                        OnQuizAnswerCorrect(answer, user, bonusExperience, bonusMoney);
                                        shouldContinue = false;
                                        break;
                                    }
                                    else
                                    {
                                        ProcessUserMessage(userName, content, sendTime);
                                    }
                                }
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    if (IsQuizTaskRunning && !shouldContinue)
                    {
                        if (afterImagePath != null)
                        {
                            OnQuizAfterImageSend(afterImagePath);
                        }
                        if (explanation != null)
                        {
                            OnQuizExplanationSend(explanation);
                        }
                        else
                        {
                            Thread.Sleep(1500);
                        }
                    }
                }
                if (!IsQuizTaskRunning)
                {
                    break;
                }
                else if (i == requestQuizCount - 1)
                {
                    OnQuizAllCompleted();
                    IsQuizTaskRunning = false;
                }
            }
        }
        /// <summary>
        /// 퀴즈 데이터 목록을 얻어옵니다.
        /// </summary>
        /// <param name="requestedQuizType">요청된 퀴즈 유형</param>
        /// <param name="subjects">주제 목록</param>
        /// <param name="quizCount">퀴즈 개수</param>
        /// <returns>퀴즈 데이터 목록</returns>
        protected List <Quiz.Data> GetQuizDataList(Quiz.TypeOption requestedQuizType, string[] subjects, int quizCount)
        {
            var data2dList = new List <List <Quiz.Data> >();
            List <Quiz.Data> tempList;
            bool             matchesChildSubject;

            /* resultDataList 초기화 */
            for (int i = 0; i < subjects.Length; i++)
            {
                foreach (Quiz quiz in QuizList)
                {
                    if (quiz.Type == requestedQuizType)
                    {
                        string mainSubject = quiz.MainSubject;
                        if (mainSubject == subjects[i])
                        {
                            tempList = new List <Quiz.Data>();
                            for (int j = 0; j < quiz.DataList.Count; j++)
                            {
                                var data = quiz.DataList[j];
                                if (quiz.UseMultiChoice == true)
                                {
                                    data.Choices = GetChoiceList(quiz, data);
                                }
                                tempList.Add(data);
                            }
                            data2dList.Add(tempList);
                            break;
                        }
                        else
                        {
                            matchesChildSubject = false;
                            foreach (string childSubject in quiz.ChildSubjects)
                            {
                                if ($"{mainSubject}-{childSubject}" == subjects[i])
                                {
                                    tempList = new List <Quiz.Data>();
                                    for (int j = 0; j < quiz.DataList.Count; j++)
                                    {
                                        var data = quiz.DataList[j];
                                        if (data.ChildSubject == childSubject)
                                        {
                                            if (quiz.UseMultiChoice == true)
                                            {
                                                data.Choices = GetChoiceList(quiz, data);
                                            }
                                            tempList.Add(data);
                                        }
                                    }
                                    data2dList.Add(tempList);
                                    matchesChildSubject = true;
                                }
                            }
                            if (matchesChildSubject)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            /* data2dList Shuffle */
            int shuffleCount = 3;

            for (int i = 0; i < data2dList.Count; i++)
            {
                for (int j = 0; j < shuffleCount; j++)
                {
                    ListUtil.Shuffle(data2dList[i]);
                }
            }

            /* 배열 리스트, 문항 수 초기화 */
            List <Quiz.Data[]> dataArrList = new List <Quiz.Data[]>();
            int totalCount = 0;

            for (int i = 0; i < data2dList.Count; i++)
            {
                dataArrList.Add(new Quiz.Data[data2dList[i].Count]);
                totalCount += data2dList[i].Count;
            }
            if (totalCount > quizCount)
            {
                totalCount = quizCount;
            }

            /* 문제 선택 및 결과 리스트로 이동 */
            var resultDataList = new List <Quiz.Data>();

            for (int i = 0; i < totalCount; i++)
            {
                int randomValue = BotRandom.Next(dataArrList.Count);
                for (int j = 0; j < dataArrList[randomValue].Length; j++)
                {
                    if (dataArrList[randomValue][j] == null)
                    {
                        dataArrList[randomValue][j] = data2dList[randomValue][j];
                        resultDataList.Add(dataArrList[randomValue][j]);
                        break;
                    }
                    else if (j == dataArrList[randomValue].Length - 1)
                    {
                        i--;
                    }
                }
            }

            /* 결과 리스트 shuffle (리스트 내에서 다시 셔플) */
            for (int i = 0; i < shuffleCount; i++)
            {
                ListUtil.Shuffle(resultDataList);
            }

            return(resultDataList);
        }