#include "noteeditorlogic.h"
#include "customDocument.h"
#include "customMarkdownHighlighter.h"
#include "dbmanager.h"
#include "taglistview.h"
#include "taglistmodel.h"
#include "tagpool.h"
#include "taglistdelegate.h"
#include <QScrollBar>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QDebug>
#include <QCursor>

#define FIRST_LINE_MAX 80
// 构造函数，根据 Qt 版本不同，参数有所不同
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)

NoteEditorLogic::NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLabel,
                                 QLineEdit *searchEdit, QWidget *kanbanWidget,
                                 TagListView *tagListView, TagPool *tagPool, DBManager *dbManager,
                                 QObject *parent)
#else
NoteEditorLogic::NoteEditorLogic(CustomDocument *textEdit, QLabel *editorDateLabel,
                                 QLineEdit *searchEdit, TagListView *tagListView, TagPool *tagPool,
                                 DBManager *dbManager, QObject *parent)
#endif
    : QObject(parent),
      m_textEdit{ textEdit },
      m_highlighter{ new CustomMarkdownHighlighter{ m_textEdit->document() } },
      m_editorDateLabel{ editorDateLabel },
      m_searchEdit{ searchEdit },
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
      m_kanbanWidget{ kanbanWidget },
#endif
      m_tagListView{ tagListView },
      m_dbManager{ dbManager },
      m_isContentModified{ false },
      m_spacerColor{ 191, 191, 191 },
      m_currentAdaptableEditorPadding{ 0 },
      m_currentMinimumEditorPadding{ 0 }
{// 连接文本编辑器的文本变化信号
    connect(m_textEdit, &QTextEdit::textChanged, this, &NoteEditorLogic::onTextEditTextChanged);
    // 连接创建或更新笔记请求信号
    connect(this, &NoteEditorLogic::requestCreateUpdateNote, m_dbManager,
            &DBManager::onCreateUpdateRequestedNoteContent, Qt::QueuedConnection);
    // auto save timer 自动保存定时器
    m_autoSaveTimer.setSingleShot(true);
    m_autoSaveTimer.setInterval(50);
    connect(&m_autoSaveTimer, &QTimer::timeout, this, [this]() { saveNoteToDB(); });
    // 初始化标签列表模型和委托
    m_tagListModel = new TagListModel{ this };
    m_tagListModel->setTagPool(tagPool);
    m_tagListView->setModel(m_tagListModel);
    m_tagListDelegate = new TagListDelegate{ this };
    m_tagListView->setItemDelegate(m_tagListDelegate);
    // 连接标签池数据更新信号
    connect(tagPool, &TagPool::dataUpdated, this, [this](int) { showTagListForCurrentNote(); });
    // 连接滚动条位置变化信号
    connect(m_textEdit->verticalScrollBar(), &QScrollBar::valueChanged, this, [this](int value) {
        if (m_currentNotes.size() == 1 && m_currentNotes[0].id() != SpecialNodeID::InvalidNodeId) {
            m_currentNotes[0].setScrollBarPosition(value);
            emit updateNoteDataInList(m_currentNotes[0]);
            m_isContentModified = true;
            m_autoSaveTimer.start();
        }
    });
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
    // 连接显示看板视图信号
    connect(this, &NoteEditorLogic::showKanbanView, this, [this]() {
        if (m_kanbanWidget != nullptr) {
            emit setVisibilityOfFrameRightNonEditor(false);
            bool shouldRecheck = checkForTasksInEditor();
            if (shouldRecheck) {
                checkForTasksInEditor();
            }
            m_kanbanWidget->show();
            m_textEdit->hide();
            m_textEdit->clearFocus();
            emit kanbanShown();
        }
    });
    // 连接隐藏看板视图信号
    connect(this, &NoteEditorLogic::hideKanbanView, this, [this]() {
        if (m_kanbanWidget != nullptr) {
            emit setVisibilityOfFrameRightNonEditor(true);
            m_kanbanWidget->hide();
            m_textEdit->show();
            emit clearKanbanModel();
            emit textShown();
        }
    });
#endif
}
// 获取是否启用 Markdown
bool NoteEditorLogic::markdownEnabled() const
{
    return m_highlighter->document() != nullptr;
}
// 设置是否启用 Markdown
void NoteEditorLogic::setMarkdownEnabled(bool enabled)
{
    m_highlighter->setDocument(enabled ? m_textEdit->document() : nullptr);
}
// 在编辑器中显示笔记
void NoteEditorLogic::showNotesInEditor(const QVector<NodeData> &notes)
{
    auto currentId = currentEditingNoteId();
    if (notes.size() == 1 && notes[0].id() != SpecialNodeID::InvalidNodeId) {
        if (currentId != SpecialNodeID::InvalidNodeId && notes[0].id() != currentId) {
            emit noteEditClosed(m_currentNotes[0], false);
        }

#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
        emit resetKanbanSettings();
        emit checkMultipleNotesSelected(
                QVariant(false)); // TODO: if not PRO version, should be true
#endif

        m_textEdit->blockSignals(true);

        m_currentNotes = notes;
        showTagListForCurrentNote();
        //     fixing bug #202
        m_textEdit->setTextBackgroundColor(QColor(247, 247, 247, 0));

        QString content = notes[0].content();
        QDateTime dateTime = notes[0].lastModificationdateTime();
        int scrollbarPos = notes[0].scrollBarPosition();

        // set text and date
        bool isTextChanged = content != m_textEdit->toPlainText();
        if (isTextChanged) {
            m_textEdit->setText(content);
        }
        QString noteDate = dateTime.toString(Qt::ISODate);
        QString noteDateEditor = getNoteDateEditor(noteDate);
        m_editorDateLabel->setText(noteDateEditor);
        // set scrollbar position
        m_textEdit->verticalScrollBar()->setValue(scrollbarPos);
        m_textEdit->blockSignals(false);
        m_textEdit->setReadOnly(false);
        m_textEdit->setTextInteractionFlags(Qt::TextEditorInteraction);
        m_textEdit->setFocusPolicy(Qt::StrongFocus);
        highlightSearch();
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
        if (m_kanbanWidget != nullptr && m_kanbanWidget->isVisible()) {
            emit clearKanbanModel();
            bool shouldRecheck = checkForTasksInEditor();
            if (shouldRecheck) {
                checkForTasksInEditor();
            }
            m_textEdit->setVisible(false);
            return;
        } else {
            m_textEdit->setVisible(true);
        }
#else
        m_textEdit->setVisible(true);
#endif
        emit textShown();
    } else if (notes.size() > 1) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
        emit checkMultipleNotesSelected(QVariant(true));
#endif
        m_currentNotes = notes;
        m_tagListView->setVisible(false);
        m_textEdit->blockSignals(true);
        auto verticalScrollBarValueToRestore = m_textEdit->verticalScrollBar()->value();
        m_textEdit->clear();
        auto padding = m_currentAdaptableEditorPadding > m_currentMinimumEditorPadding
                ? m_currentAdaptableEditorPadding
                : m_currentMinimumEditorPadding;
        QPixmap sep(QSize{ m_textEdit->width() - padding * 2 - 12, 4 });
        sep.fill(Qt::transparent);
        QPainter painter(&sep);
        painter.setPen(m_spacerColor);
        painter.drawRect(0, 1, sep.width(), 1);
        m_textEdit->document()->addResource(QTextDocument::ImageResource, QUrl("mydata://sep.png"),
                                            sep);
        for (int i = 0; i < notes.size(); ++i) {
            auto cursor = m_textEdit->textCursor();
            cursor.movePosition(QTextCursor::End);
            if (!notes[i].content().endsWith("\n")) {
                if (i != 0) {
                    cursor.insertText("\n" + notes[i].content() + "\n");
                } else {
                    cursor.insertText(notes[i].content() + "\n");
                }
            } else {
                cursor.insertText(notes[i].content());
            }
            if (i != notes.size() - 1) {
                cursor.movePosition(QTextCursor::End);
                cursor.insertText("\n");
                cursor.insertImage("mydata://sep.png");
                cursor.insertText("\n");
            }
        }
        m_textEdit->verticalScrollBar()->setValue(verticalScrollBarValueToRestore);
        m_textEdit->blockSignals(false);
        m_textEdit->setReadOnly(true);
        m_textEdit->setTextInteractionFlags(Qt::TextSelectableByMouse);
        m_textEdit->setFocusPolicy(Qt::NoFocus);
        highlightSearch();
    }
}
// 文本编辑器内容变化时的处理
void NoteEditorLogic::onTextEditTextChanged()
{
    if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) {
        m_textEdit->blockSignals(true);
        QString content = m_currentNotes[0].content();
        if (m_textEdit->toPlainText() != content) {
            // move note to the top of the list
            emit moveNoteToListViewTop(m_currentNotes[0]);

            // Get the new data
            QString firstline = getFirstLine(m_textEdit->toPlainText());
            QDateTime dateTime = QDateTime::currentDateTime();
            QString noteDate = dateTime.toString(Qt::ISODate);
            m_editorDateLabel->setText(NoteEditorLogic::getNoteDateEditor(noteDate));
            // update note data
            m_currentNotes[0].setContent(m_textEdit->toPlainText());
            m_currentNotes[0].setFullTitle(firstline);
            m_currentNotes[0].setLastModificationDateTime(dateTime);
            m_currentNotes[0].setIsTempNote(false);
            m_currentNotes[0].setScrollBarPosition(m_textEdit->verticalScrollBar()->value());
            // update note data in list view
            emit updateNoteDataInList(m_currentNotes[0]);
            m_isContentModified = true;
            m_autoSaveTimer.start();
            emit setVisibilityOfFrameRightWidgets(false);
        }
        m_textEdit->blockSignals(false);
    } else {
        qDebug() << "NoteEditorLogic::onTextEditTextChanged() : m_currentNote is not valid";
    }
}

