#include "srcedit_storyunit.h"
#include "keywordshighlighter.h"
#include <QComboBox>
#include <QMenu>
#include <QSpinBox>
#include <QSplitter>
#include <QVBoxLayout>
#include <QDebug>

using namespace Core;
using namespace Parse::Result;
using namespace Components;
using namespace Enhancement;

StoryunitSourceEdit::StoryunitSourceEdit()
    : edit_square(new QTextEdit),
      refers_descriptions(new QTableView()),
      refers_model(new QStandardItemModel()),
      split(new QSplitter()),
      words_highlighter(new KeywordsHighlighter(this))
{
    words_highlighter->setDocument(this->edit_square->document());
    refers_descriptions->setModel(refers_model);

    connect(this->edit_square, &QTextEdit::textChanged, [this](){
        emit this->dataChanged(filepath_store);
    });

    split->addWidget(edit_square);
    split->addWidget(this->refers_descriptions);
}

StoryunitSourceEdit::~StoryunitSourceEdit()
{
    delete this->refers_descriptions;

    split->deleteLater();
}

void StoryunitSourceEdit::modeReset(const QString &) const
{

}

QList<QString> StoryunitSourceEdit::modes() const
{
    return QList<QString>() << "源码";
}

QString StoryunitSourceEdit::currentMode() const
{
    return modes()[0];
}



void StoryunitSourceEdit::cursor_contex_query()
{
    refers_model->clear();
    refers_model->setHorizontalHeaderLabels(QStringList() << "故事" << "内容");

    auto cursor = edit_square->textCursor();
    auto doc = this->core_ins->parseCore()->queryDocument(QFileInfo(this->absoluteTargetPath()));
    auto words = doc->getWords(cursor.block().blockNumber());

    QList<DesNode*> frags;
    for (auto &w : words) {
        QHash<uint, Parse::Result::DesNode*> nodes;

        auto xhost = w->host();
        while (xhost != nullptr){
            nodes[xhost->typeValue()] = xhost;
            xhost = xhost->parent();
        }

        if(!nodes.contains(NODE_STORYFRAGMENT))
            continue;

        xhost = nodes[NODE_STORYFRAGMENT];
        if(!frags.contains(xhost))
            frags << xhost;
    }

    auto less_equal_than = [](std::pair<uint, uint> a, std::pair<uint, uint> b)->bool{
        if(a.first < b.first)
            return true;
        else if(a.first == b.first)
            return a.second <= b.second;
        return false;
    };
    for(auto &x : frags){
        auto first = x->refered().first();
        auto last = x->refered().last();

        auto tcursor = std::make_pair(cursor.block().blockNumber(), cursor.positionInBlock());
        auto fcursor = std::make_pair(first->row(), first->column());
        auto lcursor = std::make_pair(last->row(), last->column()+last->length());

        if(less_equal_than(fcursor, tcursor) && less_equal_than(tcursor, lcursor))
        {
            auto name = static_cast<NamedNode*>(x)->name();
            present_refersed_tips(name[1], name[0]);
            break;
        }
    }
}

void StoryunitSourceEdit::present_refersed_tips(const QString &unit, const QString &frag)
{
    QHash<QString, DesNode*> nodes;

    auto storys = core_ins->parseCore()->allStoryBoards();
    for (auto &s : storys) {
        auto xins = static_cast<NamedNode*>(s);
        auto refs_x = s->children();
        for(auto &ref : refs_x)
            if(ref->typeValue() == NODE_FRAGMENTREFERENCE){
                auto nm_ref = static_cast<NamedNode*>(ref);

                if(nm_ref->name()[0] == frag && nm_ref->name()[1] == unit){
                    nodes[xins->name()[0]] = ref;
                    break;
                }
            }
    }

    for(auto &nm : nodes.keys())
    {
        QList<QStandardItem*> row;
        row << new QStandardItem(nm);
        row.last()->setEditable(false);
        row << new QStandardItem(nodes[nm]->toString());
        row.last()->setEditable(false);

        refers_model->appendRow(row);
    }

    refers_descriptions->resizeColumnsToContents();
    refers_descriptions->resizeRowsToContents();
}

