// ===================================================================================================================
// Widget Example
// Drawing standard ImGui widgets inside the node body
//
// First, some unsorted notes about which widgets do and don't draw well inside nodes.  Run the examples to see all the allowed widgets.
//
// - Child windows with scrolling doesn't work in the node.  The child window appears in a normal node,
//   and scrolls, but its contents are floating around in the wrong location, and they are not scaled.
//   Note that you can put scrolling child windows into "deferred popups" (see next item).
// - Listboxes and combo-boxes only work in nodes with a work-around: deferring the popup calls until after the node drawing is
//   completed. Look to the popup-demo for an example.
// - Headers and trees work inside the nodes only with hacks.  This is because they attempt to span the "avaialbe width"
//   and the nodes can't tell these widgets how wide it is. The work-around is to set up a fake
//   table with a static column width, then draw your header and tree widgets in that column.
// - Clickable tabs don't work in nodes.  Tabs appear, but you cannot actually change the tab, so they're functionally useless.
// - Editable text areas work, but you have to manually manage disabling the editor shorcuts while typing is detected.
//   Look around for the call to ed::EnableShortcuts() for an example.
// - Most of the cool graph widgets can't be used because they are hard-coded in ImGui to spawn tooltips, which don't work.

# include <imgui.h>
# include <imgui_internal.h>
# include <imgui_node_editor.h>
# include <application.h>

namespace ed = ax::NodeEditor;

# ifdef _MSC_VER
# define portable_strcpy    strcpy_s
# define portable_sprintf   sprintf_s
# else
# define portable_strcpy    strcpy
# define portable_sprintf   sprintf
# endif