#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
// 重新排列文本编辑器中的任务
void NoteEditorLogic::rearrangeTasksInTextEditor(int startLinePosition, int endLinePosition,
                                                 int newLinePosition)
{
    QTextDocument *document = m_textEdit->document();
    QTextCursor cursor(document);
    cursor.setPosition(document->findBlockByNumber(startLinePosition).position());
    cursor.setPosition(document->findBlockByNumber(endLinePosition).position(),
                       QTextCursor::KeepAnchor);
    cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
    if (document->findBlockByNumber(endLinePosition + 1).isValid()) {
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
    }
    QString selectedText = cursor.selectedText();
    cursor.removeSelectedText();

    if (newLinePosition <= startLinePosition) {
        cursor.setPosition(document->findBlockByLineNumber(newLinePosition).position());
    } else {
        int newPositionBecauseOfRemoval =
                newLinePosition - (endLinePosition - startLinePosition + 1);
        if (newPositionBecauseOfRemoval == document->lineCount()) {
            cursor.setPosition(
                    document->findBlockByLineNumber(newPositionBecauseOfRemoval - 1).position());
            cursor.movePosition(QTextCursor::EndOfBlock);
            selectedText = "\n" + selectedText;
        } else {
            cursor.setPosition(
                    document->findBlockByLineNumber(newPositionBecauseOfRemoval).position());
        }
    }
    cursor.insertText(selectedText);

    checkForTasksInEditor();
}
// 重新排列文本编辑器中的列
void NoteEditorLogic::rearrangeColumnsInTextEditor(int startLinePosition, int endLinePosition,
                                                   int newLinePosition)
{
    QTextDocument *document = m_textEdit->document();
    QTextCursor cursor(document);
    cursor.setPosition(document->findBlockByNumber(startLinePosition).position());
    cursor.setPosition(document->findBlockByNumber(endLinePosition).position(),
                       QTextCursor::KeepAnchor);
    cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
    if (document->findBlockByNumber(endLinePosition + 1).isValid()) {
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
    }
    QString selectedText = cursor.selectedText();
    cursor.removeSelectedText();
    cursor.setPosition(document->findBlockByNumber(startLinePosition).position());
    if (document->findBlockByNumber(startLinePosition + 1).isValid()) {
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
        cursor.removeSelectedText();
    }

    if (startLinePosition < newLinePosition) {
        // Goes down
        int newPositionBecauseOfRemoval =
                newLinePosition - (endLinePosition - startLinePosition + 1);
        cursor.setPosition(document->findBlockByLineNumber(newPositionBecauseOfRemoval).position());
        cursor.movePosition(QTextCursor::EndOfBlock);
        cursor.insertText("\n" + selectedText);
    } else {
        // Goes up
        cursor.setPosition(document->findBlockByLineNumber(newLinePosition).position());
        cursor.insertText(selectedText + "\n");
    }

    checkForTasksInEditor();
}
// 获取某行的任务数据
QMap<QString, int> NoteEditorLogic::getTaskDataInLine(const QString &line)
{
    QStringList taskExpressions = { "- [ ]", "- [x]", "* [ ]", "* [x]", "- [X]", "* [X]" };
    QMap<QString, int> taskMatchLineData;
    taskMatchLineData["taskMatchIndex"] = -1;

    int taskMatchIndex = -1;
    for (int j = 0; j < taskExpressions.size(); j++) {
        taskMatchIndex = line.indexOf(taskExpressions[j]);
        if (taskMatchIndex != -1) {
            taskMatchLineData["taskMatchIndex"] = taskMatchIndex;
            taskMatchLineData["taskExpressionSize"] = taskExpressions[j].size();
            taskMatchLineData["taskChecked"] = taskExpressions[j][3] == 'x' ? 1 : 0;
            return taskMatchLineData;
        }
    }

    return taskMatchLineData;
}
// 检查某行的任务
void NoteEditorLogic::checkTaskInLine(int lineNumber)
{
    QTextDocument *document = m_textEdit->document();
    QTextBlock block = document->findBlockByLineNumber(lineNumber);

    if (block.isValid()) {
        int indexOfTaskInLine = getTaskDataInLine(block.text())["taskMatchIndex"];
        if (indexOfTaskInLine == -1)
            return;
        QTextCursor cursor(block);
        cursor.setPosition(block.position() + indexOfTaskInLine + 3, QTextCursor::MoveAnchor);

        // Remove the old character and insert the new one
        cursor.deleteChar();
        cursor.insertText("x");
    }
}
// 取消检查某行的任务
void NoteEditorLogic::uncheckTaskInLine(int lineNumber)
{
    QTextDocument *document = m_textEdit->document();
    QTextBlock block = document->findBlockByLineNumber(lineNumber);

    if (block.isValid()) {
        int indexOfTaskInLine = getTaskDataInLine(block.text())["taskMatchIndex"];
        if (indexOfTaskInLine == -1)
            return;
        QTextCursor cursor(block);
        cursor.setPosition(block.position() + indexOfTaskInLine + 3, QTextCursor::MoveAnchor);

        // Remove the old character and insert the new one
        cursor.deleteChar();
        cursor.insertText(" ");
    }
}
// 替换某行之间的文本
void NoteEditorLogic::replaceTextBetweenLines(int startLinePosition, int endLinePosition,
                                              QString &newText)
{
    QTextDocument *document = m_textEdit->document();
    QTextBlock startBlock = document->findBlockByLineNumber(startLinePosition);
    QTextBlock endBlock = document->findBlockByLineNumber(endLinePosition);
    QTextCursor cursor(startBlock);
    cursor.setPosition(endBlock.position() + endBlock.length() - 1, QTextCursor::KeepAnchor);
    cursor.removeSelectedText();
    cursor.insertText(newText);
}
// 更新任务文本
void NoteEditorLogic::updateTaskText(int startLinePosition, int endLinePosition,
                                     const QString &newText)
{
    QTextDocument *document = m_textEdit->document();
    QTextBlock block = document->findBlockByLineNumber(startLinePosition);
    if (block.isValid()) {
        QMap<QString, int> taskData = getTaskDataInLine(block.text());
        int indexOfTaskInLine = taskData["taskMatchIndex"];
        if (indexOfTaskInLine == -1)
            return;
        QString taskExpressionText =
                block.text().mid(0, taskData["taskMatchIndex"] + taskData["taskExpressionSize"]);

        QString newTextModified = newText;
        newTextModified.replace("\n\n", "\n");
        newTextModified.replace("~~", "");
        QStringList taskExpressions = { "- [ ]", "- [x]", "* [ ]", "* [x]", "- [X]", "* [X]" };
        for (const auto &taskExpression : taskExpressions) {
            newTextModified.replace(taskExpression, "");
        }

        // We must allow hashtags solely for the first line, otherwise it will mess up
        // the parser - interprate the task's description as columns
        if (newTextModified.count('\n') > 1) {
            QStringList newTextModifiedSplitted = newTextModified.split('\n');

            if (newTextModifiedSplitted.size() > 1) {
                for (int i = 1; i < newTextModifiedSplitted.size(); i++) {
                    // Skipping the first line
                    newTextModifiedSplitted[i].replace("# ", "");
                    newTextModifiedSplitted[i].replace("#", "");
                }

                newTextModified = newTextModifiedSplitted.join('\n');
            }
        }

        QString newTaskText = taskExpressionText + " " + newTextModified;
        if (newTaskText.size() > 0 && newTaskText[newTaskText.size() - 1] == '\n') {
            newTaskText.remove(newTaskText.size() - 1, 1);
        }
        replaceTextBetweenLines(startLinePosition, endLinePosition, newTaskText);
        checkForTasksInEditor();
    }
}
// 添加新任务
void NoteEditorLogic::addNewTask(int startLinePosition, const QString newTaskText)
{
    QString newText = "\n- [ ] " + newTaskText;
    QTextDocument *document = m_textEdit->document();
    QTextBlock startBlock = document->findBlockByLineNumber(startLinePosition);

    if (!startBlock.isValid())
        return;

    QTextCursor cursor(startBlock);
    cursor.movePosition(QTextCursor::EndOfBlock);
    cursor.insertText(newText);

    checkForTasksInEditor();
}
// 移除某行之间的文本
void NoteEditorLogic::removeTextBetweenLines(int startLinePosition, int endLinePosition)
{
    if (startLinePosition < 0 || endLinePosition < startLinePosition) {
        return;
    }

    QTextDocument *document = m_textEdit->document();
    QTextCursor cursor(document);
    cursor.setPosition(document->findBlockByNumber(startLinePosition).position());
    cursor.setPosition(document->findBlockByNumber(endLinePosition).position(),
                       QTextCursor::KeepAnchor);
    cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
    if (document->findBlockByNumber(endLinePosition + 1).isValid()) {
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
    }
    cursor.removeSelectedText();
}
// 移除任务
void NoteEditorLogic::removeTask(int startLinePosition, int endLinePosition)
{
    removeTextBetweenLines(startLinePosition, endLinePosition);
    checkForTasksInEditor();
}
// 添加新列
void NoteEditorLogic::addNewColumn(int startLinePosition, const QString &columnTitle)
{
    if (startLinePosition < 0)
        return;

    QTextDocument *document = m_textEdit->document();
    QTextBlock block = document->findBlockByNumber(startLinePosition);

    if (block.isValid()) {
        QTextCursor cursor(block);
        if (startLinePosition == 0) {
            cursor.movePosition(QTextCursor::StartOfBlock);
        } else {
            cursor.movePosition(QTextCursor::EndOfBlock);
        }
        cursor.insertText(columnTitle);
        m_textEdit->setTextCursor(cursor);
    } else {
        m_textEdit->append(columnTitle);
    }

    checkForTasksInEditor();
}
// 移除列
void NoteEditorLogic::removeColumn(int startLinePosition, int endLinePosition)
{
    removeTextBetweenLines(startLinePosition, endLinePosition);

    if (startLinePosition < 0 || endLinePosition < startLinePosition)
        return;
    QTextDocument *document = m_textEdit->document();
    QTextCursor cursor(document);
    cursor.setPosition(document->findBlockByNumber(startLinePosition).position());
    if (cursor.block().isValid() && cursor.block().text().isEmpty()) {
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
        cursor.removeSelectedText();
    }

    checkForTasksInEditor();
}
// 更新列标题
void NoteEditorLogic::updateColumnTitle(int lineNumber, const QString &newText)
{
    QTextDocument *document = m_textEdit->document();
    QTextBlock block = document->findBlockByLineNumber(lineNumber);

    if (block.isValid()) {
        // Header by hashtag
        int lastIndexOfHashTag = block.text().lastIndexOf("#");
        if (lastIndexOfHashTag != -1) {
            QTextCursor cursor(block);
            cursor.setPosition(block.position() + lastIndexOfHashTag + 1, QTextCursor::MoveAnchor);
            cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
            cursor.removeSelectedText();
            cursor.setPosition(block.position() + lastIndexOfHashTag + 1);
            cursor.insertText(" " + newText);

        } else {
            int lastIndexofColon = block.text().lastIndexOf("::");
            if (lastIndexofColon != -1) {
                // Header by double colons
                QTextCursor cursor(block);
                cursor.setPosition(block.position(), QTextCursor::MoveAnchor);
                cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
                cursor.removeSelectedText();
                cursor.setPosition(block.position());
                cursor.insertText(newText + "::");
            }
        }
    }
}
// 向文本编辑器添加无标题列
void NoteEditorLogic::addUntitledColumnToTextEditor(int startLinePosition)
{
    QString columnTitle = "# Untitled\n\n";
    QTextDocument *document = m_textEdit->document();
    QTextBlock block = document->findBlockByNumber(startLinePosition);

    if (block.isValid()) {
        QTextCursor cursor(block);
        cursor.movePosition(QTextCursor::StartOfBlock);
        cursor.insertText(columnTitle);
    }
}
// 向 JSON 数据添加新列
void NoteEditorLogic::appendNewColumn(QJsonArray &data, QJsonObject &currentColumn,
                                      QString &currentTitle, QJsonArray &tasks)
{
    if (!tasks.isEmpty()) {
        currentColumn["title"] = currentTitle;
        currentColumn["tasks"] = tasks;
        currentColumn["columnEndLine"] = tasks.last()["taskEndLine"];
        data.append(currentColumn);
        currentColumn = QJsonObject();
        tasks = QJsonArray();
    }
}

