#include "pch.h"
#include "app.h"
#include "node_icon.h"
#include "node_dialog_open.h"
#include "node_text.h"
#include "node_progress_bar.h"
#include "node_dialog_picker.h"

void App::title_update()
{
    static char str[256];
    snprintf(str, 256, "Panodoc: %s%s (%s)", doc_name.c_str(), canvas->m_canvas->m_unsaved ? "*" : "", res_to_string(canvas->m_canvas->m_width).c_str());
    if (auto docname = layout[main_id]->find<NodeText>("txt-docname"))
        docname->set_text(str);
}

void App::init_toolbar_main()
{
	if (auto* button = layout[main_id]->find<NodeButton>("btn-anim"))
    {
        button->on_click = [this, button](Node*) {
            if (canvas)
            {
                //canvas->m_canvas->export_anim();
            }
        };
    }
    if (auto* button = layout[main_id]->find<NodeButton>("btn-open"))
    {
        button->on_click = [this, button](Node*) {
            dialog_open();
        };
    }
    if (auto* button = layout[main_id]->find<NodeButton>("btn-save"))
    {
        button->on_click = [this, button](Node*) {
            dialog_save();
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-undo"))
    {
        button->on_click = [this, button](Node*) {
            ActionManager::undo();
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-redo"))
    {
        button->on_click = [this, button](Node*) {
            ActionManager::redo();
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-clean-memory"))
    {
        button->on_click = [this](Node*) {
            ActionManager::clear();
        };
    }
    if (auto* button = layout[main_id]->find<NodeButton>("btn-clear"))
    {
        button->on_click = [this](Node*) {
            //exit(0); 
            if (canvas)
                canvas->m_canvas->clear({ 0, 0, 0, 0 });
        };
    }
    if (auto* button = layout[main_id]->find<NodeButton>("btn-popup"))
    {
        button->on_click = [this](Node*) {
            msgbox = new NodeMessageBox();
            msgbox->m_manager = &layout;
            msgbox->init();
            layout[main_id]->add_child(msgbox);
            layout[main_id]->update();
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-settings"))
    {
        button->on_click = [this](Node*) {
            settings = new NodeSettings();
            settings->m_manager = &layout;
            settings->init();
            layout[main_id]->add_child(settings);
            layout[main_id]->update();
        };
    }
}

template <class T> std::shared_ptr<T> find_or_create_panel(NodeScroll* panels)
{
    std::shared_ptr<T> ret;
    auto node_find = std::find_if(panels->m_children.begin(), panels->m_children.end(),
        [](const std::shared_ptr<Node>&p) { return (bool)std::dynamic_pointer_cast<T>(p); });
    if (node_find != panels->m_children.end())
    {
        ret = std::static_pointer_cast<T>(*node_find);
    }
    else
    {
        ret = std::make_shared<T>();
        ret->m_manager = panels->m_manager;
        ret->init();
        ret->create();
        ret->loaded();
    }
    return ret;
}

void App::init_sidebar()
{
    sidebar = layout[main_id]->find<NodeBorder>("sidebar");
    panels = layout[main_id]->find<NodeScroll>("panels");
    canvas = layout[main_id]->find<NodeCanvas>("paint-canvas");

    //brushes = layout[main_id]->find<NodePanelBrush>("panel-brush");
    //layers = layout[main_id]->find<NodePanelLayer>("panel-layer");
    //color = layout[main_id]->find<NodePanelColor>("panel-color");
    //stroke = layout[main_id]->find<NodePanelStroke>("panel-stroke");

    //brushes = find_or_create_panel<NodePanelBrush>(panels);
    layers = find_or_create_panel<NodePanelLayer>(panels);
    color = find_or_create_panel<NodePanelColor>(panels);
    stroke = find_or_create_panel<NodePanelStroke>(panels);
    grid = find_or_create_panel<NodePanelGrid>(panels);
    //presets = find_or_create_panel<NodePanelBrushPreset>(panels);

    canvas->m_canvas->on_mode_changed = [this](kCanvasMode prev, kCanvasMode mode) {
    };
    color->on_color_changed = [this](Node* target, glm::vec4 color) {
        Canvas::I->m_current_brush->m_tip_color = color;
    };
    stroke->on_brush_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
        Canvas::I->m_current_brush->load_tip(path, thumb);
        stroke->m_preview->draw_stroke();
    };
    stroke->on_pattern_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
        Canvas::I->m_current_brush->load_pattern(path, thumb);
        stroke->m_preview->draw_stroke();
    };
    stroke->on_dual_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
        Canvas::I->m_current_brush->load_dual(path, thumb);
        stroke->m_preview->draw_stroke();
    };

    layers->on_layer_add = [this](Node*) {
        canvas->m_canvas->layer_add(layers->m_layers.back()->m_label_text.c_str());
        canvas->m_canvas->m_unsaved = true;
        title_update();
    };

    layers->on_layer_duplicate = [this](Node*, int source_index) {
        Canvas::I->layer_add(layers->m_layers.back()->m_label_text.c_str());
        auto& dst = Canvas::I->m_layers.back();
        auto& src = Canvas::I->m_layers[Canvas::I->m_order[source_index]];
        for (int i = 0; i < 6; i++)
        {
            if (!src->m_dirty_face[i])
                continue;
            dst->m_rtt[i].copy(src->m_rtt[i]);
            dst->m_dirty_face[i] = src->m_dirty_face[i];
            dst->m_dirty_box[i] = src->m_dirty_box[i];
            dst->m_opacity = src->m_opacity;
            dst->m_blend_mode = src->m_blend_mode;
            dst->m_alpha_locked = src->m_alpha_locked;
        }
        Canvas::I->m_unsaved = true;
        title_update();
    };

    layers->on_layer_change = [this](Node*, int old_idx, int new_idx) {
        canvas->m_canvas->m_current_layer_idx = canvas->m_canvas->m_order[new_idx];
    };

    layers->on_layer_order = [this](Node*, int old_idx, int new_idx) {
        canvas->m_canvas->layer_order(old_idx, new_idx);
        canvas->m_canvas->m_unsaved = true;
        title_update();
    };

    layers->on_layer_delete = [this](Node*, int idx) {
        canvas->m_canvas->layer_remove(idx);
        canvas->m_canvas->m_unsaved = true;
        title_update();
    };

    layers->on_layer_opacity_changed = [this](Node*, int idx, float value) {
        canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_opacity = value;
        canvas->m_canvas->m_unsaved = true;
        title_update();
    };

    layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) {
        canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_visible = visible;
        canvas->m_canvas->m_unsaved = true;
        title_update();
    };
    
    layers->on_layer_alpha_lock_changed = [this](Node*, int idx, bool locked) {
        canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_alpha_locked = locked;
        canvas->m_canvas->m_unsaved = true;
        title_update();
    };
    
    layers->on_layer_blend_mode_changed = [this](Node*, int idx, int mode) {
        canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_blend_mode = mode;
        canvas->m_canvas->m_unsaved = true;
        title_update();
    };

    layers->on_layer_highlight_changed = [this](Node*, int idx, bool highlight) {
        canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_hightlight = highlight;
    };
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-stroke"))
    {
        button->on_click = [this, button](Node*) {
            panels->get_child_index(stroke.get()) == -1 ? panels->add_child(stroke) : panels->remove_child(stroke.get());
            panels->fix_scroll();
            button->set_active(panels->get_child_index(stroke.get()) != -1);
        };
    }
    //if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-brush"))
    //{
    //    button->on_click = [this, button](Node*) {
    //        panels->get_child_index(brushes.get()) == -1 ? panels->add_child(brushes) : panels->remove_child(brushes.get());
    //        panels->fix_scroll();
    //        button->set_color(panels->get_child_index(brushes.get()) == -1 ? color_button_normal : color_button_hlight);
    //    };
    //}
    //if (auto* button = layout[main_id]->find<NodeButton>("btn-brush-preset"))
    //{
    //    button->on_click = [this, button](Node*) {
    //        panels->get_child_index(presets.get()) == -1 ? panels->add_child(presets) : panels->remove_child(presets.get());
    //        panels->fix_scroll();
    //        button->set_color(panels->get_child_index(presets.get()) == -1 ? color_button_normal : color_button_hlight);
    //    };
    //}
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-color"))
    {
        button->on_click = [this, button](Node*) {
            panels->get_child_index(color.get()) == -1 ? panels->add_child(color) : panels->remove_child(color.get());
            panels->fix_scroll();
            button->set_active(panels->get_child_index(color.get()) != -1);
//             auto pick = layout[main_id]->add_child<NodeColorPicker>();
//             pick->m_color_cur->m_color = Canvas::I->m_current_brush->m_tip_color;
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-layer"))
    {
        button->on_click = [this, button](Node*) {
            panels->get_child_index(layers.get()) == -1 ? panels->add_child(layers) : panels->remove_child(layers.get());
            panels->fix_scroll();
            button->set_active(panels->get_child_index(layers.get()) != -1);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-grids-panel"))
    {
        button->on_click = [this, button](Node*) {
            panels->get_child_index(grid.get()) == -1 ? panels->add_child(grid) : panels->remove_child(grid.get());
            panels->fix_scroll();
            button->set_active(panels->get_child_index(grid.get()) != -1);
        };
    }
}
template<class T>
void select_button(Node* main, T* button) {
    main->find<NodeButtonCustom>("btn-pen")->set_active(false);
    main->find<NodeButtonCustom>("btn-erase")->set_active(false);
    main->find<NodeButtonCustom>("btn-line")->set_active(false);
    main->find<NodeButton>("btn-cam")->set_active(false);
    main->find<NodeButton>("btn-grid")->set_active(false);
    main->find<NodeButton>("btn-copy")->set_active(false);
    main->find<NodeButton>("btn-cut")->set_active(false);
    //main->find<NodeButton>("btn-fill")->set_color(color_button_normal);
    main->find<NodeButtonCustom>("btn-mask-free")->set_active(false);
    main->find<NodeButtonCustom>("btn-mask-line")->set_active(false);
    button->set_active(false);
};

void App::init_toolbar_draw()
{
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-pen"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            Canvas::set_mode(kCanvasMode::Draw);
        };
        //button->set_active(true);
        Canvas::set_mode(kCanvasMode::Draw);
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-pick"))
    {
        button->on_click = [this](Node*) {
            CanvasModePen* mode = (CanvasModePen*)canvas->m_canvas->modes[(int)kCanvasMode::Draw][0];
            if (mode && canvas->m_canvas->m_current_mode == kCanvasMode::Draw)
            {
                mode->m_picking = !mode->m_picking;
            }
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-touchlock"))
    {
        button->on_click = [this](Node*) {
            canvas->m_canvas->m_touch_lock = !canvas->m_canvas->m_touch_lock;
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-erase"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            Canvas::set_mode(kCanvasMode::Erase);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-line"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            Canvas::set_mode(kCanvasMode::Line);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButton>("btn-cam"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            Canvas::set_mode(kCanvasMode::Camera);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButton>("btn-grid"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            Canvas::set_mode(kCanvasMode::Grid);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButton>("btn-copy"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            auto m = static_cast<CanvasModeTransform*>(canvas->m_canvas->modes[(int)kCanvasMode::Copy][0]);
            m->m_action = CanvasModeTransform::ActionType::Copy;
            Canvas::set_mode(kCanvasMode::Copy);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButton>("btn-cut"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            auto m = static_cast<CanvasModeTransform*>(canvas->m_canvas->modes[(int)kCanvasMode::Cut][0]);
            m->m_action = CanvasModeTransform::ActionType::Cut;
            Canvas::set_mode(kCanvasMode::Cut);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButton>("btn-fill"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            Canvas::set_mode(kCanvasMode::Fill);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-mask-free"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            Canvas::set_mode(kCanvasMode::MaskFree);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-mask-line"))
    {
        button->on_click = [this, button](Node*) {
            select_button(layout[main_id], button);
            Canvas::set_mode(kCanvasMode::MaskLine);
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-bucket"))
    {
        button->on_click = [this](Node*) {
            canvas->m_canvas->clear(Canvas::I->m_current_brush->m_tip_color);
        };
    }
}

void App::init_menu_file()
{
    if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-file"))
    {
        menu_file->on_click = [=](Node*) {
            glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
            auto popup = (NodePopupMenu*)layout[const_hash("file-menu")]->m_children[0]->clone();
            popup->update();
            if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
                pos.x = pos.x - popup->m_size.x + menu_file->m_size.x;
            popup->SetPositioning(YGPositionTypeAbsolute);
            popup->SetPosition(pos.x, pos.y);
            layout[main_id]->add_child(popup);
            layout[main_id]->update();
            popup->mouse_capture();
            popup->m_mouse_ignore = false;
            popup->m_flood_events = true;
            popup->m_capture_children = false;

            if (auto b = popup->find<NodeButtonCustom>("file-newdoc"))
                b->on_click = [this, popup](Node*) {
                    dialog_newdoc();
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-import"))
                b->on_click = [this, popup](Node*) {
                    App::I.pick_file({ "JPG", "PNG", "ABR" }, [this](std::string path){
                        std::string name, base, ext;
                        std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)");
                        std::smatch m;
                        if (!std::regex_search(path, m, r))
                            return;
                        base = m[1].str();
                        name = m[2].str();
                        ext = m[3].str();

                        if (str_iequals(ext, "abr"))
                        {
                            std::thread([this,path] { stroke->import_abr(path); }).detach();
                        }
                        else
                        {
                            Image img;
                            async_start();
                            img.load_file(path);
                            if (img.width == img.height / 6 || img.width == img.height * 2)
                            {
                                Canvas::I->import_equirectangular(path);
                            }
                            else
                            {
                                auto m = static_cast<CanvasModeTransform*>(canvas->m_canvas->modes[(int)kCanvasMode::Import][0]);
                                m->m_action = CanvasModeTransform::ActionType::Import;
                                m->m_source_image = std::move(img);
                                Canvas::set_mode(kCanvasMode::Import);
                            }
                            async_redraw();
                            async_end();
                        }
                    });
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-open"))
                b->on_click = [this, popup](Node*) {
                    //dialog_open();
                    App::I.pick_file({"ppi","PPI"}, [this](std::string path){
                       App::I.open_document(path);
                    });
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-browse"))
                b->on_click = [this, popup](Node*) {
                    dialog_browse();
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-save"))
                b->on_click = [this, popup](Node*) {
                    if (Canvas::I->m_newdoc)
                    {
                        dialog_save();
                    }
                    else if(Canvas::I->m_unsaved)
                    {
                        canvas->m_canvas->project_save();
                    }
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-save-as"))
                b->on_click = [this, popup](Node*) {
                    dialog_save();
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-save-ver"))
                b->on_click = [this, popup](Node*) {
                    Canvas::I->m_newdoc ? dialog_save() : dialog_save_ver();
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-export"))
                b->on_click = [this, popup](Node*) {
                    dialog_export(".jpg");
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-export-tick"))
                b->on_click = [this, b, popup](Node*) {
                    glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
                    auto subpopup = (NodePopupMenu*)layout[const_hash("file-submenu-export")]->m_children[0]->clone();
                    subpopup->update();
                    if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
                        pos.x = pos.x - subpopup->m_size.x + b->m_size.x;
                    subpopup->SetPositioning(YGPositionTypeAbsolute);
                    subpopup->SetPosition(pos.x, pos.y);
                    layout[main_id]->add_child(subpopup);
                    layout[main_id]->update();
                    subpopup->mouse_capture();
                    subpopup->m_mouse_ignore = false;
                    subpopup->m_flood_events = true;
                    subpopup->m_capture_children = false;
                    subpopup->find<NodeButtonCustom>("file-submenu-export-png")->on_click = [this, subpopup, popup](Node*) {
                        dialog_export(".png");
                        subpopup->mouse_release();
                        subpopup->destroy();
                        popup->mouse_release();
                        popup->destroy();
                    };
                    subpopup->find<NodeButtonCustom>("file-submenu-export-layers")->on_click = [this, subpopup, popup](Node*) {
                        dialog_export_layers();
                        subpopup->mouse_release();
                        subpopup->destroy();
                        popup->mouse_release();
                        popup->destroy();
                    };
                };
            if (auto b = popup->find<NodeButtonCustom>("file-share"))
                b->on_click = [this, popup](Node*) {
                    share_file(doc_path);
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-resize"))
                b->on_click = [this, popup](Node*) {
                    dialog_resize();
                    popup->mouse_release();
                    popup->destroy();
                };
			if (auto b = popup->find<NodeButtonCustom>("file-export-cubes"))
                b->on_click = [this, popup](Node*) {
                    dialog_export_cubes();
                    popup->mouse_release();
                    popup->destroy();
                };
			if (auto b = popup->find<NodeButtonCustom>("file-cloud-upload"))
                b->on_click = [this, popup](Node*) {
                    cloud_upload();
                    popup->mouse_release();
                    popup->destroy();
                };
            if (auto b = popup->find<NodeButtonCustom>("file-cloud-browse"))
                b->on_click = [this, popup](Node*) {
                    cloud_browse();
                    popup->mouse_release();
                    popup->destroy();
                };
        };
    }
}

void App::init_menu_edit()
{
    if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-edit"))
    {
        menu_file->on_click = [=](Node*) {
            glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
            auto popup = (NodePopupMenu*)layout[const_hash("edit-menu")]->m_children[0]->clone();
            popup->update();
            if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
                pos.x = pos.x - popup->m_size.x + menu_file->m_size.x;
            popup->SetPositioning(YGPositionTypeAbsolute);
            popup->SetPosition(pos.x, pos.y);
            layout[main_id]->add_child(popup);
            layout[main_id]->update();
            popup->mouse_capture();
            popup->m_mouse_ignore = false;
            popup->m_flood_events = true;
            popup->m_capture_children = false;
        };
    }
}

void App::init_menu_timelapse()
{
    if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-timelapse"))
    {
        menu_file->on_click = [=](Node*) {
            glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
            auto popup = (NodePopupMenu*)layout[const_hash("timelapse-menu")]->m_children[0]->clone();
            popup->update();
            if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
                pos.x = pos.x - popup->m_size.x + menu_file->m_size.x;
            popup->SetPositioning(YGPositionTypeAbsolute);
            popup->SetPosition(pos.x, pos.y);
            layout[main_id]->add_child(popup);
            layout[main_id]->update();
            popup->mouse_capture();
            popup->m_mouse_ignore = false;
            popup->m_flood_events = true;
            popup->m_capture_children = false;
            
            if (auto item = popup->find<NodeButtonCustom>("timelapse-start"))
            {
                if (auto text = popup->find<NodeText>("menu-label"))
                {
                    text->set_text(App::I.rec_running ? "Stop Recording" : "Start Recording");
                }
            }

            popup->find<NodeButtonCustom>("timelapse-start")->on_click = [this, popup](Node*) {
                App::I.rec_running ? App::I.rec_stop() : App::I.rec_start();
                popup->mouse_release();
                popup->destroy();
            };

            popup->find<NodeButtonCustom>("timelapse-clear")->on_click = [this, popup](Node*) {
                App::I.rec_clear();
                popup->mouse_release();
                popup->destroy();
            };

            popup->find<NodeButtonCustom>("timelapse-export")->on_click = [this, popup](Node*) {
                popup->mouse_release();
                popup->destroy();
                App::I.rec_export("");
            };
        };
    }
}

void App::init_menu_about()
{
    if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-about"))
    {
        menu_file->on_click = [=](Node*) {
            glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
            auto popup = (NodePopupMenu*)layout[const_hash("about-menu")]->m_children[0]->clone();
            popup->update();
            if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
                pos.x = pos.x - popup->m_size.x + menu_file->m_size.x;
            popup->SetPositioning(YGPositionTypeAbsolute);
            popup->SetPosition(pos.x, pos.y);
            layout[main_id]->add_child(popup);
            layout[main_id]->update();
            popup->mouse_capture();
            popup->m_mouse_ignore = false;
            popup->m_flood_events = true;
            popup->m_capture_children = false;

            popup->find<NodeButtonCustom>("about-app")->on_click = [this, popup](Node*) {
                dialog_about();
                popup->mouse_release();
                popup->destroy();
            };

            popup->find<NodeButtonCustom>("about-doc")->on_click = [this, popup](Node*) {
//                 auto path = Asset::absolute("data/doc/test.pdf");
//                 display_file(path);
                dialog_usermanual();
                popup->mouse_release();
                popup->destroy();
            };

            if (auto item = popup->find<NodeButtonCustom>("about-news"))
            {
                if (auto text = item->find<NodeText>("menu-label"))
                {
                    static char label[128];
                    sprintf(label, "What's new in %d.%d.%d?", g_version_major, g_version_minor, g_version_fix);
                    text->set_text(label);
                }
                item->on_click = [this, popup](Node*) {
                    dialog_changelog();
                    popup->mouse_release();
                    popup->destroy();
                };
            }
            
            if (auto b = popup->find<NodeButtonCustom>("about-crash"))
            {
                b->on_click = [this, popup](Node*) {
                    LOG("crashing");
                    App::I.crash_test();
                    popup->mouse_release();
                    popup->destroy();
                };
            }

            if (auto b = popup->find<NodeButtonCustom>("about-perf"))
            {
                b->on_click = [this, popup](Node*) {
                    LOG("perf");
                    auto start = std::chrono::high_resolution_clock::now();
                    Canvas::I->stroke_start({ 0, 0, 0 }, 0.9f);
                    for (int i = 0; i < 100; i++)
                    {
                        Canvas::I->stroke_update({ 100, 100, 0 }, 0.9f);
                        Canvas::I->stroke_update({ 200, 200, 0 }, 0.9f);
                        Canvas::I->stroke_update({ 200, 100, 0 }, 0.9f);
                        Canvas::I->stroke_update({ 100, 200, 0 }, 0.9f);
                        Canvas::I->stroke_update({ 300, 300, 0 }, 0.9f);
                        Canvas::I->stroke_update({ 200, 500, 0 }, 0.9f);
                        Canvas::I->stroke_update({ 500, 500, 0 }, 0.9f);
                        Canvas::I->stroke_update({ 400, 400, 0 }, 0.9f);
                        Canvas::I->stroke_update({ 0, 200, 0 }, 0.9f);
                        Canvas::I->stroke_update({ 200, 0, 0 }, 0.9f);
                        Canvas::I->stroke_draw();
                    }
                    Canvas::I->stroke_end();
                    auto diff = std::chrono::high_resolution_clock::now() - start;
                    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(diff).count();
                    LOG("%lld ms", ms);
                    static char str[256];
                    sprintf(str, "Time %lld ms", ms);
                    App::I.message_box("Performance test", str);
                    popup->mouse_release();
                    popup->destroy();
                };
            }
        };
    }
}

void App::brush_update()
{
//     brushes->select_brush(canvas->m_brush->id);
//     stroke->set_params(canvas->m_brush);
}

void App::init_menu_layer()
{
    if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-layers"))
    {
        menu_file->on_click = [=](Node*) {
            glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
            auto popup = (NodePopupMenu*)layout[const_hash("layers-menu")]->m_children[0]->clone();
            popup->update();
            if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
                pos.x = pos.x - popup->m_size.x + menu_file->m_size.x;
            popup->SetPositioning(YGPositionTypeAbsolute);
            popup->SetPosition(pos.x, pos.y);
            layout[main_id]->add_child(popup);
            layout[main_id]->update();
            popup->mouse_capture();
            popup->m_mouse_ignore = false;
            popup->m_flood_events = true;
            popup->m_capture_children = false;
            popup->find<NodeButtonCustom>("clear-grids")->on_click = [this, popup](Node*) {
                CanvasModeGrid* mode = (CanvasModeGrid*)Canvas::modes[(int)kCanvasMode::Grid][0];
                mode->clear();
                popup->mouse_release();
                popup->destroy();
            };

            popup->find<NodeButtonCustom>("camera-reset")->on_click = [this, popup](Node*) {
                canvas->reset_camera();
                popup->mouse_release();
                popup->destroy();
            };

            popup->find<NodeButtonCustom>("layer-clear")->on_click = [this, popup](Node*) {
                canvas->m_canvas->clear();
                popup->mouse_release();
                popup->destroy();
            };
            if (layers->m_current_layer)
                popup->find<NodeButtonCustom>("layer-clear")->
                    find<NodeText>("menu-label")->
                    set_text(("Clear Layer " + layers->m_current_layer->m_label_text).c_str());

            popup->find<NodeButtonCustom>("layer-rename")->on_click = [this, popup](Node*) {
                dialog_layer_rename();
                popup->mouse_release();
                popup->destroy();
            };
            if (layers->m_current_layer)
                popup->find<NodeButtonCustom>("layer-rename")->
                    find<NodeText>("menu-label")->
                    set_text(("Rename Layer " + layers->m_current_layer->m_label_text).c_str());
            else
                popup->find<NodeButtonCustom>("layer-rename")->
                    find<NodeText>("menu-label")->
                    set_text("Rename Layer (Select a layer)");

            popup->find<NodeButtonCustom>("layer-merge")->on_click = [this, popup](Node*) {
                const auto& order = canvas->m_canvas->m_order;
                //layers->get_child_index(layers->)
                int current_idx_order = std::distance(order.begin(), std::find(order.begin(), order.end(), canvas->m_canvas->m_current_layer_idx));
                if (current_idx_order > 0)
                {
                    int dest_layer_idx = order[current_idx_order - 1];
                    canvas->m_canvas->layer_merge(canvas->m_canvas->m_current_layer_idx, dest_layer_idx);
                    canvas->m_canvas->layer_remove(current_idx_order);
                    layers->clear();
                    for (auto& i : canvas->m_canvas->m_order)
                        layers->add_layer(canvas->m_canvas->m_layers[i]->m_name.c_str());
                    layers->m_current_layer->m_selected = false;
                    layers->m_current_layer = layers->m_layers[current_idx_order - 1];
                    layers->m_current_layer->m_selected = true;
                    layers->m_current_layer->on_selected(layers->m_current_layer);
                }
                popup->mouse_release();
                popup->destroy();
            };
            if (layers->m_current_layer)
            {
                const auto& order = canvas->m_canvas->m_order;
                int current_idx_order = std::distance(order.begin(), std::find(order.begin(), order.end(), canvas->m_canvas->m_current_layer_idx));
                if (current_idx_order > 0)
                {
                    int down_layer_idx = order[current_idx_order - 1];
                    popup->find<NodeButtonCustom>("layer-merge")->
                        find<NodeText>("menu-label")->
                        set_text(("Merge with " + canvas->m_canvas->m_layers[down_layer_idx]->m_name).c_str());
                }
                else
                {
                    popup->find<NodeButtonCustom>("layer-merge")->
                        find<NodeText>("menu-label")->
                        set_text("Merge Layer (Select upper layers)");
                }
            }
            else
                popup->find<NodeButtonCustom>("layer-merge")->
                find<NodeText>("menu-label")->
                set_text("Merge Layer (Select a layer)");

        };
    }
}

void App::initLayout()
{
    LOG("initializing layout statics");
    NodeBorder::static_init();
    NodeImage::static_init();
    NodeIcon::static_init();
    NodeStrokePreview::static_init();

    layout.on_loaded = [&] {
        LOG("initializing layout updating after load");
        layout[main_id]->update(width, height, zoom);

        LOG("initializing layout components");
 
        init_sidebar();

        canvas->m_canvas->layer_add("Default");
        layers->add_layer("Default");
        
        init_toolbar_draw();
        init_toolbar_main();
        init_menu_file();
        init_menu_edit();
        init_menu_layer();
        init_menu_timelapse();
        init_menu_about();

        // set version string
        if (auto* version_label = layout[main_id]->find<NodeText>("version"))
        {
            version_label->set_text(g_version);
        }

        if (auto x = layout[main_id]->find<NodeBorder>("ext-fbf"))
        {
            x->m_color = ShaderManager::ext_framebuffer_fetch ? glm::vec4(0, 1, 0, 1) : glm::vec4(1, 0, 0, 1);
        }

        brush_update();
        
        // hacky thing to make the toolbar buttons not steal events when moving cursor fast
        if (auto* toolbar = layout[main_id]->find<Node>("toolbar"))
            toolbar->m_flood_events = true;

        NodeImage* n = new NodeImage;
        n->m_path = "data/ui/p-black.png";
        n->m_tex_id = const_hash("data/ui/p-black.png");
        n->SetSize(30, 45);
        n->create();

        NodeButtonCustom* butt = new NodeButtonCustom;
        butt->create();
        butt->add_child(n);
        butt->SetPositioning(YGPositionTypeAbsolute);
        butt->set_color({ 0, 0, 0, 0 });
        //n->SetPosition(100, 100);
        YGNodeStyleSetPosition(butt->y_node, YGEdgeBottom, 8);
        YGNodeStyleSetPosition(butt->y_node, YGEdgeLeft, 15);
        //butt->SetSize(30, 45);
        layout[main_id]->add_child(butt);

        butt->on_click = [this](Node*){
            toggle_ui();
        };

        if (auto* slider = layout[main_id]->find<NodeSliderH>("frames-slider"))
        {
            auto frame_text = layout[main_id]->find<NodeText>("timeline-frame");
            slider->on_value_changed = [this, frame_text](Node*, float value)
            {
                auto& c = *Canvas::I;

                for (int i = 0; i < c.m_layers.size(); i++)
                {
                    auto l = layers->get_layer_at(i);
                    layers->handle_layer_opacity(l, .0f);
                }

                int current_layer = (int)glm::clamp<int>(
                    floor(value * c.m_layers.size()), 1, (int)c.m_layers.size() - 1);
                auto l = layers->get_layer_at(current_layer);
                layers->handle_layer_selected(l);
                layers->handle_layer_opacity(l, 1.f);
                if (current_layer > 0)
                {
                    auto l = layers->get_layer_at(current_layer - 1);
                    layers->handle_layer_opacity(l, .25f);
                }

                // First layer always visible
                {
                    auto l = layers->get_layer_at(0);
                    layers->handle_layer_opacity(l, 1.0f);
                }

                if (frame_text)
                {
                    char str[16];
                    snprintf(str, sizeof(str), "%02d", current_layer);
                    frame_text->set_text(str);
                }
            };
        }

        App::I.redraw = true;
    };
    LOG("initializing layout xml");
    if (layout.m_loaded)
    {
        LOG("restore layout");
        layout.restore_context();
        //if (panels->get_child_index(brushes.get()) == -1) brushes->restore_context();
        if (panels->get_child_index(layers.get()) == -1) layers->restore_context();
        if (panels->get_child_index(color.get()) == -1) color->restore_context();
        if (panels->get_child_index(stroke.get()) == -1) stroke->restore_context();
    }
    else
        layout.load("data/layout.xml");
    LOG("initializing layout completed");
}