void StoryunitSourceEdit::contexBinding(Core::AppCore *app) {
    this->core_ins = app;

    connect(edit_square, &QTextEdit::cursorPositionChanged, this, &StoryunitSourceEdit::cursor_contex_query);
}

void StoryunitSourceEdit::renderRepeat() const
{
    this->words_highlighter->rehighlight();
}

QList<QString> StoryunitSourceEdit::suffixes() const
{
    return QList<QString>() << "storyunit";
}

std::tuple<bool, QString> StoryunitSourceEdit::create(const QDir &target, const QString &name, const QString &suffix)
{
    auto filepath = target.filePath(QString("%1.%2").arg(name, suffix));
    if(QFileInfo::exists(filepath))
        return std::make_tuple(false, filepath);

    QFile nf(filepath);
    if(!nf.open(QIODevice::WriteOnly|QIODevice::Text))
        throw new SimpleException("新建文件错误：指定文件无法创建："+filepath);

    return std::make_tuple(true, filepath);
}

MakeTools::FilePresent *StoryunitSourceEdit::newInst() const
{
    return new StoryunitSourceEdit();
}

void StoryunitSourceEdit::applySetting(const QString &name, Core::AppCore *core)
{
    this->name_store = name;
}

QString StoryunitSourceEdit::name() const
{
    return name_store;
}

void StoryunitSourceEdit::load(const QFileInfo &target_file)
{
    if(!target_file.exists())
        throw new SimpleException("文件打开：指定文件无法不存在："+target_file.absoluteFilePath());

    if(!target_file.isFile())
        throw new SimpleException("文件打开：指定文件无效：" + target_file.absoluteFilePath());
    filepath_store = target_file.absoluteFilePath();

    QFile bin(filepath_store);
    if(!bin.open(QIODevice::ReadOnly|QIODevice::Text))
        throw new SimpleException("文件打开：指定的文件无法打开："+filepath_store);
    QTextStream tin(&bin);
    tin.setCodec("UTF-8");
    this->edit_square->setText(tin.readAll());
}

void StoryunitSourceEdit::saveAs(const QString &path)
{
    if(!path.isEmpty())
        filepath_store = path;

    QFile bout(filepath_store);
    if(!bout.open(QIODevice::WriteOnly|QIODevice::Text))
        throw new SimpleException("保存出错：无法写出到指定文件："+filepath_store);
    QTextStream tout(&bout);
    tout.setCodec("UTF-8");
    tout << this->edit_square->toPlainText();
    tout.flush();
}

QString StoryunitSourceEdit::relativeTargetPath(const QDir &base) const
{
    return base.relativeFilePath(filepath_store);
}

QString StoryunitSourceEdit::absoluteTargetPath() const
{
    return filepath_store;
}

QString StoryunitSourceEdit::getText() const
{
    return edit_square->toPlainText();
}

bool StoryunitSourceEdit::isModified() const
{
    return true;
}

void StoryunitSourceEdit::jumpTo(const QList<QString> &path)
{
    auto fpath = this->absoluteTargetPath();
    auto core = core_ins->parseCore();
    if(path.size()){
        auto storynode = core->queryStoryUnit(path[0]).first();
        auto first_word = storynode->refered()[0];

        auto textblock = this->edit_square->document()->findBlockByNumber(first_word->row());
        auto cur = this->edit_square->textCursor();
        cur.setPosition(textblock.position());
        cur.select(QTextCursor::SelectionType::LineUnderCursor);
        this->edit_square->setTextCursor(cur);

        if(path.size() > 1){
            auto storypoint = core->queryStoryFragment(storynode, path[1]).first();
            first_word = storypoint->refered()[0];

            auto textblock = this->edit_square->document()->findBlockByNumber(first_word->row());
            cur.setPosition(textblock.position());
            cur.select(QTextCursor::SelectionType::LineUnderCursor);
            this->edit_square->setTextCursor(cur);
        }
    }

    edit_square->setFocus();
}


QWidget *StoryunitSourceEdit::widget() const
{
    return split;
}

MakeTools::FilePresent::Features StoryunitSourceEdit::features()
{
    return Feature::Compile | Feature::HighLight;
}