// Check if there are any tasks in the current note.
// If there are, sends the data to the kanban view.
// Structure:
// QJsonArray([
// {
//    "title":"TODO",
//    "columnStartLine": 1
//    "columnEndLine": 4
//    "tasks":[
//              {"checked":false,"text":"todo 1", "taskStartine": 3, "taskEndLine": 3},
//              {"checked":false,"text":"todo 2", "taskStartine": 4, "taskEndLine": 4}}]
// },
// ])
bool NoteEditorLogic::checkForTasksInEditor()// 检查编辑器中的任务
{
    QStringList lines = m_textEdit->toPlainText().split("\n");
    QJsonArray data;
    QJsonObject currentColumn;
    QJsonArray tasks;
    QString currentTitle = "";
    bool isPreviousLineATask = false;

    for (int i = 0; i < lines.size(); i++) {
        QString line = lines[i];
        QString lineTrimmed = line.trimmed();

        // Header title
        if (lineTrimmed.startsWith("#")) {
            if (!tasks.isEmpty() && currentTitle.isEmpty()) {
                // If we have only tasks without a header we insert one and call this function again
                addUntitledColumnToTextEditor(tasks.first()["taskStartLine"].toInt());
                return true;
            }
            appendNewColumn(data, currentColumn, currentTitle, tasks);
            currentColumn["columnStartLine"] = i;
            int countOfHashTags = lineTrimmed.count('#');
            currentTitle = lineTrimmed.mid(countOfHashTags);
            isPreviousLineATask = false;
        }
        // Non-header text with double colons
        else if (lineTrimmed.endsWith("::") && getTaskDataInLine(line)["taskMatchIndex"] == -1) {
            if (!tasks.isEmpty() && currentTitle.isEmpty()) {
                // If we have only tasks without a header we insert one and call this function again
                addUntitledColumnToTextEditor(tasks.first()["taskStartLine"].toInt());
                return true;
            }
            appendNewColumn(data, currentColumn, currentTitle, tasks);
            currentColumn["columnStartLine"] = i;
            QStringList parts = line.split("::");
            currentTitle = parts[0].trimmed();
            isPreviousLineATask = false;
        }
        // Todo item
        else {
            QMap<QString, int> taskDataInLine = getTaskDataInLine(line);
            int indexOfTaskInLine = taskDataInLine["taskMatchIndex"];

            if (indexOfTaskInLine != -1) {
                QJsonObject taskObject;
                QString taskText =
                        line.mid(indexOfTaskInLine + taskDataInLine["taskExpressionSize"])
                                .trimmed();
                taskObject["text"] = taskText;
                taskObject["checked"] = taskDataInLine["taskChecked"] == 1;
                taskObject["taskStartLine"] = i;
                taskObject["taskEndLine"] = i;
                tasks.append(taskObject);
                isPreviousLineATask = true;
            }
            // If it's a continues description of the task push current line's text to the last task
            else if (!line.isEmpty() && isPreviousLineATask) {
                if (tasks.size() > 0) {
                    QJsonObject newTask = tasks[tasks.size() - 1].toObject();
                    QString newTaskText = newTask["text"].toString() + "  \n"
                            + lineTrimmed; // For markdown rendering a line break needs two white
                                           // spaces
                    newTask["text"] = newTaskText;
                    newTask["taskEndLine"] = i;
                    tasks[tasks.size() - 1] = newTask;
                }
            } else {
                isPreviousLineATask = false;
            }
        }
    }

    if (!tasks.isEmpty() && currentTitle.isEmpty()) {
        // If we have only tasks without a header we insert one and call this function again
        addUntitledColumnToTextEditor(tasks.first()["taskStartLine"].toInt());
        return true;
    }

    appendNewColumn(data, currentColumn, currentTitle, tasks);

    emit tasksFoundInEditor(QVariant(data));

    return false;
}
#endif
// 将字符串转换为 QDateTime
QDateTime NoteEditorLogic::getQDateTime(const QString &date)
{
    QDateTime dateTime = QDateTime::fromString(date, Qt::ISODate);
    return dateTime;
}
// 显示当前笔记的标签列表
void NoteEditorLogic::showTagListForCurrentNote()
{
    if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) {
        auto tagIds = m_currentNotes[0].tagIds();
        if (tagIds.count() > 0) {
            m_tagListView->setVisible(true);
            m_tagListModel->setModelData(tagIds);
            return;
        }
        m_tagListModel->setModelData(tagIds);
    }
    m_tagListView->setVisible(false);
}

