using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// 由BaseQuestion拆分而成,如果一个Base中只有一个填充项,则会被拆分成一个Question。
    /// BaseQuestion的Body(题干)中有几个填空项,就应该有几个答案列表。
    public class Question
    {
        private List<ChoiceItem> choiceItems = new List<ChoiceItem>();
        /// <summary>
        /// 选择支列表。
        /// </summary>
        public List<ChoiceItem> ChoiceItems
        {
            get { return this.choiceItems; }
        }

        private string answer = string.Empty;
        /// <summary>
        /// [只读]仅适用于选择题(包括判断题)、填空题。不适用于主观题。
        /// </summary>
        public string Answer
        {
            get
            {
                if (choiceItems.Count <= 0)
                {
                    if (string.IsNullOrEmpty(this.titleString)) return string.Empty;

                    //填空题
                    int startBracket = this.titleString.IndexOf("【");
                    int closeBracket = this.titleString.IndexOf("】");

                    if (startBracket < 0 || closeBracket < 0 || closeBracket < startBracket) return string.Empty;

                    return this.titleString.Substring(startBracket, closeBracket - startBracket);
                }

                return this.answer;
            }
        }

        /// <summary>
        /// [构造方法]用于生成一道只有题干的试题。
        /// </summary>
        /// <param name="titleString">试题的题干文本。</param>
        public Question(string titleString)
        {
            this.TitleString = titleString;
        }

        /// <summary>
        /// [构造方法]生成一道试题。适用于填空题(无答案)、选择题(有答案项、错项,)、判断题(特殊的选择题)。
        /// </summary>
        /// <param name="titleString">应传入由BaseQuestion.BuildQuestionTitles()方法生成的String[]的某个成员。
        /// 它的形式是一行带一个填空的文本。</param>
        /// <param name="bodyString">应传入每个试题的原文本的“答案”部分,
        /// 每个答案文本片段中应包含一个“答案”及一个或多个“错项”。</param>
        public Question(string titleString, string bodyString)
        {
            this.TitleString = titleString;

            if (string.IsNullOrEmpty(bodyString)) return;//填空题,有填充项而无答案项。

            // 根据bodyStrings生成BaseQuestionItems
            String[] itemStrings = bodyString.Split(new string[1] { "错项>>" }, StringSplitOptions.None);
            // 第一个片段是答案,其余每个片段都是“错项”。

            if (itemStrings.Length > 1)
            {
                ChoiceItem answerChoiceItem = new ChoiceItem();
                answerChoiceItem.IsAnswer = true;

                int indexOfAnalysis = itemStrings[0].IndexOf("解析>>");
                if (indexOfAnalysis > -1)
                {
                    answerChoiceItem.Text = itemStrings[0].Substring(0, indexOfAnalysis);
                    answerChoiceItem.AnalysisText = L.FormatChoiceQuestionTextString(itemStrings[0].Substring(indexOfAnalysis + 4));
                }
                else
                {
                    answerChoiceItem.Text = itemStrings[0];
                }

                this.choiceItems.Add(answerChoiceItem);
                this.answer = answerChoiceItem.Text;

                for (int ii = 1; ii < itemStrings.Length; ii++)
                {
                    ChoiceItem otherChoiceItem = new ChoiceItem();

                    indexOfAnalysis = -1;
                    // 索引从1开始。
                    indexOfAnalysis = itemStrings[ii].IndexOf("解析>>");
                    if (indexOfAnalysis > -1)
                    {
                        otherChoiceItem.Text = L.FormatChoiceQuestionTextString(itemStrings[ii].Substring(0, indexOfAnalysis));

                        otherChoiceItem.AnalysisText = L.FormatChoiceQuestionTextString(itemStrings[ii].Substring(indexOfAnalysis + 4));
                    }
                    else
                    {
                        otherChoiceItem.Text = L.FormatChoiceQuestionTextString(itemStrings[ii]);
                    }

                    this.choiceItems.Add(otherChoiceItem);
                }
            }

            // 使用随机数混淆顺序。
            int[] random = L.GetRandomIntArray(choiceItems.Count);
            for (int i = 0; i < choiceItems.Count; i++)
            {
                choiceItems[i].OrderNumber = random[i];
            }
            choiceItems.Sort(new CmparsionQuestionItem());
        }

        /// <summary>
        /// [构造方法]适用于
        /// </summary>
        /// <param name="titleString"></param>
        /// <param name="titleAndAnswers"></param>
        public Question(string titleString, string[] titleAndAnswers)
        {
            this.titleString = titleString;

            this.answers.Clear();
            for (int i = 1; i < titleAndAnswers.Length; i++)
            {
                this.answers.Add(titleAndAnswers[i]);
            }
        }

        /// <summary>
        /// 试题类型:判断、选择、主观。
        /// </summary>
        public QuestionType Type
        {
            get
            {
                if (this.choiceItems.Count <= 0)
                {
                    if (this.answers.Count > 0) return QuestionType.Subjective;

                    if (this.titleString.Contains("【") == false || this.titleString.Contains("】") == false) return QuestionType.Text;//没有任何填空项,只呈现纯文本。

                    return QuestionType.FillBlank;
                }
                else
                {
                    //判断题是只有两个选项的选择题,且两个选项必须是:一个“正确”、一个“错误”。
                    if (this.choiceItems.Count == 2)
                    {
                        if ((this.choiceItems[0].Text == "正确" && this.choiceItems[1].Text == "错误") ||
                            (this.choiceItems[0].Text == "错误" && this.choiceItems[1].Text == "正确"))
                            return QuestionType.Judge;
                    }
                    return QuestionType.Choice;
                }
            }
        }

        /// <summary>
        /// 判断文本行是否属于与试题相关的行。
        /// </summary>
        /// <param name="lineText">源文本。</param>
        /// <returns></returns>
        public static bool IsExamTextLine(string lineText)
        {
            return (lineText.StartsWith("  试题>>") || lineText.StartsWith("<<材料>>") || lineText.StartsWith("<<出处>>") ||
                    lineText.StartsWith("<<问题>>") || lineText.StartsWith("  答案>>") || lineText.StartsWith("  解析>>") ||
                    lineText.StartsWith("  错项>>"));
        }

        /// <summary>
        /// 将试题文本编译为Html标签文本。(编译试题)
        /// </summary>
        /// <param name="questionText">试题内容文本。</param>
        public static string ConvertQuestionsToHtml(string questionText)
        {
            if (string.IsNullOrEmpty(questionText)) return "";

            //考虑到Markdown与TestPaper的混杂难以处理,应先将试题处理为html,期间还需要考虑图像引用问题。
            //注意:所有在试题影响范围内的文本均算是试题的一部分,这样试题就能支持多行文本了。

            //填空题比较复杂:
            //    ⑴如果填空题后面找不到“〓〓〓〓〓〓”结尾,则填空题只有一行文本。
            //    ⑵如果填空题后能找到试题结束标记“〓〓〓〓〓〓”,则到这个标记的所有文本均算是这个试题的内容。

            if (questionText.StartsWith("  试题>>"))
            {
                questionText = "\n" + questionText;
            }

            var startIndex = questionText.IndexOf("\n  试题>>");//必须是在文本行开头才能算!

            if (startIndex < 0) return questionText;//没有试题,原样输出。

            StringBuilder resultBuilder = new StringBuilder();
            List<Question> questions = new List<Question>();

            string temp = questionText.Replace("\n  试题>>", "\n  <<&LINE&>>试题>>");
            var sections = temp.Split(new string[] { "<<&LINE&>>" }, StringSplitOptions.None);

            int startNumber = 1;

            foreach (var s in sections)
            {
                if (s.StartsWith("试题>>"))
                {
                    //这个字段中包含试题文本,需要特殊处理

                    //截取出试题结束符后的文本,这部分不是试题的组成部分,直接附加即可。
                    string header = s;
                    string tail = "";
                    var tailIndex = s.IndexOf("〓〓〓〓〓〓");
                    if (tailIndex >= 0)
                    {
                        tail = s.Substring(tailIndex + 6);
                        header = s.Substring(0, tailIndex);
                    }

                    header = ConvertQuestionsToHtml(header, ref startNumber);

                    resultBuilder.Append("\n\n");
                    resultBuilder.Append(header);
                    resultBuilder.Append("\n\n");
                    resultBuilder.Append(tail);
                }
                else
                {
                    //不需要特殊处理。
                    //Markdown中,空行、空格都是有用处的,不能随意去除。
                    //resultBuilder.Append(FormatText(s));
                    resultBuilder.Append(s);
                }
            }

            //Markdown中,空行、空格都是有用处的,不能随意去除。
            //return FormatText(resultBuilder.ToString());
            return resultBuilder.ToString();
        }

        /// <summary>
        /// 文本格式化,去除每行首尾的空白字符,统一换行符为“\n”。
        /// </summary>
        /// <param name="sourceText">源文本。</param>
        private static string FormatText(string sourceText)
        {
            if (string.IsNullOrEmpty(sourceText)) return "";

            StringBuilder sb = new StringBuilder();
            var lines = sourceText.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var line in lines)
            {
                string s = line.Trim(new char[] { '\t', ' ', ' ' });
                sb.Append(s + (s.Length <= 0 ? "" : "\n"));
            }

            var result = sb.ToString();
            if (result.EndsWith("\n"))
            {
                return result.Substring(0, result.Length - 1);
            }
            else return result;
        }

        /// <summary>
        /// 将试题文本转换为Html。(试题编译)
        /// </summary>
        /// <param name="questionText">试题文本。</param>
        /// <param name="startNumber">试题序号。</param>
        private static string ConvertQuestionsToHtml(string questionText, ref int startNumber)
        {
            if (string.IsNullOrEmpty(questionText)) return "";

            List<Question> newQuestions = BaseQuestion.BuildQuestionList(questionText);
            if (newQuestions == null || newQuestions.Count <= 0) return "";

            StringBuilder sb = new StringBuilder();
            StringBuilder endScript = new StringBuilder();

            var hideText = "";
            if (Globals.MainWindow.HideExamAnswer)
            {
                hideText = " style = 'display: none;'";
            }

            for (int i = 0; i < newQuestions.Count; i++)
            {
                var q = newQuestions[i];
                switch (q.Type)
                {
                    case QuestionType.Judge://判断题是特殊形式的选择题。
                    case QuestionType.Choice:
                        {
                            var indexOfStart = q.TitleString.IndexOf("【");
                            var indexOfEnd = q.TitleString.IndexOf("】");
                            if (indexOfStart < 0 || indexOfEnd < 0 || indexOfEnd <= indexOfStart) continue;//选择题无填充项,则无法确定答案。

                            //var fillblank = "";
                            //fillblank = q.TitleString.Substring(indexOfStart + 1, indexOfEnd - indexOfStart - 2);

                            //var displayTitle = q.TitleString.Replace("【", "<code class='exam_fb'>").Replace("】", "</code>");
                            var displayTitle = q.TitleString.Substring(0, indexOfStart + 1) + "  " + q.TitleString.Substring(indexOfEnd);

                            sb.Append("<div class='ex_c'>");
                            sb.Append("<p class='ex_c_t'>" + startNumber.ToString() + "." + FormatText(displayTitle) + "</p>");

                            //嵌入标题中的图像文件链接
                            if (string.IsNullOrWhiteSpace(q.MarkdownImagePath) == false)
                            {
                                sb.Append($"<p><img src=\"{q.MarkdownImagePath}\" alt=\"{q.MarkdownImageTooltip}\"/></p>");
                            }

                            startNumber++;

                            sb.Append("<ol class='ex_c_xx' type='A'>");

                            //TODO:还缺混淆:q.ChoiceItems.Sort();

                            endScript.Append("<script>");

                            for (int m = 0; m < q.ChoiceItems.Count; m++)
                            {
                                var item = q.ChoiceItems[m];
                                sb.Append($"<li id='li_{startNumber}_{m}'>" + FormatText(item.Text) + "</li>");
                                sb.Append($"<div class='ex_c_jx' id='jx_{startNumber}_{m}'{hideText}><p>" +
                                    (item.IsAnswer ? "<span class='ex_c_right'>答 案</span>" : "<span class='ex_c_err'>非答案</span>") +
                                    FormatText(item.AnalysisText) + "</p></div>");

                                endScript.Append(
                                        "$(document).ready(function() {" +
                                            $@"$('#li\_{startNumber}\_{m}').click(function() {{" +
                                                $@"$('#jx\_{startNumber}\_{m}').toggle();" +
                                            "});" +
                                        "});"
                                    );
                            }
                            sb.Append("</ol>");
                            sb.Append("</div>");

                            endScript.Append("</script>");
                            break;
                        }
                    case QuestionType.FillBlank:
                        {
                            var titleSpans = q.TitleString.Replace("【", "[[^]]【").Split(new string[] { "[[^]]", "】" }, StringSplitOptions.None);
                            var title = new StringBuilder();
                            var fillblanks = new StringBuilder();

                            int num = 0;
                            foreach (var s in titleSpans)
                            {
                                if (s.StartsWith("【"))
                                {
                                    num++;
                                    title.Append("【<u> [" + num + "] </u>】");

                                    fillblanks.Append("[" + num + "]" + s.Substring(1));
                                    fillblanks.Append(" ");
                                }
                                else
                                {
                                    title.Append(s);
                                }
                            }

                            var fillBlanksText = fillblanks.ToString();
                            if (fillBlanksText.EndsWith(" "))
                            {
                                fillBlanksText = fillBlanksText.Substring(0, fillBlanksText.Length - 1);
                            }


                            sb.Append("<div class='ex_f'>");
                            sb.Append("<p class='ex_f_t'>" + startNumber.ToString() + "." + FormatText(title.ToString()) + "</p>");

                            //嵌入标题中的图像文件链接
                            if (string.IsNullOrWhiteSpace(q.MarkdownImagePath) == false)
                            {
                                sb.Append($"<p><img src=\"{q.MarkdownImagePath}\" alt=\"{q.MarkdownImageTooltip}\"/></p>");
                            }

                            sb.Append($"<p class='ex_f_da'><span class='ex_f_da' id='span_ah_{startNumber}'>答案</span><span id='span_at_{startNumber}'{hideText}>" + fillBlanksText + "</span></p>");

                            endScript.Append("<script>");
                            endScript.Append(
                                        "$(document).ready(function() {" +
                                            $@"$('#span\_ah\_{startNumber}').click(function() {{" +
                                                $@"$('#span\_at\_{startNumber}').toggle();" +
                                            "});" +
                                        "});"
                                    );
                            endScript.Append("</script>");

                            startNumber++;

                            sb.Append("</div>");
                            break;
                        }
                    case QuestionType.Subjective:
                        {
                            sb.Append("<div class='ex_m'>");

                            if (q.Materials != null && q.Materials.Count > 0)
                            {
                                sb.Append("<p class='ex_m_dy'>" + startNumber.ToString() + ".阅读下列材料:</p>");
                            }
                            else
                            {
                                sb.Append("<p class='ex_m_dy'>" + startNumber.ToString() + ".</p>");
                            }

                            startNumber++;

                            sb.Append("<div class='ex_m_cl'>\n");

                            foreach (var s in q.Materials)
                            {
                                int indexOfcc = s.IndexOf("<<出处>>");
                                if (indexOfcc >= 0)
                                {
                                    sb.Append("<p class='ex_m_cl'>" + FormatText(s.Substring(0, indexOfcc)) + "</p>");

                                    string clcc = s.Substring(indexOfcc + 6);
                                    if (clcc.StartsWith("——") == false)
                                    {
                                        clcc = "——" + clcc;
                                    }

                                    sb.Append("<p class='ex_m_cc'>" + FormatText(clcc) + "</p>");
                                }
                                else
                                {
                                    sb.Append("<p class='ex_m_cl'>" + FormatText(s) + "</p>");
                                }
                            }
                            sb.Append("</div>");//<div class='ex_m_cl'>,材料块。

                            sb.Append("<p>请回答:</p>");

                            //问题部分
                            for (int j = 0; j < q.AsksList.Count; j++)
                            {
                                var s = q.AsksList[j];
                                sb.Append("<p class='ex_m_q'>(" + (j + 1).ToString() + ")" + FormatText(s) + "<p>");
                            }

                            sb.Append($"<p class='ex_m_dy'><span class='ex_m_answer' id='ex_m_answer_{startNumber}'>参考答案</span></p>");

                            //下面是答案部分。

                            for (int j = 0; j < q.Answers.Count; j++)
                            {
                                var s = q.Answers[j];
                                int indexOfjx = s.IndexOf("解析>>");

                                endScript.Append("<script>");
                                if (indexOfjx >= 0)
                                {
                                    //注意:Markdown编译器在处理带下划线的字符串时遵循如下规则:
                                    //      如果该下划线是在某个Html Tag的内部,则原样输出;
                                    //      如果该下划线不是某个Html Tag的内部(例如在Js代码中),要转义才能输出,不转义会被解释为倾斜效果。

                                    sb.Append($"<p class='ex_m_da'{hideText} id='ex_m_da_{startNumber}_{j}'>(" + (j + 1).ToString() + ")" + FormatText(s.Substring(0, indexOfjx)) + "</p>");

                                    sb.Append($"<p class='ex_m_jx'{hideText} id='ex_m_jx_{startNumber}_{j}'><span class='ex_m_analysis'>  解析</span>" + FormatText(s.Substring(indexOfjx + 6)) + "</p>");

                                    endScript.Append(
                                            "$(document).ready(function() {" +
                                                $@"$('#ex\_m\_da\_{startNumber}\_{j}').click(function() {{" +
                                                    $@"$('#ex\_m\_da\_{startNumber}\_{j}').toggle();" +
                                                "});" +
                                            "});"
                                        );//点击隐藏自身
                                    endScript.Append(
                                               "$(document).ready(function() {" +
                                                   $@"$('#ex\_m\_jx\_{startNumber}\_{j}').click(function() {{" +
                                                       $@"$('#ex\_m\_jx\_{startNumber}\_{j}').toggle();" +
                                                   "});" +
                                               "});"
                                           );//点击隐藏自身
                                    endScript.Append(
                                            "$(document).ready(function() {" +
                                                $@"$('#ex\_m\_answer\_{startNumber}').click(function() {{" +
                                                    $@"$('#ex\_m\_da\_{startNumber}\_{j}').toggle();" +
                                                    $@"$('#ex\_m\_jx\_{startNumber}\_{j}').toggle();" +
                                                "});" +
                                            "});"
                                        );//点击显示或隐藏下属的答案、解析
                                }
                                else
                                {
                                    sb.Append($@"<p class='ex_m_da'{hideText} id='ex_m_da_{startNumber}_{j}'>(" + (j + 1).ToString() + ")" + FormatText(s) + "</p>");

                                    endScript.Append(
                                            "$(document).ready(function() {" +
                                                $@"$('#ex\_m\_da\_{startNumber}\_{j}').click(function() {{" +
                                                    $@"$('#ex\_m\_da\_{startNumber}\_{j}').toggle();" +
                                                "});" +
                                            "});"
                                           );//点击隐藏自身
                                    endScript.Append(
                                            "$(document).ready(function() {" +
                                                $@"$('#ex\_m\_answer\_{startNumber}').click(function() {{" +
                                                    $@"$('#ex\_m\_da\_{startNumber}\_{j}').toggle();" +
                                                "});" +
                                            "});"
                                        );//点击显示或隐藏下属的答案、解析
                                }

                                endScript.Append("</script>");
                            }

                            sb.Append("</div>");
                            break;
                        }
                    case QuestionType.Text:
                    default:
                        {
                            return questionText;
                        }
                }
            }

            var end = endScript.ToString();
            sb.Append(endScript);

            return sb.ToString();
        }

        /// <summary>
        /// 此方法用于从 Markdown 格式的图像链接文本中取出链接到的图像的路径。
        /// 试题题干中可以嵌入 Markdown 格式的图像链接。这样试题就可以支持图像了。
        /// </summary>
        /// <param name="imageLinkText">Markdown 格式的图像链接文本。</param>
        /// <returns>去除其它信息,只保留图像地址。</returns>
        public static string GetMarkdownIamgePath(string imageLinkText)
        {
            if (string.IsNullOrWhiteSpace(imageLinkText)) return null;

            //Regex regex = new Regex("(?<=\\!\\[.*\\]\\().*(?=[ \"\\)])");

            int leftBracketIndex;
            int rightBracketIndex;

            leftBracketIndex = imageLinkText.IndexOf("](");

            if (leftBracketIndex < 0) return null;

            rightBracketIndex = imageLinkText.IndexOf(" \"", leftBracketIndex);

            if (rightBracketIndex < 0)
                rightBracketIndex = imageLinkText.IndexOf(")", leftBracketIndex);

            if (rightBracketIndex < 0) return null;
            if (rightBracketIndex <= leftBracketIndex) return null;

            return imageLinkText.Substring(leftBracketIndex, rightBracketIndex - leftBracketIndex).Trim(new char[] { ' ', ' ' });
        }

        /// <summary>
        /// 取 Markdown 图像链接文本。用在题干文本中提取图像链接。
        /// 格式如下: 
        ///     ![图像标题](链接 "提示文本")
        /// </summary>
        /// <param name="text">题干文本。</param>
        /// <param name="header">传出 Markdown 图像链接中的图像标题。</param>
        /// <param name="path">传出 Markdown 图像链接中的链接。</param>
        /// <param name="tooltip">传出 Markdown 图像链接中的提示部分。</param>
        /// <returns>顺利完成时返回 string.Empty。</returns>
        public static string GetMarkdownImageLinkText(string text, out string header, out string path, out string tooltip)
        {
            if (string.IsNullOrWhiteSpace(text))
            {
                header = string.Empty;
                path = string.Empty;
                tooltip = string.Empty;
                return string.Empty;
            }

            var start = text.IndexOf("![");
            if (start < 0)
            {
                header = string.Empty;
                tooltip = string.Empty;
                path = string.Empty;
                return string.Empty;
            }

            var end = text.IndexOf(")", start);
            if (end < 0)
            {
                header = string.Empty;
                tooltip = string.Empty;
                path = string.Empty;
                return string.Empty;
            }

            var result = text.Substring(start, end - start) + ")";
            var index = result.IndexOf("](");
            if (index < 0)
            {
                header = string.Empty;
                tooltip = string.Empty;
                path = string.Empty;
                return string.Empty;
            }

            header = result.Substring(2, index - 2);
            var newEnd = result.IndexOf(" \"", index);
            if (newEnd < 0)
            {
                newEnd = result.Length;
                path = result.Substring(index + 2, newEnd - index - 3).Trim(new char[] { ' ', ' ', '\t' });//最后是个")"。
                tooltip = string.Empty;
            }
            else
            {
                path = result.Substring(index + 2, newEnd - index - 2).Trim(new char[] { ' ', ' ', '\t' });//最后是个")"。
                var newEnd2 = result.IndexOf("\")", newEnd);
                if (newEnd2 < 0 || newEnd2 < newEnd + 2)
                {
                    tooltip = string.Empty;
                }
                else
                {
                    tooltip = result.Substring(newEnd + 2, newEnd2 - newEnd - 2);
                }
            }
            return result;
        }

        #region 主观题使用的几个列表。

        private List<string> materials = new List<string>();
        /// <summary>
        /// 材料列表。
        /// </summary>
        public List<string> Materials { get { return this.materials; } }

        private List<string> asksList = new List<string>();
        /// <summary>
        /// 问题列表。
        /// </summary>
        public List<string> AsksList { get { return this.asksList; } }

        private List<string> answers = new List<string>();
        /// <summary>
        /// [只读]此属性仅适用于主观题。
        /// </summary>
        public List<string> Answers { get { return this.answers; } }

        #endregion


        private string titleString;
        /// <summary>
        /// 题干文本。
        /// </summary>
        public string TitleString
        {
            get
            {
                string result = this.titleString;
                ;
                Regex regex = new Regex(@"!\[.*\]\(.*\)");
                var martch = regex.Match(this.titleString);
                if (martch != null && martch.Success)
                {
                    string shortName = "右图";
                    var link = martch.Value;
                    Regex regex2 = new Regex(@"\[.*\]");
                    var martch2 = regex2.Match(link);
                    if (martch2 != null && martch2.Success && martch2.Length > 2)
                    {
                        shortName = martch2.Value.Substring(1, martch2.Length - 2);
                    }
                    result = this.titleString.Substring(0, martch.Index) + "《" + shortName + "》" + this.titleString.Substring(martch.Index + martch.Length);
                    return result;
                }

                return result;
            }
            set
            {
                this.titleString = value;

                string header;
                string path;
                string tooltip;

                this.markdownLinkText = GetMarkdownImageLinkText(value, out header, out path, out tooltip);
                this.markdownImageHeader = header;
                this.markdownImagePath = path;
                this.markdownIamgeTooltip = tooltip;
            }
        }

        private string markdownImagePath = "";
        /// <summary>
        /// 通过 Markdown 格式引用的图像链接的路径。
        /// </summary>
        public string MarkdownImagePath { get { return this.markdownImagePath; } }

        private string markdownImageHeader = "";
        /// <summary>
        /// 通过 Markdown 格式引用的图像链接的标题文本。
        /// </summary>
        public string MarkdownIamgeHeader { get { return this.markdownImageHeader; } }

        private string markdownIamgeTooltip = "";
        /// <summary>
        /// 通过 Markdown 格式引用的图像链接的提示文本。
        /// </summary>
        public string MarkdownImageTooltip { get { return this.markdownIamgeTooltip; } }

        private string markdownLinkText = "";
        /// <summary>
        /// 通过 Markdown 格式引用的图像链接的完全文本。形如:
        ///     ![图像标题](引用路径 "提示文本")
        /// </summary>
        public string MarkdownLinkText { get { return this.markdownLinkText; } }

        private int orderNumber = 0;
        /// <summary>
        /// 用于给试题随机排序。
        /// </summary>
        public int OrderNumber
        {
            get { return orderNumber; }
            set { this.orderNumber = value; }
        }
    }

    /// <summary>
    /// 支持的试题类别。
    /// </summary>
    public enum QuestionType
    {
        /// <summary>
        /// 选择题。
        /// </summary>
        Choice,

        /// <summary>
        /// 填空题
        /// </summary>
        FillBlank,

        /// <summary>
        /// 判断题。
        /// </summary>
        Judge,      
        
        /// <summary>
        /// 主观题。
        /// </summary>
        Subjective,

        /// <summary>
        /// 非试题,只是文本。
        /// </summary>
        Text
    }
}