#include "mainwindow.h"
#include "srcedit_defaulttext.h"
#include "srcedit_storyboard.h"
#include "srcedit_storychain.h"
#include "srcedit_storyunit.h"
#include "srcedit_storyvolume.h"
#include "xapp.h"

#include <QApplication>
#include <QDateTime>
#include <QDebug>
#include <QFileDialog>
#include <QInputDialog>
#include <QLabel>
#include <QList>
#include <QMenuBar>
#include <QMessageBox>
#include <QStatusBar>
#include <QTextEdit>
#include <QToolBar>
#include <QVBoxLayout>
#include <xmlconfig.h>
#include <xmlprojectmanager.h>

#include "command_list.h"

using namespace Project;
using namespace MakeTools;
using namespace Parse::Result;
using namespace Components;
using namespace Core;
using namespace Tools;
using namespace CommandList;
using namespace DataModel;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      app_core(new AppCore(this)),
      sync_kernel(new StatusSyncCore(this)),
      split_first(new QSplitter(Qt::Horizontal, this)),
      split_second(new QSplitter(Qt::Vertical, this)),
      split_third(new QSplitter(Qt::Horizontal, this)),
      project_manager(new XMLProjectManager(this)),
      left_funcs(new PresentContainer(QTabWidget::West, this)),
      right_funcs(new PresentContainer(QTabWidget::West, this)),
      center_funcs(new PresentContainer(QTabWidget::North, this)),
      bottom_funcs(new PresentContainer(QTabWidget::North, this)),
      docs_container(new DocumentsManager(app_core, project_manager, this)),
      fragments_model(new FragmentsOrderviewModel(app_core)),
      boards_model(new StoryboardsPresentModel(app_core)),
      chains_model(new StorychainsPresentModel(app_core)),
      concepts_model(new StoryconceptsPresentModel(app_core)),
      units_model(new StoryunitsPresentModel(app_core)),
      welcome_list(new WelcomePanel()),
      errors_present(new MessagesPresent(app_core->getMakeCore(), this)),
      project_present(new ProjectPresent(XApp::disp_core, docs_container, this)),
      fragments_order(new FragmentsOrderPresent(XApp::disp_core, fragments_model, this)),
      boards_view(new StoryboardsPresent(XApp::disp_core, boards_model, this)),
      chains_view(new StorychainsPresent(XApp::disp_core, chains_model, this)),
      concept_view(new StoryconceptsPresent(XApp::disp_core, concepts_model, this)),
      units_view(new StoryunitsPresent(XApp::disp_core, units_model, this)) {

    QApplication::instance()->installEventFilter(this);
    initial_commandlist(XApp::disp_core);

    XApp::disp_core->registerMember(this->app_core);
    this->app_core->setCurrentProject(project_manager);
    XApp::disp_core->registerMember(docs_container);
    XApp::disp_core->registerMember(fragments_model);
    XApp::disp_core->registerMember(boards_model);
    XApp::disp_core->registerMember(chains_model);
    XApp::disp_core->registerMember(concepts_model);
    XApp::disp_core->registerMember(units_model);

    setMinimumSize(1000, 600);
    setWindowTitle("提线木偶");

    project_present->widget()->setVisible(false);
    chains_view->setVisible(false);
    units_view->setVisible(false);
    errors_present->setVisible(false);
    boards_view->setVisible(false);
    concept_view->setVisible(false);
    fragments_order->setVisible(false);

    auto mbar = menuBar();
    initial_menubar(mbar);

    setCentralWidget(this->split_first);
    this->split_first->addWidget(left_funcs->hostWidget());
    this->split_first->addWidget(this->split_second);

    this->split_second->addWidget(this->split_third);
    this->split_second->addWidget(this->bottom_funcs->hostWidget());

    this->split_third->addWidget(this->center_funcs->hostWidget());
    this->split_third->addWidget(this->right_funcs->hostWidget());

    connect(static_cast<PresentContainer *>(left_funcs), &PresentContainer::presentTransTo, this, &MainWindow::accept_view_transport);
    connect(static_cast<PresentContainer *>(right_funcs), &PresentContainer::presentTransTo, this, &MainWindow::accept_view_transport);
    connect(static_cast<PresentContainer *>(bottom_funcs), &PresentContainer::presentTransTo, this, &MainWindow::accept_view_transport);
    connect(static_cast<PresentContainer *>(center_funcs), &PresentContainer::presentTransTo, this, &MainWindow::accept_view_transport);
}