bool NoteEditorLogic::isInEditMode() const// 获取是否处于编辑模式
{
    if (m_currentNotes.size() == 1) {
        return true;
    }
    return false;
}

int NoteEditorLogic::currentMinimumEditorPadding() const// 获取当前最小编辑器填充
{
    return m_currentMinimumEditorPadding;
}
// 设置当前最小编辑器填充
void NoteEditorLogic::setCurrentMinimumEditorPadding(int newCurrentMinimumEditorPadding)
{
    m_currentMinimumEditorPadding = newCurrentMinimumEditorPadding;
}
// 获取当前可适应的编辑器填充
int NoteEditorLogic::currentAdaptableEditorPadding() const
{
    return m_currentAdaptableEditorPadding;
}
// 设置当前可适应的编辑器填充
void NoteEditorLogic::setCurrentAdaptableEditorPadding(int newCurrentAdaptableEditorPadding)
{
    m_currentAdaptableEditorPadding = newCurrentAdaptableEditorPadding;
}
// 获取当前编辑的笔记 ID
int NoteEditorLogic::currentEditingNoteId() const
{
    if (isInEditMode()) {
        return m_currentNotes[0].id();
    }
    return SpecialNodeID::InvalidNodeId;
}
// 将笔记保存到数据库
void NoteEditorLogic::saveNoteToDB()
{
    if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId && m_isContentModified
        && !m_currentNotes[0].isTempNote()) {
        emit requestCreateUpdateNote(m_currentNotes[0]);
        m_isContentModified = false;
    }
}
// 关闭编辑器
void NoteEditorLogic::closeEditor()
{
    if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) {
        saveNoteToDB();
        emit noteEditClosed(m_currentNotes[0], false);
    }
    m_currentNotes.clear();

    m_textEdit->blockSignals(true);
    m_textEdit->clear();
    m_textEdit->clearFocus();
    m_textEdit->blockSignals(false);
    m_tagListModel->setModelData({});
}
// 笔记标签列表发生变化时的处理
void NoteEditorLogic::onNoteTagListChanged(int noteId, const QSet<int> &tagIds)
{
    if (currentEditingNoteId() == noteId) {
        m_currentNotes[0].setTagIds(tagIds);
        showTagListForCurrentNote();
    }
}
// 删除当前笔记
void NoteEditorLogic::deleteCurrentNote()
{
    if (isTempNote()) {
        auto noteNeedDeleted = m_currentNotes[0];
        m_currentNotes.clear();
        m_textEdit->blockSignals(true);
        m_textEdit->clear();
        m_textEdit->clearFocus();
        m_textEdit->blockSignals(false);
        emit noteEditClosed(noteNeedDeleted, true);
    } else if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) {
        auto noteNeedDeleted = m_currentNotes[0];
        saveNoteToDB();
        m_currentNotes.clear();
        m_textEdit->blockSignals(true);
        m_textEdit->clear();
        m_textEdit->clearFocus();
        m_textEdit->blockSignals(false);
        emit noteEditClosed(noteNeedDeleted, false);
        emit deleteNoteRequested(noteNeedDeleted);
    }
}

