#pragma once
#include "lsTextEdit.h"
#include "lsMarkedString.h"
#include "lsCommand.h"

// The kind of a completion entry.
enum class lsCompletionItemKind
{
    Text = 1,
    Method = 2,
    Function = 3,
    Constructor = 4,
    Field = 5,
    Variable = 6,
    Class = 7,
    Interface = 8,
    Module = 9,
    Property = 10,
    Unit = 11,
    Value = 12,
    Enum = 13,
    Keyword = 14,
    Snippet = 15,
    Color = 16,
    File = 17,
    Reference = 18,
    Folder = 19,
    EnumMember = 20,
    Constant = 21,
    Struct = 22,
    Event = 23,
    Operator = 24,
    TypeParameter = 25,
};
MAKE_REFLECT_TYPE_PROXY(lsCompletionItemKind);

// Defines whether the insert text in a completion item should be interpreted as
// plain text or a snippet.
enum class lsInsertTextFormat
{
    // The primary text to be inserted is treated as a plain string.
    PlainText = 1,

    // The primary text to be inserted is treated as a snippet.
    //
    // A snippet can define tab stops and placeholders with `$1`, `$2`
    // and `${3:foo}`. `$0` defines the final tab stop, it defaults to
    // the end of the snippet. Placeholders with equal identifiers are linked,
    // that is typing in one will update others too.
    //
    // See also:
    // https://github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md
    Snippet = 2
};
MAKE_REFLECT_TYPE_PROXY(lsInsertTextFormat);

namespace lsp
{
std::string ToString(lsCompletionItemKind);
std::string ToString(lsInsertTextFormat);
} // namespace lsp
/**
 * The Completion request is sent from the client to the server to compute completion items at a given cursor position.
 * Completion items are presented in the IntelliSense user class. If computing complete completion items is expensive
 * servers can additional provide a handler for the resolve completion item request. This request is send when a
 * completion item is selected in the user class.
 */
struct lsCompletionItem
{

    // The label of this completion item. By default
    // also the text that is inserted when selecting
    // this completion.
    std::string label;

    // The kind of this completion item. Based of the kind
    // an icon is chosen by the editor.
    optional<lsCompletionItemKind> kind;

    // A human-readable string with additional information
    // about this item, like type or symbol information.
    optional<std::string> detail;

    // A human-readable string that represents a doc-comment.
    optional<std::pair<optional<std::string>, optional<MarkupContent>>> documentation;

    /**
   * Indicates if this item is deprecated.
   */
    optional<bool> deprecated;

    /**
   * Select this item when showing.
   *
   * *Note* that only one completion item can be selected and that the
   * tool / client decides which item that is. The rule is that the *first
   * item of those that match best is selected.
   */
    optional<bool> preselect;

    // Internal information to order candidates.
    int relevance = 0;

    // A string that shoud be used when comparing this item
    // with other items. When `falsy` the label is used.
    optional<std::string> sortText;

    // A string that should be used when filtering a set of
    // completion items. When `falsy` the label is used.
    optional<std::string> filterText;

    // A string that should be inserted a document when selecting
    // this completion. When `falsy` the label is used.
    optional<std::string> insertText;

    // The format of the insert text. The format applies to both the `insertText`
    // property and the `newText` property of a provided `textEdit`.
    optional<lsInsertTextFormat> insertTextFormat;

    // An edit which is applied to a document when selecting this completion. When
    // an edit is provided the value of `insertText` is ignored.
    //
    // *Note:* The range of the edit must be a single line range and it must
    // contain the position at which completion has been requested.
    optional<lsTextEdit> textEdit;

    // An optional array of additional text edits that are applied when
    // selecting this completion. Edits must not overlap with the main edit
    // nor with themselves.
    // std::vector<TextEdit> additionalTextEdits;

    // An optional command that is executed *after* inserting this completion.
    // *Note* that additional modifications to the current document should be
    // described with the additionalTextEdits-property. Command command;

    // An data entry field that is preserved on a completion item between
    // a completion and a completion resolve request.
    // data ? : any

    // Use this helper to figure out what content the completion item will insert
    // into the document, as it could live in either |textEdit|, |insertText|, or
    // |label|.
    std::string const& InsertedContent() const;

    std::string DisplayText();
    /**
 * An optional array of additional text edits that are applied when
 * selecting this completion. Edits must not overlap (including the same insert position)
 * with the main edit nor with themselves.
 *
 * Additional text edits should be used to change text unrelated to the current cursor position
 * (for example adding an import statement at the top of the file if the completion item will
 * insert an unqualified type).
 */
    optional<std::vector<lsTextEdit>> additionalTextEdits;

    /**
* An optional set of characters that when pressed while this completion is active will accept it first and
* then type that character. *Note* that all commit characters should have `length=1` and that superfluous
* characters will be ignored.
*/
    optional<std::vector<std::string>> commitCharacters;

    /**
* An optional command that is executed *after* inserting this completion. *Note* that
* additional modifications to the current document should be described with the
* additionalTextEdits-property.
*/
    optional<lsCommandWithAny> command;

    /**
* An data entry field that is preserved on a completion item between a completion and a completion resolve request.
*/
    optional<lsp::Any> data;
    std::string ToString();
    MAKE_SWAP_METHOD(
        lsCompletionItem, label, kind, detail, documentation, sortText, insertText, filterText, insertTextFormat,
        textEdit, deprecated, preselect, additionalTextEdits, commitCharacters, command, data
    );
};

MAKE_REFLECT_STRUCT(
    lsCompletionItem, label, kind, detail, documentation, sortText, insertText, filterText, insertTextFormat, textEdit,
    deprecated, preselect, additionalTextEdits, commitCharacters, command, data
);

struct CompletionList
{
    // This list it not complete. Further typing should result in recomputing
    // this list.
    bool isIncomplete = false;
    // The completion items.
    std::vector<lsCompletionItem> items;

    void swap(CompletionList& arg) noexcept
    {
        items.swap(arg.items);
        std::swap(isIncomplete, arg.isIncomplete);
    }
};
MAKE_REFLECT_STRUCT(CompletionList, isIncomplete, items);
