void NodeJSDebugger::OnToggleBreakpoint(clDebugEvent& event)
{
    event.Skip();
    if(NodeJSWorkspace::Get()->IsOpen()) {
        event.Skip(false);
        IEditor* editor = clGetManager()->GetActiveEditor();
        if(editor && (editor->GetFileName().GetFullPath() == event.GetFileName())) {
            // Correct editor
            // add marker
            NodeJSBreakpoint bp = m_bptManager.GetBreakpoint(event.GetFileName(), event.GetInt());
            if(bp.IsOk()) {
                if(bp.IsApplied() && IsConnected()) {
                    // Tell NodeJS to delete this breakpoint
                    DeleteBreakpoint(bp);
                }
                m_bptManager.DeleteBreakpoint(event.GetFileName(), event.GetInt());
            } else {
                // We have no breakpoint on this file/line (yet)
                m_bptManager.AddBreakpoint(event.GetFileName(), event.GetInt());
                bp = m_bptManager.GetBreakpoint(event.GetFileName(), event.GetInt());
                if(IsConnected()) {
                    SetBreakpoint(bp);
                }
            }

            // Update the UI
            m_bptManager.SetBreakpoints(editor);
            clDebugEvent event(wxEVT_NODEJS_DEBUGGER_UPDATE_BREAKPOINTS_VIEW);
            EventNotifier::Get()->AddPendingEvent(event);
        }
    }
}
void NodeJSDevToolsProtocol::SetBreakpoint(clWebSocketClient& socket, const NodeJSBreakpoint& bp)
{
    try {
        JSONItem params = bp.ToJSON("params");
        // Nodejs is using 0 based line numbers, while the editor starts from 1
        params.removeProperty("lineNumber");
        params.addProperty("lineNumber", bp.GetLine() - 1);
        // Send the command
        SendSimpleCommand(socket, "Debugger.setBreakpointByUrl", params);

        // Register a handler to handle this command when it returns
        CommandHandler handler(message_id, [=](const JSONItem& result) {
            wxString breakpointId = result.namedObject("breakpointId").toString();
            NodeJSBreakpoint& b = m_debugger->GetBreakpointsMgr()->GetBreakpoint(bp.GetFilename(), bp.GetLine());
            if(b.IsOk()) {
                b.SetNodeBpID(breakpointId);
                clDebugEvent bpEvent(wxEVT_NODEJS_DEBUGGER_UPDATE_BREAKPOINTS_VIEW);
                EventNotifier::Get()->AddPendingEvent(bpEvent);
            }
        });
        m_waitingReplyCommands.insert({ handler.m_commandID, handler });
    } catch(clSocketException& e) {
        clWARNING() << e.what();
    }
}
void NodeJSBptManager::AddBreakpoint(const wxFileName& filename, int line)
{
    NodeJSBreakpoint::List_t::const_iterator iter =
        std::find_if(m_breakpoints.begin(), m_breakpoints.end(), [&](const NodeJSBreakpoint& bp) {
            if(bp.GetFilename() == filename.GetFullPath() && bp.GetLine() == line) return true;
            return false;
        });
    if(iter == m_breakpoints.end()) {
        // new breakpoint
        NodeJSBreakpoint bp;
        bp.SetFilename(filename.GetFullPath());
        bp.SetLine(line);
        m_breakpoints.push_back(bp);
    }
}
void NodeJSDebugger::DeleteBreakpoint(const NodeJSBreakpoint& bp)
{
    // Sanity
    if(!IsConnected()) return;
    if(!bp.IsApplied()) return;

    // Build the request
    JSONElement request = JSONElement::createObject();
    request.addProperty("type", "request");
    request.addProperty("command", "clearbreakpoint");
    JSONElement args = JSONElement::createObject("arguments");
    request.append(args);
    args.addProperty("breakpoint", bp.GetNodeBpID());

    // Write the command
    m_socket->WriteRequest(request, new NodeJSSetBreakpointHandler(bp));
}
void NodeJSDebugger::SetBreakpoint(const NodeJSBreakpoint& bp)
{
    // Sanity
    if(!IsConnected()) return;

    // Build the request
    JSONElement request = JSONElement::createObject();
    request.addProperty("type", "request");
    request.addProperty("command", "setbreakpoint");
    JSONElement args = JSONElement::createObject("arguments");
    request.append(args);
    args.addProperty("type", "script");
    args.addProperty("target", bp.GetFilename());
    args.addProperty("line", bp.GetLine() - 1);
    args.addProperty("column", 0);

    // Write the command
    m_socket->WriteRequest(request, new NodeJSSetBreakpointHandler(bp));
}
NodeJSWorkspaceUser& NodeJSWorkspaceUser::Load()
{
    wxFileName fn = GetFileName();
    JSON root(fn);
    JSONItem element = root.toElement();

    m_debuggerPort = element.namedObject("m_debuggerPort").toInt(m_debuggerPort);
    m_debuggerHost = element.namedObject("m_debuggerHost").toString(m_debuggerHost);
    m_scriptToExecute = element.namedObject("m_scriptToExecute").toString(m_scriptToExecute);
    m_commandLineArgs = element.namedObject("m_commandLineArgs").toArrayString();
    m_workingDirectory = element.namedObject("m_workingDirectory").toString();

    m_breakpoints.clear();
    JSONItem bpArr = element.namedObject("m_breakpoints");
    int bpcount = bpArr.arraySize();
    for(int i = 0; i < bpcount; ++i) {
        NodeJSBreakpoint bp;
        bp.FromJSON(bpArr.arrayItem(i));
        m_breakpoints.push_back(bp);
    }
    return *this;
}
void NodeJSDevToolsProtocol::DeleteBreakpoint(clWebSocketClient& socket, const NodeJSBreakpoint& bp)
{
    try {
        JSONItem params = JSONItem::createObject("params");
        params.addProperty("breakpointId", bp.GetNodeBpID());
        // Send the command
        SendSimpleCommand(socket, "Debugger.removeBreakpoint", params);

        // Register a handler to handle this command when it returns
        CommandHandler handler(message_id, [=](const JSONItem& result) {
            clDebugEvent bpEvent(wxEVT_NODEJS_DEBUGGER_UPDATE_BREAKPOINTS_VIEW);
            EventNotifier::Get()->AddPendingEvent(bpEvent);
        });
        m_waitingReplyCommands.insert({ handler.m_commandID, handler });
    } catch(clSocketException& e) {
        clWARNING() << e.what();
    }
}