#include "pch.h"
#include "AllShortcutActions.h"
#include "ActionArgs.h"
#include "ActionAndArgs.h"
#include "ActionAndArgs.g.cpp"

#include "JsonUtils.h"
#include "HashUtils.h"

#include <LibraryResources.h>

static constexpr std::string_view AdjustFontSizeKey{ "adjustFontSize" };
static constexpr std::string_view CloseOtherPanesKey{ "closeOtherPanes" };
static constexpr std::string_view CloseOtherTabsKey{ "closeOtherTabs" };
static constexpr std::string_view ClosePaneKey{ "closePane" };
static constexpr std::string_view CloseTabKey{ "closeTab" };
static constexpr std::string_view CloseTabsAfterKey{ "closeTabsAfter" };
static constexpr std::string_view CloseWindowKey{ "closeWindow" };
static constexpr std::string_view CopyTextKey{ "copy" };
static constexpr std::string_view DuplicateTabKey{ "duplicateTab" };
static constexpr std::string_view ExecuteCommandlineKey{ "wt" };
static constexpr std::string_view FindKey{ "find" };
static constexpr std::string_view MoveFocusKey{ "moveFocus" };
static constexpr std::string_view MovePaneKey{ "movePane" };
static constexpr std::string_view SwapPaneKey{ "swapPane" };
static constexpr std::string_view NewTabKey{ "newTab" };
static constexpr std::string_view NextTabKey{ "nextTab" };
static constexpr std::string_view OpenNewTabDropdownKey{ "openNewTabDropdown" };
static constexpr std::string_view OpenSettingsKey{ "openSettings" };
static constexpr std::string_view OpenTabColorPickerKey{ "openTabColorPicker" };
static constexpr std::string_view PasteTextKey{ "paste" };
static constexpr std::string_view PrevTabKey{ "prevTab" };
static constexpr std::string_view RenameTabKey{ "renameTab" };
static constexpr std::string_view OpenTabRenamerKey{ "openTabRenamer" };
static constexpr std::string_view ResetFontSizeKey{ "resetFontSize" };
static constexpr std::string_view ResizePaneKey{ "resizePane" };
static constexpr std::string_view ScrollDownKey{ "scrollDown" };
static constexpr std::string_view ScrollDownPageKey{ "scrollDownPage" };
static constexpr std::string_view ScrollUpKey{ "scrollUp" };
static constexpr std::string_view ScrollUpPageKey{ "scrollUpPage" };
static constexpr std::string_view ScrollToTopKey{ "scrollToTop" };
static constexpr std::string_view ScrollToBottomKey{ "scrollToBottom" };
static constexpr std::string_view ScrollToMarkKey{ "scrollToMark" };
static constexpr std::string_view AddMarkKey{ "addMark" };
static constexpr std::string_view ClearMarkKey{ "clearMark" };
static constexpr std::string_view ClearAllMarksKey{ "clearAllMarks" };
static constexpr std::string_view SendInputKey{ "sendInput" };
static constexpr std::string_view SetColorSchemeKey{ "setColorScheme" };
static constexpr std::string_view SetTabColorKey{ "setTabColor" };
static constexpr std::string_view SplitPaneKey{ "splitPane" };
static constexpr std::string_view SwitchToTabKey{ "switchToTab" };
static constexpr std::string_view TabSearchKey{ "tabSearch" };
static constexpr std::string_view ToggleAlwaysOnTopKey{ "toggleAlwaysOnTop" };
static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" };
static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" };
static constexpr std::string_view SetFocusModeKey{ "setFocusMode" };
static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
static constexpr std::string_view SetFullScreenKey{ "setFullScreen" };
static constexpr std::string_view SetMaximizedKey{ "setMaximized" };
static constexpr std::string_view TogglePaneZoomKey{ "togglePaneZoom" };
static constexpr std::string_view ToggleSplitOrientationKey{ "toggleSplitOrientation" };
static constexpr std::string_view LegacyToggleRetroEffectKey{ "toggleRetroEffect" };
static constexpr std::string_view ToggleShaderEffectsKey{ "toggleShaderEffects" };
static constexpr std::string_view MoveTabKey{ "moveTab" };
static constexpr std::string_view BreakIntoDebuggerKey{ "breakIntoDebugger" };
static constexpr std::string_view FindMatchKey{ "findMatch" };
static constexpr std::string_view TogglePaneReadOnlyKey{ "toggleReadOnlyMode" };
static constexpr std::string_view EnablePaneReadOnlyKey{ "enableReadOnlyMode" };
static constexpr std::string_view DisablePaneReadOnlyKey{ "disableReadOnlyMode" };
static constexpr std::string_view NewWindowKey{ "newWindow" };
static constexpr std::string_view IdentifyWindowKey{ "identifyWindow" };
static constexpr std::string_view IdentifyWindowsKey{ "identifyWindows" };
static constexpr std::string_view RenameWindowKey{ "renameWindow" };
static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
static constexpr std::string_view GlobalSummonKey{ "globalSummon" };
static constexpr std::string_view QuakeModeKey{ "quakeMode" };
static constexpr std::string_view FocusPaneKey{ "focusPane" };
static constexpr std::string_view OpenSystemMenuKey{ "openSystemMenu" };
static constexpr std::string_view ExportBufferKey{ "exportBuffer" };
static constexpr std::string_view ClearBufferKey{ "clearBuffer" };
static constexpr std::string_view MultipleActionsKey{ "multipleActions" };
static constexpr std::string_view QuitKey{ "quit" };
static constexpr std::string_view AdjustOpacityKey{ "adjustOpacity" };
static constexpr std::string_view RestoreLastClosedKey{ "restoreLastClosed" };
static constexpr std::string_view SelectAllKey{ "selectAll" };
static constexpr std::string_view SelectCommandKey{ "selectCommand" };
static constexpr std::string_view SelectOutputKey{ "selectOutput" };
static constexpr std::string_view MarkModeKey{ "markMode" };
static constexpr std::string_view ToggleBlockSelectionKey{ "toggleBlockSelection" };
static constexpr std::string_view SwitchSelectionEndpointKey{ "switchSelectionEndpoint" };
static constexpr std::string_view ColorSelectionKey{ "experimental.colorSelection" };
static constexpr std::string_view ShowContextMenuKey{ "showContextMenu" };
static constexpr std::string_view ExpandSelectionToWordKey{ "expandSelectionToWord" };
static constexpr std::string_view RestartConnectionKey{ "restartConnection" };