MainWindow::~MainWindow() {}

void MainWindow::initial_menubar(QMenuBar *mbar) {

    // 项目菜单树
    auto project = mbar->addMenu("项目");
    auto opnp = project->addAction("打开项目", [this]() {
        auto file = QFileDialog::getOpenFileName(this, "打开项目", QString(), "小说项目(*.nsf)");
        if (file == "")
            return;

        XApp::disp_core->postCommand(OpenProject(file));
        build_internal(true);
    });
    sync_kernel->actionEnableSync(opnp, [this]() -> bool { return !project_manager->isOpen(); });

    auto newp = project->addAction("新建项目", [this]() {
        auto name = QInputDialog::getText(this, "输入项目名称", "项目名称");
        if (name == "")
            return;
        auto dir_path = QFileDialog::getExistingDirectory(this, "指定项目存储路径");
        if (dir_path == "")
            return;

        XApp::disp_core->postCommand(NewProject(QDir(dir_path), name));
    });
    sync_kernel->actionEnableSync(newp, [this]() -> bool { return !project_manager->isOpen(); });

    auto clsp = project->addAction("关闭项目", [this]() {
        XApp::disp_core->postCommand(CloseProject());
        this->refresh_views();
    });
    sync_kernel->actionEnableSync(clsp, [this]() -> bool { return project_manager->isOpen(); });
    project->addSeparator();

    auto pnew = project->addAction("新建路径", [this]() {
        auto packages = QInputDialog::getText(this, "输入包路径名称/PackA/PackB/PackC", "包路径：");
        if (packages != "")
            XApp::disp_core->postCommand(NewPackage(packages));
    });
    sync_kernel->actionEnableSync(pnew, [this]() -> bool { return project_manager->isOpen(); });

    auto _xnew = project->addMenu("新建文件");
    _xnew->setEnabled(project_manager->isOpen());
    auto types = docs_container->fileTypes();
    for (auto &t : types) {
        _xnew->addAction(t, [this, &t]() {
            auto name = QInputDialog::getText(this, "输入名称", "名称：");
            if (name == "")
                return;

            auto idx = project_present->currentIndex();
            if (!idx.isValid())
                idx = project_manager->model()->item(0)->index();

            auto group_path = docs_container->converter(idx);
            XApp::disp_core->postCommand(NewFile(group_path, name, t));
        });
    }
    sync_kernel->widgetEnableSync(_xnew, [this]() -> bool { return project_manager->isOpen(); });
    project->addSeparator();

    auto sav = project->addAction("保存全部", [this]() { XApp::disp_core->postCommand(SaveAll()); });
    sav->setShortcut(QKeySequence::StandardKey::Save);
    sync_kernel->actionEnableSync(sav, [this]() -> bool { return project_manager->isOpen(); });
    project->addSeparator();

    project->addAction("退出", [this]() {
        XApp::disp_core->postCommand(SaveAll());
        QApplication::exit(0);
    });

    // 编辑菜单
    auto edit = mbar->addMenu("编辑");
    edit->addAction("撤销一步");
    edit->addAction("重做一步");
    edit->addSeparator();
    edit->addAction("查找关键词");
    edit->addAction("替换关键词");

    // 视图菜单
    auto view = mbar->addMenu("视图");
    auto area = view->addMenu("区域管理");

    auto xtool = new QToolBar(this);
    xtool->setVisible(false);
    addToolBar(Qt::ToolBarArea::TopToolBarArea, xtool);
    auto act_tool = area->addAction("工具栏");
    sync_kernel->actionCheckSync(act_tool, [xtool]() -> bool { return xtool->isVisible(); });
    connect(act_tool, &QAction::triggered, [xtool](bool v) { xtool->setVisible(v); });

    auto xstatus = statusBar();
    xstatus->setVisible(false);
    xstatus->addWidget(new QLabel("文本消息", this));
    auto act_status = area->addAction("状态栏");
    sync_kernel->actionCheckSync(act_status, [xstatus]() -> bool { return xstatus->isVisible(); });
    connect(act_status, &QAction::triggered, [xstatus](bool v) { xstatus->setVisible(v); });

    auto act_left = area->addAction("左侧功能区");
    sync_kernel->actionCheckSync(act_left, [this]() -> bool { return this->left_funcs->visibleState(); });
    connect(act_left, &QAction::triggered, [this](bool v) { this->left_funcs->setVisibleState(v); });

    auto act_right = area->addAction("右侧功能区");
    sync_kernel->actionCheckSync(act_right, [this]() -> bool { return this->right_funcs->visibleState(); });
    connect(act_right, &QAction::triggered, [this](bool v) { this->right_funcs->setVisibleState(v); });

    auto act_bottom = area->addAction("底部功能区");
    sync_kernel->actionCheckSync(act_bottom, [this]() -> bool { return this->bottom_funcs->visibleState(); });
    connect(act_bottom, &QAction::triggered, [this](bool v) { this->bottom_funcs->setVisibleState(v); });

    view->addSeparator();
    auto change = view->addMenu("内容切换");
    change->addAction("序列视图");
    change->addAction("编辑视图");
    view->addSeparator();
    auto func = view->addMenu("功能视图");
    sync_kernel->widgetEnableSync(func, [this]() { return this->project_manager->isOpen(); });
    // 项目管理
    {
        auto project_v = func->addAction("项目管理", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, project_present, v)); });
        sync_kernel->actionCheckSync(project_v, [this]() -> bool { return this->project_present->isVisible(); });
    }
    func->addAction("引用统计");
    func->addAction("脉络分蘖");
    // 编译信息
    {
        auto msgs = func->addAction("编译输出", [this](bool v) {
            XApp::disp_core->postCommand(CompVisible(this, this->errors_present, true));
            XApp::disp_core->postCommand(Build(false));
        });
        sync_kernel->actionCheckSync(msgs, [this]() -> bool { return bottom_funcs->instContains(errors_present); });
    }
    // 脉络统计
    {
        auto chain_v = func->addAction("脉络统计", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, this->chains_view, v)); });
        chain_v->setCheckable(true);
        sync_kernel->registerTrigger([this]() -> bool { return right_funcs->instContains(chains_view); },
                                     [chain_v](bool v) {
                                         if (v != chain_v->isChecked())
                                             chain_v->setChecked(v);
                                     });
    }
    // 单元统计
    {
        auto unit_v = func->addAction("单元统计", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, this->units_view, v)); });
        unit_v->setCheckable(true);
        sync_kernel->registerTrigger([this]() -> bool { return right_funcs->instContains(units_view); },
                                     [unit_v](bool v) {
                                         if (v != unit_v->isChecked())
                                             unit_v->setChecked(v);
                                     });
    }
    // 故事统计
    {
        auto board_v = func->addAction("故事统计", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, boards_view, v)); });
        board_v->setCheckable(true);
        sync_kernel->registerTrigger([this]() -> bool { return right_funcs->instContains(boards_view); },
                                     [board_v](bool v) {
                                         if (v != board_v->isChecked())
                                             board_v->setChecked(v);
                                     });
    }
    // 概念统计
    {
        auto concept_v = func->addAction("概念统计", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, concept_view, v)); });
        concept_v->setCheckable(true);
        sync_kernel->registerTrigger([this]() -> bool { return right_funcs->instContains(concept_view); },
                                     [concept_v](bool v) {
                                         if (v != concept_v->isChecked())
                                             concept_v->setChecked(v);
                                     });
    }
    // 情节序列
    {
        auto fragments_v = func->addAction("情节序列", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, fragments_order, v)); });
        fragments_v->setCheckable(true);
        sync_kernel->registerTrigger([this]() -> bool { return right_funcs->instContains(fragments_order); },
                                     [fragments_v](bool v) {
                                         if (v != fragments_v->isChecked())
                                             fragments_v->setChecked(v);
                                     });
    }

    // 工具菜单
    auto tool = mbar->addMenu("工具");
    auto word = tool->addMenu("敏感词检测");
    word->addAction("工具配置");
    word->addAction("敏感词编辑");
    auto vcs = tool->addMenu("版本管理");
    vcs->addAction("工具配置");
    vcs->addAction("启用");
    auto exp = tool->addMenu("导入导出");
    exp->addAction("导入WsOutlines");
    exp->addSeparator();
    exp->addAction("导出TXT大纲");
    exp->addAction("导出Web大纲");
    exp->addAction("导出WsOutlines");
    exp->addSeparator();
    exp->addAction("导出TXT内容");
    auto build = tool->addAction("编译", [this]() { this->build_internal(); });
    sync_kernel->actionEnableSync(build, [this]() -> bool { return project_manager->isOpen(); });

    // 窗口菜单
    auto window = mbar->addMenu("窗口");
    window->addAction("新建窗口");
    window->addAction("关闭窗口");
    window->addSeparator();
    window->addAction("布局管理");
    window->addMenu("布局切换");
    window->addMenu("窗口切换");

    // 系统
    auto sys = mbar->addMenu("系统");
    sys->addAction("软件激活");
    sys->addAction("系统信息");
    sys->addSeparator();
    sys->addAction("关于……");
}