struct Example:
    public Application
{
    using Application::Application;

    struct LinkInfo
    {
        ed::LinkId Id;
        ed::PinId  InputId;
        ed::PinId  OutputId;
    };

    void OnStart() override
    {
        ed::Config config;
        config.SettingsFile = "Widgets.json";
        m_Context = ed::CreateEditor(&config);
    }

    void OnStop() override
    {
        ed::DestroyEditor(m_Context);
    }

    void OnFrame(float deltaTime) override
    {
        static bool firstframe = true; // Used to position the nodes on startup
        auto& io = ImGui::GetIO();

        // FPS Counter Ribbon
        ImGui::Text("FPS: %.2f (%.2gms)", io.Framerate, io.Framerate ? 1000.0f / io.Framerate : 0.0f);
        ImGui::Separator();

        // Node Editor Widget
        ed::SetCurrentEditor(m_Context);
        ed::Begin("My Editor", ImVec2(0.0, 0.0f));
            int uniqueId = 1;


            // Basic Widgets Demo  ==============================================================================================
            auto basic_id = uniqueId++;
            ed::BeginNode(basic_id);
            ImGui::Text("Basic Widget Demo");
            ed::BeginPin(uniqueId++, ed::PinKind::Input);
            ImGui::Text("-> In");
            ed::EndPin();
            ImGui::SameLine();
            ImGui::Dummy(ImVec2(250, 0)); // Hacky magic number to space out the output pin.
            ImGui::SameLine();
            ed::BeginPin(uniqueId++, ed::PinKind::Output);
            ImGui::Text("Out ->");
            ed::EndPin();

            // Widget Demo from imgui_demo.cpp...
            // Normal Button
            static int clicked = 0;
            if (ImGui::Button("Button"))
                clicked++;
            if (clicked & 1)
            {
                ImGui::SameLine();
                ImGui::Text("Thanks for clicking me!");
            }

            // Checkbox
            static bool check = true;
            ImGui::Checkbox("checkbox", &check);

            // Radio buttons
            static int e = 0;
            ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine();
            ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine();
            ImGui::RadioButton("radio c", &e, 2);

            // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
            for (int i = 0; i < 7; i++)
            {
                if (i > 0)
                    ImGui::SameLine();
                ImGui::PushID(i);
                ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f));
                ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f));
                ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f));
                ImGui::Button("Click");
                ImGui::PopStyleColor(3);
                ImGui::PopID();
            }

            // Use AlignTextToFramePadding() to align text baseline to the baseline of framed elements (otherwise a Text+SameLine+Button sequence will have the text a little too high by default)
            ImGui::AlignTextToFramePadding();
            ImGui::Text("Hold to repeat:");
            ImGui::SameLine();

            // Arrow buttons with Repeater
            static int counter = 0;
            float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
            ImGui::PushButtonRepeat(true);
            if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; }
            ImGui::SameLine(0.0f, spacing);
            if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; }
            ImGui::PopButtonRepeat();
            ImGui::SameLine();
            ImGui::Text("%d", counter);

            // The input widgets also require you to manually disable the editor shortcuts so the view doesn't fly around.
            // (note that this is a per-frame setting, so it disables it for all text boxes.  I left it here so you could find it!)
            ed::EnableShortcuts(!io.WantTextInput);
            // The input widgets require some guidance on their widths, or else they're very large. (note matching pop at the end).
            ImGui::PushItemWidth(200);
            static char str1[128] = "";
            ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1));

            static float f0 = 0.001f;
            ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f");

            static float f1 = 1.00f, f2 = 0.0067f;
            ImGui::DragFloat("drag float", &f1, 0.005f);
            ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns");
            ImGui::PopItemWidth();

            ed::EndNode();
            if (firstframe)
            {
                ed::SetNodePosition(basic_id, ImVec2(20, 20));
            }

            // Headers and Trees Demo =======================================================================================================
            // TreeNodes and Headers streatch to the entire remaining work area. To put them in nodes what we need to do is to tell
            // ImGui out work area is shorter. We can achieve that right now only by using columns API.
            //
            // Relevent bugs: https://github.com/thedmd/imgui-node-editor/issues/30
            auto header_id = uniqueId++;
            ed::BeginNode(header_id);
                ImGui::Text("Tree Widget Demo");

                // Pins Row
                ed::BeginPin(uniqueId++, ed::PinKind::Input);
                    ImGui::Text("-> In");
                ed::EndPin();
                ImGui::SameLine();
                ImGui::Dummy(ImVec2(35, 0)); //  magic number - Crude & simple way to nudge over the output pin. Consider using layout and springs
                ImGui::SameLine();
                ed::BeginPin(uniqueId++, ed::PinKind::Output);
                    ImGui::Text("Out ->");
                ed::EndPin();

                // Tree column startup -------------------------------------------------------------------
                // Push dummy widget to extend node size. Columns do not do that.
                float width = 135; // bad magic numbers. used to define width of tree widget
                ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
                ImGui::Dummy(ImVec2(width, 0));
                ImGui::PopStyleVar();

                // Start columns, but use only first one.
                ImGui::BeginColumns("##TreeColumns", 2,
                    ImGuiOldColumnFlags_NoBorder |
                    ImGuiOldColumnFlags_NoResize |
                    ImGuiOldColumnFlags_NoPreserveWidths |
                    ImGuiOldColumnFlags_NoForceWithinWindow);

                // Adjust column width to match requested one.
                ImGui::SetColumnWidth(0, width
                    + ImGui::GetStyle().WindowPadding.x
                    + ImGui::GetStyle().ItemSpacing.x);
                // End of tree column startup --------------------------------------------------------------

                // Back to normal ImGui drawing, in our column.
                if (ImGui::CollapsingHeader("Open Header"))
                {
                    ImGui::Text("Hello There");
                    if (ImGui::TreeNode("Open Tree")) {
                        static bool OP1_Bool = false;
                        ImGui::Text("Checked: %s", OP1_Bool ? "true" : "false");
                        ImGui::Checkbox("Option 1", &OP1_Bool);
                        ImGui::TreePop();
                    }
                }
                // Tree Column Shutdown
                ImGui::EndColumns();
            ed::EndNode(); // End of Tree Node Demo

            if (firstframe)
            {
                ed::SetNodePosition(header_id, ImVec2(420, 20));
            }

            // Tool Tip & Pop-up Demo =====================================================================================
            // Tooltips, combo-boxes, drop-down menus need to use a work-around to place the "overlay window" in the canvas.
            // To do this, we must defer the popup calls until after we're done drawing the node material.
            //
            // Relevent bugs:  https://github.com/thedmd/imgui-node-editor/issues/48
            auto popup_id = uniqueId++;
            ed::BeginNode(popup_id);
                ImGui::Text("Tool Tip & Pop-up Demo");
                ed::BeginPin(uniqueId++, ed::PinKind::Input);
                    ImGui::Text("-> In");
                ed::EndPin();
                ImGui::SameLine();
                ImGui::Dummy(ImVec2(85, 0)); // Hacky magic number to space out the output pin.
                ImGui::SameLine();
                ed::BeginPin(uniqueId++, ed::PinKind::Output);
                    ImGui::Text("Out ->");
                ed::EndPin();

                // Tooltip example
                ImGui::Text("Hover over me");
                static bool do_tooltip = false;
                do_tooltip = ImGui::IsItemHovered() ? true : false;
                ImGui::SameLine();
                ImGui::Text("- or me");
                static bool do_adv_tooltip = false;
                do_adv_tooltip = ImGui::IsItemHovered() ? true : false;

                // Use AlignTextToFramePadding() to align text baseline to the baseline of framed elements
                // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default)
                ImGui::AlignTextToFramePadding();
                ImGui::Text("Option:");
                ImGui::SameLine();
                static char popup_text[128] = "Pick one!";
                static bool do_popup = false;
                if (ImGui::Button(popup_text)) {
                    do_popup = true;	// Instead of saying OpenPopup() here, we set this bool, which is used later in the Deferred Pop-up Section
                }
            ed::EndNode();
            if (firstframe) {
                ed::SetNodePosition(popup_id, ImVec2(610, 20));
            }

            // --------------------------------------------------------------------------------------------------
            // Deferred Pop-up Section

            // This entire section needs to be bounded by Suspend/Resume!  These calls pop us out of "node canvas coordinates"
            // and draw the popups in a reasonable screen location.
            ed::Suspend();
            // There is some stately stuff happening here.  You call "open popup" exactly once, and this
            // causes it to stick open for many frames until the user makes a selection in the popup, or clicks off to dismiss.
            // More importantly, this is done inside Suspend(), so it loads the popup with the correct screen coordinates!
            if (do_popup) {
                ImGui::OpenPopup("popup_button"); // Cause openpopup to stick open.
                do_popup = false; // disable bool so that if we click off the popup, it doesn't open the next frame.
            }

            // This is the actual popup Gui drawing section.
            if (ImGui::BeginPopup("popup_button")) {
                // Note: if it weren't for the child window, we would have to PushItemWidth() here to avoid a crash!
                ImGui::TextDisabled("Pick One:");
                ImGui::BeginChild("popup_scroller", ImVec2(100, 100), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
                if (ImGui::Button("Option 1")) {
                    portable_strcpy(popup_text, "Option 1");
                    ImGui::CloseCurrentPopup();  // These calls revoke the popup open state, which was set by OpenPopup above.
                }
                if (ImGui::Button("Option 2")) {
                    portable_strcpy(popup_text, "Option 2");
                    ImGui::CloseCurrentPopup();
                }
                if (ImGui::Button("Option 3")) {
                    portable_strcpy(popup_text, "Option 3");
                    ImGui::CloseCurrentPopup();
                }
                if (ImGui::Button("Option 4")) {
                    portable_strcpy(popup_text, "Option 4");
                    ImGui::CloseCurrentPopup();
                }
                ImGui::EndChild();
                ImGui::EndPopup(); // Note this does not do anything to the popup open/close state. It just terminates the content declaration.
            }

            // Handle the simple tooltip
            if (do_tooltip)
                ImGui::SetTooltip("I am a tooltip");

            // Handle the advanced tooltip
            if (do_adv_tooltip) {
                ImGui::BeginTooltip();
                ImGui::Text("I am a fancy tooltip");
                static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
                ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr));
                ImGui::EndTooltip();
            }

            ed::Resume();
            // End of "Deferred Pop-up section"



            // Plot Widgets =========================================================================================
            // Note: most of these plots can't be used in nodes missing, because they spawn tooltips automatically,
            // so we can't trap them in our deferred pop-up mechanism.  This causes them to fly into a random screen
            // location.
            auto plot_id = uniqueId++;
            ed::BeginNode(plot_id);
                ImGui::Text("Plot Demo");
                ed::BeginPin(uniqueId++, ed::PinKind::Input);
                    ImGui::Text("-> In");
                ed::EndPin();
                ImGui::SameLine();
                ImGui::Dummy(ImVec2(250, 0)); // Hacky magic number to space out the output pin.
                ImGui::SameLine();
                ed::BeginPin(uniqueId++, ed::PinKind::Output);
                    ImGui::Text("Out ->");
                ed::EndPin();

                ImGui::PushItemWidth(300);

                // Animate a simple progress bar
                static float progress = 0.0f, progress_dir = 1.0f;
                progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;
                if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; }
                if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; }


                // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,
                // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.
                ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f));
                ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
                ImGui::Text("Progress Bar");

                float progress_saturated = (progress < 0.0f) ? 0.0f : (progress > 1.0f) ? 1.0f : progress;
                char buf[32];
                portable_sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753);
                ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf);

                ImGui::PopItemWidth();
            ed::EndNode();
            if (firstframe) {
                ed::SetNodePosition(plot_id, ImVec2(850, 20));
            }
            // ==================================================================================================
            // Link Drawing Section

            for (auto& linkInfo : m_Links)
                ed::Link(linkInfo.Id, linkInfo.InputId, linkInfo.OutputId);

            // ==================================================================================================
            // Interaction Handling Section
            // This was coppied from BasicInteration.cpp. See that file for commented code.

            // Handle creation action ---------------------------------------------------------------------------
            if (ed::BeginCreate())
            {
                ed::PinId inputPinId, outputPinId;
                if (ed::QueryNewLink(&inputPinId, &outputPinId))
                {
                    if (inputPinId && outputPinId)
                    {
                        if (ed::AcceptNewItem())
                        {
                            m_Links.push_back({ ed::LinkId(m_NextLinkId++), inputPinId, outputPinId });
                            ed::Link(m_Links.back().Id, m_Links.back().InputId, m_Links.back().OutputId);
                        }
                    }
                }
            }
            ed::EndCreate();

            // Handle deletion action ---------------------------------------------------------------------------
            if (ed::BeginDelete())
            {
                ed::LinkId deletedLinkId;
                while (ed::QueryDeletedLink(&deletedLinkId))
                {
                    if (ed::AcceptDeletedItem())
                    {
                        for (auto& link : m_Links)
                        {
                            if (link.Id == deletedLinkId)
                            {
                                m_Links.erase(&link);
                                break;
                            }
                        }
                    }
                }
            }
            ed::EndDelete();

        ed::End();
        ed::SetCurrentEditor(nullptr);
        firstframe = false;
        //ImGui::ShowMetricsWindow();
        //ImGui::ShowDemoWindow();
    }

    ed::EditorContext* m_Context = nullptr;

    ImVector<LinkInfo>   m_Links;                // List of live links. It is dynamic unless you want to create read-only view over nodes.
    int                  m_NextLinkId = 100;     // Counter to help generate link ids. In real application this will probably based on pointer to user data structure.
};

int Main(int argc, char** argv)
{
    Example exampe("Widgets", argc, argv);

    if (exampe.Create())
        return exampe.Run();

    return 0;
}