/*!
 * \brief NoteEditorLogic::getFirstLine
 * Get a string 'str' and return only the first line of it
 * If the string contain no text, return "New Note"
 * TODO: We might make it more efficient by not loading the entire string into the memory
 * \param str
 * \return
 */
QString NoteEditorLogic::getFirstLine(const QString &str)// 获取文本的首行
{
    QString text = str.trimmed();
    if (text.isEmpty()) {
        return "New Note";
    }
    QTextStream ts(&text);
    return ts.readLine(FIRST_LINE_MAX);
}

QString NoteEditorLogic::getSecondLine(const QString &str)// 获取文本的第二行
{
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
    auto sl = str.split("\n", QString::SkipEmptyParts);
#else
    auto sl = str.split("\n", Qt::SkipEmptyParts);
#endif
    if (sl.size() < 2) {
        return getFirstLine(str);
    }
    int i = 1;
    QString text;
    do {
        if (i >= sl.size()) {
            return getFirstLine(str);
        }
        text = sl[i].trimmed();
        ++i;
    } while (text.isEmpty());
    QTextStream ts(&text);
    return ts.readLine(FIRST_LINE_MAX);
}
// 设置主题
void NoteEditorLogic::setTheme(Theme::Value theme, QColor textColor, qreal fontSize)
{
    m_tagListDelegate->setTheme(theme);
    m_highlighter->setTheme(theme, textColor, fontSize);
    switch (theme) {
    case Theme::Light: {
        m_spacerColor = QColor(191, 191, 191);
        break;
    }
    case Theme::Dark: {
        m_spacerColor = QColor(212, 212, 212);
        break;
    }
    case Theme::Sepia: {
        m_spacerColor = QColor(191, 191, 191);
        break;
    }
    }
    if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId) {
        int verticalScrollBarValueToRestore = m_textEdit->verticalScrollBar()->value();
        m_textEdit->setText(
                m_textEdit->toPlainText()); // TODO: Update the text color without setting the text
        m_textEdit->verticalScrollBar()->setValue(verticalScrollBarValueToRestore);
    } else {
        int verticalScrollBarValueToRestore = m_textEdit->verticalScrollBar()->value();
        showNotesInEditor(m_currentNotes);
        m_textEdit->verticalScrollBar()->setValue(verticalScrollBarValueToRestore);
    }
}
// 获取笔记编辑日期
QString NoteEditorLogic::getNoteDateEditor(const QString &dateEdited)
{
    QDateTime dateTimeEdited(getQDateTime(dateEdited));
    QLocale usLocale(QLocale(QStringLiteral("en_US")));

    return usLocale.toString(dateTimeEdited, QStringLiteral("MMMM d, yyyy, h:mm A"));
}
// 高亮搜索结果
void NoteEditorLogic::highlightSearch() const
{
    QString searchString = m_searchEdit->text();

    if (searchString.isEmpty())
        return;

    m_textEdit->moveCursor(QTextCursor::Start);

    QList<QTextEdit::ExtraSelection> extraSelections;
    QTextCharFormat highlightFormat;
    highlightFormat.setBackground(Qt::yellow);

    while (m_textEdit->find(searchString))
        extraSelections.append({ m_textEdit->textCursor(), highlightFormat });

    if (!extraSelections.isEmpty()) {
        m_textEdit->setTextCursor(extraSelections.first().cursor);
        m_textEdit->setExtraSelections(extraSelections);
    }
}
// 获取是否是临时笔记
bool NoteEditorLogic::isTempNote() const
{
    if (currentEditingNoteId() != SpecialNodeID::InvalidNodeId && m_currentNotes[0].isTempNote()) {
        return true;
    }
    return false;
}