void MainWindow::initial_commandlist(Schedule::CommandsDispatcher *host) {
    host->registerCommand(new Build(true));
    host->registerCommand(new CloseProject());

    host->registerCommand(new CompVisible(this, welcome_list, true));
    host->registerCommand(new CompVisible(this, errors_present, true));
    host->registerCommand(new CompVisible(this, project_present, true));
    host->registerCommand(new CompVisible(this, fragments_order, true));
    host->registerCommand(new CompVisible(this, boards_view, true));
    host->registerCommand(new CompVisible(this, chains_view, true));
    host->registerCommand(new CompVisible(this, concept_view, true));
    host->registerCommand(new CompVisible(this, units_view, true));

    host->registerCommand(new NewFile(Route(), "", ""));
    host->registerCommand(new NewPackage(""));
    host->registerCommand(new NewProject(QDir(), ""));
    host->registerCommand(new OpenFile(Route()));
    host->registerCommand(new OpenProject(""));
    host->registerCommand(new SaveAll());
    host->registerCommand(new StoryboardDetailShow(QList<QString>()));
    host->registerCommand(new StoryboardJumpTo(QList<QString>()));
    host->registerCommand(new StorychainDetailShow(QList<QString>()));
    host->registerCommand(new StorychainJumpTo(QList<QString>()));
    host->registerCommand(new StoryconceptDetailShow(QList<QString>()));
    host->registerCommand(new StoryconceptJumpTo(QList<QString>()));
    host->registerCommand(new StoryunitDetailShow(QList<QString>()));
    host->registerCommand(new StoryunitJumpTo(QList<QString>()));
}