static constexpr std::string_view ActionKey{ "action" };

// This key is reserved to remove a keybinding, instead of mapping it to an action.
static constexpr std::string_view UnboundKey{ "unbound" };

#define KEY_TO_ACTION_PAIR(action) { action##Key, ShortcutAction::action },
#define ACTION_TO_KEY_PAIR(action) { ShortcutAction::action, action##Key },
#define ACTION_TO_SERIALIZERS_PAIR(action) { ShortcutAction::action, { action##Args::FromJson, action##Args::ToJson } },

namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
    using namespace ::Microsoft::Terminal::Settings::Model;

    // Specifically use a map here over an unordered_map. We want to be able to
    // iterate over these entries in-order when we're serializing the keybindings.
    // HERE BE DRAGONS:
    // These are string_views that are being used as keys. These string_views are
    // just pointers to other strings. This could be dangerous, if the map outlived
    // the actual strings being pointed to. However, since both these strings and
    // the map are all const for the lifetime of the app, we have nothing to worry
    // about here.
    const std::map<std::string_view, ShortcutAction, std::less<>> ActionAndArgs::ActionKeyNamesMap{
#define ON_ALL_ACTIONS(action) KEY_TO_ACTION_PAIR(action)
        ALL_SHORTCUT_ACTIONS
#undef ON_ALL_ACTIONS
    };

    static const std::map<ShortcutAction, std::string_view, std::less<>> ActionToStringMap{
#define ON_ALL_ACTIONS(action) ACTION_TO_KEY_PAIR(action)
        ALL_SHORTCUT_ACTIONS
#undef ON_ALL_ACTIONS
    };

    using ParseResult = std::tuple<IActionArgs, std::vector<SettingsLoadWarnings>>;
    using ParseActionFunction = std::function<ParseResult(const Json::Value&)>;
    using SerializeActionFunction = std::function<Json::Value(IActionArgs)>;

    // This is a map of ShortcutAction->{function<IActionArgs(Json::Value)>, function<Json::Value(IActionArgs)>. It holds
    // a set of (de)serializer functions that can be used to (de)serialize an IActionArgs
    // from json. Each type of IActionArgs that can accept arbitrary args should be
    // placed into this map, with the corresponding deserializer function as the
    // value.
    static const std::unordered_map<ShortcutAction, std::pair<ParseActionFunction, SerializeActionFunction>> argSerializerMap{

        // These are special cases.
        // - QuakeMode: deserializes into a GlobalSummon, so we don't need a serializer
        // - Invalid: has no args
        { ShortcutAction::QuakeMode, { GlobalSummonArgs::QuakeModeFromJson, nullptr } },
        { ShortcutAction::Invalid, { nullptr, nullptr } },

#define ON_ALL_ACTIONS_WITH_ARGS(action) ACTION_TO_SERIALIZERS_PAIR(action)
        ALL_SHORTCUT_ACTIONS_WITH_ARGS
#undef ON_ALL_ACTIONS_WITH_ARGS
    };

    ActionAndArgs::ActionAndArgs(ShortcutAction action)
    {
        // Find the deserializer
        const auto deserializersIter = argSerializerMap.find(action);
        if (deserializersIter != argSerializerMap.end())
        {
            auto pfn = deserializersIter->second.first;
            if (pfn)
            {
                // Call the deserializer on an empty JSON object.
                // This ensures that we have a valid ActionArgs
                std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> parseWarnings;
                std::tie(_Args, parseWarnings) = pfn({});
            }

            // if an arg parser was registered, but failed,
            // return the invalid ActionAndArgs we started with.
            if (pfn && _Args == nullptr)
            {
                return;
            }
        }

        // Either...
        // (1) we don't have a deserializer, so it's ok for _Args to be null, or
        // (2) we had one AND it worked, so _Args is set up properly
        _Action = action;
    }

    // Function Description:
    // - Attempts to match a string to a ShortcutAction. If there's no match, then
    //   returns ShortcutAction::Invalid
    // Arguments:
    // - actionString: the string to match to a ShortcutAction
    // Return Value:
    // - The ShortcutAction corresponding to the given string, if a match exists.
    static ShortcutAction GetActionFromString(const std::string_view actionString)
    {
        // Try matching the command to one we have. If we can't find the
        // action name in our list of names, let's just unbind that key.
        const auto found = ActionAndArgs::ActionKeyNamesMap.find(actionString);
        return found != ActionAndArgs::ActionKeyNamesMap.end() ? found->second : ShortcutAction::Invalid;
    }

    // Method Description:
    // - Deserialize an ActionAndArgs from the provided json object or string `json`.
    //   * If json is a string, we'll attempt to treat it as an action name,
    //     without arguments.
    //   * If json is an object, we'll attempt to retrieve the action name from
    //     its "action" property, and we'll use that name to find a deserializer
    //     to process the rest of the arguments in the json object.
    // - If the action name is null or "unbound", or we don't understand the
    //   action name, or we failed to parse the arguments to this action, we'll
    //   return null. This should indicate to the caller that the action should
    //   be unbound.
    // - If there were any warnings while parsing arguments for the action,
    //   they'll be appended to the warnings parameter.
    // Arguments:
    // - json: The Json::Value to attempt to parse as an ActionAndArgs
    // - warnings: If there were any warnings during parsing, they'll be
    //   appended to this vector.
    // Return Value:
    // - a deserialized ActionAndArgs corresponding to the values in json, or
    //   an "invalid" action if we failed to deserialize an action.
    winrt::com_ptr<ActionAndArgs> ActionAndArgs::FromJson(const Json::Value& json,
                                                          std::vector<SettingsLoadWarnings>& warnings)
    {
        // Invalid is our placeholder that the action was not parsed.
        auto action = ShortcutAction::Invalid;

        // Actions can be serialized in two styles:
        //   "action": "switchToTab0",
        //   "action": { "action": "switchToTab", "index": 0 },
        // NOTE: For keybindings, the "action" param is actually "command"

        // 1. In the first case, the json is a string, that's the
        //    action name. There are no provided args, so we'll pass
        //    Json::Value::null to the parse function.
        // 2. In the second case, the json is an object. We'll use the
        //    "action" in that object as the action name. We'll then pass
        //    the json object to the arg parser, for further parsing.

        auto argsVal = Json::Value::null;

        // Only try to parse the action if it's actually a string value.
        // `null` will not pass this check.
        if (json.isString())
        {
            auto commandString = json.asString();
            action = GetActionFromString(commandString);
        }
        else if (json.isObject())
        {
            if (const auto actionString{ JsonUtils::GetValueForKey<std::optional<std::string>>(json, ActionKey) })
            {
                action = GetActionFromString(*actionString);
                argsVal = json;
            }
        }

        // Some keybindings can accept other arbitrary arguments. If it
        // does, we'll try to deserialize any "args" that were provided with
        // the binding.
        IActionArgs args{ nullptr };
        std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> parseWarnings;
        const auto deserializersIter = argSerializerMap.find(action);
        if (deserializersIter != argSerializerMap.end())
        {
            auto pfn = deserializersIter->second.first;
            if (pfn)
            {
                std::tie(args, parseWarnings) = pfn(argsVal);
            }
            warnings.insert(warnings.end(), parseWarnings.begin(), parseWarnings.end());

            // if an arg parser was registered, but failed, bail
            if (pfn && args == nullptr)
            {
                return make_self<ActionAndArgs>();
            }
        }

        // Something like
        //      { name: "foo", action: "unbound" }
        // will _remove_ the "foo" command, by returning an "invalid" action here.
        return make_self<ActionAndArgs>(action, args);
    }

    Json::Value ActionAndArgs::ToJson(const Model::ActionAndArgs& val)
    {
        if (val)
        {
            // Search for the ShortcutAction
            const auto shortcutActionIter{ ActionToStringMap.find(val.Action()) };
            if (shortcutActionIter == ActionToStringMap.end())
            {
                // Couldn't find the ShortcutAction,
                // return... "command": "unbound"
                return static_cast<std::string>(UnboundKey);
            }

            if (!val.Args())
            {
                // No args to serialize,
                // output something like... "command": "copy"
                return static_cast<std::string>(shortcutActionIter->second);
            }
            else
            {
                // Serialize any set args,
                // output something like... "command": { "action": "copy", "singleLine": false }
                Json::Value result{ Json::ValueType::objectValue };

                // Set the action args, if any
                const auto actionArgSerializerIter{ argSerializerMap.find(val.Action()) };
                if (actionArgSerializerIter != argSerializerMap.end())
                {
                    auto pfn{ actionArgSerializerIter->second.second };
                    if (pfn)
                    {
                        result = pfn(val.Args());
                    }
                }

                // Set the "action" part
                result[static_cast<std::string>(ActionKey)] = static_cast<std::string>(shortcutActionIter->second);

                return result;
            }
        }

        // "command": "unbound"
        return static_cast<std::string>(UnboundKey);
    }

    com_ptr<ActionAndArgs> ActionAndArgs::Copy() const
    {
        auto copy{ winrt::make_self<ActionAndArgs>() };
        copy->_Action = _Action;
        copy->_Args = _Args ? _Args.Copy() : IActionArgs{ nullptr };
        return copy;
    }

    winrt::hstring ActionAndArgs::GenerateName() const
    {
        // Sentinel used to indicate this command must ALWAYS be generated by GenerateName
        static const winrt::hstring MustGenerate{ L"" };
        // Use a magic static to initialize this map, because we won't be able
        // to load the resources at _init_, only at runtime.
        static const auto GeneratedActionNames = []() {
            return std::unordered_map<ShortcutAction, winrt::hstring>{
                { ShortcutAction::AdjustFontSize, RS_(L"AdjustFontSizeCommandKey") },
                { ShortcutAction::CloseOtherPanes, RS_(L"CloseOtherPanesCommandKey") },
                { ShortcutAction::CloseOtherTabs, MustGenerate },
                { ShortcutAction::ClosePane, RS_(L"ClosePaneCommandKey") },
                { ShortcutAction::CloseTab, MustGenerate },
                { ShortcutAction::CloseTabsAfter, MustGenerate },
                { ShortcutAction::CloseWindow, RS_(L"CloseWindowCommandKey") },
                { ShortcutAction::CopyText, RS_(L"CopyTextCommandKey") },
                { ShortcutAction::DuplicateTab, RS_(L"DuplicateTabCommandKey") },
                { ShortcutAction::ExecuteCommandline, RS_(L"ExecuteCommandlineCommandKey") },
                { ShortcutAction::Find, RS_(L"FindCommandKey") },
                { ShortcutAction::Invalid, MustGenerate },
                { ShortcutAction::MoveFocus, RS_(L"MoveFocusCommandKey") },
                { ShortcutAction::MovePane, RS_(L"MovePaneCommandKey") },
                { ShortcutAction::SwapPane, RS_(L"SwapPaneCommandKey") },
                { ShortcutAction::NewTab, RS_(L"NewTabCommandKey") },
                { ShortcutAction::NextTab, RS_(L"NextTabCommandKey") },
                { ShortcutAction::OpenNewTabDropdown, RS_(L"OpenNewTabDropdownCommandKey") },
                { ShortcutAction::OpenSettings, RS_(L"OpenSettingsUICommandKey") },
                { ShortcutAction::OpenTabColorPicker, RS_(L"OpenTabColorPickerCommandKey") },
                { ShortcutAction::PasteText, RS_(L"PasteTextCommandKey") },
                { ShortcutAction::PrevTab, RS_(L"PrevTabCommandKey") },
                { ShortcutAction::RenameTab, RS_(L"ResetTabNameCommandKey") },
                { ShortcutAction::OpenTabRenamer, RS_(L"OpenTabRenamerCommandKey") },
                { ShortcutAction::ResetFontSize, RS_(L"ResetFontSizeCommandKey") },
                { ShortcutAction::ResizePane, RS_(L"ResizePaneCommandKey") },
                { ShortcutAction::ScrollDown, RS_(L"ScrollDownCommandKey") },
                { ShortcutAction::ScrollDownPage, RS_(L"ScrollDownPageCommandKey") },
                { ShortcutAction::ScrollUp, RS_(L"ScrollUpCommandKey") },
                { ShortcutAction::ScrollUpPage, RS_(L"ScrollUpPageCommandKey") },
                { ShortcutAction::ScrollToTop, RS_(L"ScrollToTopCommandKey") },
                { ShortcutAction::ScrollToBottom, RS_(L"ScrollToBottomCommandKey") },
                { ShortcutAction::ScrollToMark, RS_(L"ScrollToPreviousMarkCommandKey") },
                { ShortcutAction::AddMark, RS_(L"AddMarkCommandKey") },
                { ShortcutAction::ClearMark, RS_(L"ClearMarkCommandKey") },
                { ShortcutAction::ClearAllMarks, RS_(L"ClearAllMarksCommandKey") },
                { ShortcutAction::SendInput, MustGenerate },
                { ShortcutAction::SetColorScheme, MustGenerate },
                { ShortcutAction::SetTabColor, RS_(L"ResetTabColorCommandKey") },
                { ShortcutAction::SplitPane, RS_(L"SplitPaneCommandKey") },
                { ShortcutAction::SwitchToTab, RS_(L"SwitchToTabCommandKey") },
                { ShortcutAction::TabSearch, RS_(L"TabSearchCommandKey") },
                { ShortcutAction::ToggleAlwaysOnTop, RS_(L"ToggleAlwaysOnTopCommandKey") },
                { ShortcutAction::ToggleCommandPalette, MustGenerate },
                { ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") },
                { ShortcutAction::SetFocusMode, MustGenerate },
                { ShortcutAction::ToggleFullscreen, RS_(L"ToggleFullscreenCommandKey") },
                { ShortcutAction::SetFullScreen, MustGenerate },
                { ShortcutAction::SetMaximized, MustGenerate },
                { ShortcutAction::TogglePaneZoom, RS_(L"TogglePaneZoomCommandKey") },
                { ShortcutAction::ToggleSplitOrientation, RS_(L"ToggleSplitOrientationCommandKey") },
                { ShortcutAction::ToggleShaderEffects, RS_(L"ToggleShaderEffectsCommandKey") },
                { ShortcutAction::MoveTab, MustGenerate },
                { ShortcutAction::BreakIntoDebugger, RS_(L"BreakIntoDebuggerCommandKey") },
                { ShortcutAction::FindMatch, MustGenerate },
                { ShortcutAction::TogglePaneReadOnly, RS_(L"TogglePaneReadOnlyCommandKey") },
                { ShortcutAction::EnablePaneReadOnly, RS_(L"EnablePaneReadOnlyCommandKey") },
                { ShortcutAction::DisablePaneReadOnly, RS_(L"DisablePaneReadOnlyCommandKey") },
                { ShortcutAction::NewWindow, RS_(L"NewWindowCommandKey") },
                { ShortcutAction::IdentifyWindow, RS_(L"IdentifyWindowCommandKey") },
                { ShortcutAction::IdentifyWindows, RS_(L"IdentifyWindowsCommandKey") },
                { ShortcutAction::RenameWindow, RS_(L"ResetWindowNameCommandKey") },
                { ShortcutAction::OpenWindowRenamer, RS_(L"OpenWindowRenamerCommandKey") },
                { ShortcutAction::GlobalSummon, MustGenerate },
                { ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
                { ShortcutAction::FocusPane, MustGenerate },
                { ShortcutAction::OpenSystemMenu, RS_(L"OpenSystemMenuCommandKey") },
                { ShortcutAction::ExportBuffer, MustGenerate },
                { ShortcutAction::ClearBuffer, MustGenerate },
                { ShortcutAction::MultipleActions, MustGenerate },
                { ShortcutAction::Quit, RS_(L"QuitCommandKey") },
                { ShortcutAction::AdjustOpacity, MustGenerate },
                { ShortcutAction::RestoreLastClosed, RS_(L"RestoreLastClosedCommandKey") },
                { ShortcutAction::SelectCommand, MustGenerate },
                { ShortcutAction::SelectOutput, MustGenerate },
                { ShortcutAction::SelectAll, RS_(L"SelectAllCommandKey") },
                { ShortcutAction::MarkMode, RS_(L"MarkModeCommandKey") },
                { ShortcutAction::ToggleBlockSelection, RS_(L"ToggleBlockSelectionCommandKey") },
                { ShortcutAction::SwitchSelectionEndpoint, RS_(L"SwitchSelectionEndpointCommandKey") },
                { ShortcutAction::ColorSelection, MustGenerate },
                { ShortcutAction::ShowContextMenu, RS_(L"ShowContextMenuCommandKey") },
                { ShortcutAction::ExpandSelectionToWord, RS_(L"ExpandSelectionToWordCommandKey") },
                { ShortcutAction::RestartConnection, RS_(L"RestartConnectionKey") },
            };
        }();

        if (_Args)
        {
            auto nameFromArgs = _Args.GenerateName();
            if (!nameFromArgs.empty())
            {
                return nameFromArgs;
            }
        }

        const auto found = GeneratedActionNames.find(_Action);
        return found != GeneratedActionNames.end() ? found->second : L"";
    }

    winrt::hstring ActionAndArgs::Serialize(const winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs>& args)
    {
        Json::Value json{ Json::objectValue };
        JsonUtils::SetValueForKey(json, "actions", args);
        Json::StreamWriterBuilder wbuilder;
        auto str = Json::writeString(wbuilder, json);
        return winrt::to_hstring(str);
    }
    winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> ActionAndArgs::Deserialize(winrt::hstring content)
    {
        auto data = winrt::to_string(content);

        std::string errs;
        std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
        Json::Value root;
        if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
        {
            throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
        }

        winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> result{ nullptr };
        JsonUtils::GetValueForKey(root, "actions", result);
        return result;
    }

}