void MainWindow::refresh_views() {
    errors_present->refresh();
    chains_model->refreshTree();
    units_model->refreshTree();
    boards_model->refresh();
    concepts_model->refreshTree();
    fragments_model->refreshTable();
}

void MainWindow::build_internal(bool all_from_disk) {
    XApp::disp_core->postCommand(Build(all_from_disk));
    refresh_views();
}

void MainWindow::splitter_layout_save(const QList<QString> &path_type, const QList<int> &size) {
    auto convert = [](QList<int> sizes) -> QStringList {
        QStringList size_strs;
        for (auto i : sizes)
            size_strs << QString("%1").arg(i);
        return size_strs;
    };
    app_core->globalConfig()->setList(path_type, convert(size));
}

#define LEFT_SIDE "left"
#define RIGHT_SIDE "right"
#define BOTTOM_SIDE "bottom"
#define CENTER_SIDE "center"
void MainWindow::append(PresentBase *ins, const Route &unique) {
    auto cfg = project_manager->configraions();
    auto pos_query = QList<QString>() << "unique-record"
                                      << "split-area"
                                      << "index-hash"
                                      << "store-point";

    auto hash_pos = cfg->getMap(pos_query);
    auto unique_key = unique.links().join("/");
    auto pos = hash_pos[unique_key];

    if (pos == "") {
        pos = CENTER_SIDE;
        hash_pos[unique_key] = pos;
        cfg->setMap(pos_query, hash_pos);
    }

    if (pos == LEFT_SIDE) {
        left_funcs->append(ins, unique);
    } else if (pos == RIGHT_SIDE) {
        right_funcs->append(ins, unique);
    } else if (pos == BOTTOM_SIDE) {
        bottom_funcs->append(ins, unique);
    } else if (pos == CENTER_SIDE) {
        center_funcs->append(ins, unique);
    }
}
void MainWindow::accept_view_transport(const Core::Route &key, Components::GroupType type) {
    auto cfg = project_manager->configraions();
    auto pos_query = QList<QString>() << "unique-record"
                                      << "split-area"
                                      << "index-hash"
                                      << "store-point";

    auto hash_pos = cfg->getMap(pos_query);
    auto unique_key = key.links().join("/");

    if (type == GroupType::LEFT)
        hash_pos[unique_key] = LEFT_SIDE;
    else if (type == GroupType::RIGHT)
        hash_pos[unique_key] = RIGHT_SIDE;
    else if (type == GroupType::BOTTOM)
        hash_pos[unique_key] = BOTTOM_SIDE;
    else
        hash_pos[unique_key] = CENTER_SIDE;
    cfg->setMap(pos_query, hash_pos);

    auto xins = remove(key);
    append(xins, key);
}

bool MainWindow::active(const Route &unique) {
    PresentHost *array[4] = {left_funcs, right_funcs, bottom_funcs, center_funcs};
    for (auto &it : array)
        it->active(unique);

    return true;
}

PresentBase *MainWindow::remove(const Route &unique) {
    PresentHost *array[4] = {left_funcs, right_funcs, bottom_funcs, center_funcs};
    for (auto &it : array) {
        auto ins = it->remove(unique);
        if (ins)
            return ins;
    }
}

bool MainWindow::instContains(MakeTools::PresentBase *ins) const { return false; }

QWidget *MainWindow::hostWidget() const { return (QWidget *)this; }

void MainWindow::setVisibleState(bool state) { QMainWindow::setVisible(state); }

bool MainWindow::visibleState() const { return QMainWindow::isVisible(); }

void MainWindow::hasBeenAccepted(const Core::Route &key) {
    auto cfg = project_manager->configraions();
    auto pos_query = QList<QString>() << "unique-record"
                                      << "split-area"
                                      << "index-hash"
                                      << "store-point";

    auto hash_pos = cfg->getMap(pos_query);
    auto unique_key = key.links().join("/");
    if (left_funcs->active(key)) {
        hash_pos[unique_key] = LEFT_SIDE;
    } else if (right_funcs->active(key)) {
        hash_pos[unique_key] = RIGHT_SIDE;
    } else if (bottom_funcs->active(key)) {
        hash_pos[unique_key] = BOTTOM_SIDE;
    } else {
        hash_pos[unique_key] = CENTER_SIDE;
    }

    cfg->setMap(pos_query, hash_pos);
    docs_container->hasBeenAccepted(key);
}

void MainWindow::hasBeenRemoved(const Core::Route &key) { docs_container->hasBeenRemoved(key); }

void MainWindow::hasBeenClosed(const Core::Route &key) { docs_container->hasBeenClosed(key); }

void MainWindow::closeEvent(QCloseEvent *event) {
    // 关闭事件
    if (project_manager->isOpen()) {
        XApp::disp_core->postCommand(CloseProject());
    }

    QMainWindow::closeEvent(event);
}

bool MainWindow::eventFilter(QObject *watched, QEvent *event) {
    auto ev = event->type();
    switch (ev) {
    case QEvent::Type::MouseButtonPress:
    case QEvent::Type::MouseButtonRelease:
    case QEvent::Type::Wheel:
    case QEvent::Type::MouseMove:
    case QEvent::Type::KeyPress:
    case QEvent::Type::KeyRelease:
        sync_kernel->sync();
        break;
    default:
        break;
    }
    return QMainWindow::eventFilter(watched, event);
}
