//===-- REPL.h --------------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef lldb_REPL_h
#define lldb_REPL_h

// C Includes
// C++ Includes
#include <string>

// Other libraries and framework includes
// Project includes
#include "lldb/Interpreter/OptionGroupFormat.h"
#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
#include "lldb/../../source/Commands/CommandObjectExpression.h"

namespace lldb_private
{
    
class REPL : public IOHandlerDelegate
{
public:
    //----------------------------------------------------------------------
    // See TypeSystem.h for how to add subclasses to this.
    //----------------------------------------------------------------------
    enum LLVMCastKind {
        eKindClang,
        eKindSwift,
        eKindGo,
        kNumKinds
    };
    
    LLVMCastKind getKind() const { return m_kind; }
    
    REPL(LLVMCastKind kind, Target &target);
    
    ~REPL() override;
    
    //------------------------------------------------------------------
    /// Get a REPL with an existing target (or, failing that, a debugger to use), and (optional) extra arguments for the compiler.
    ///
    /// @param[out] error
    ///     If this language is supported but the REPL couldn't be created, this error is populated with the reason.
    ///
    /// @param[in] language
    ///     The language to create a REPL for.
    ///
    /// @param[in] debugger
    ///     If provided, and target is nullptr, the debugger to use when setting up a top-level REPL.
    ///
    /// @param[in] target
    ///     If provided, the target to put the REPL inside.
    ///
    /// @param[in] repl_options
    ///     If provided, additional options for the compiler when parsing REPL expressions.
    ///
    /// @return
    ///     The range of the containing object in the target process.
    //------------------------------------------------------------------
    static lldb::REPLSP
    Create (Error &Error, lldb::LanguageType language, Debugger *debugger, Target *target, const char *repl_options);

    void
    SetFormatOptions (const OptionGroupFormat &options)
    {
        m_format_options = options;
    }
    
    void
    SetValueObjectDisplayOptions (const OptionGroupValueObjectDisplay &options)
    {
        m_varobj_options = options;
    }
    
    void
    SetCommandOptions (const CommandObjectExpression::CommandOptions &options)
    {
        m_command_options = options;
    }
    
    void
    SetCompilerOptions (const char *options)
    {
        if (options)
            m_compiler_options = options;
    }
    
    lldb::IOHandlerSP
    GetIOHandler ();
    
    Error
    RunLoop ();
    
    //------------------------------------------------------------------
    // IOHandler::Delegate functions
    //------------------------------------------------------------------
    void
    IOHandlerActivated (IOHandler &io_handler) override;

    bool
    IOHandlerInterrupt (IOHandler &io_handler) override;
    
    void
    IOHandlerInputInterrupted (IOHandler &io_handler,
                               std::string &line) override;

    const char *
    IOHandlerGetFixIndentationCharacters () override;
    
    ConstString
    IOHandlerGetControlSequence (char ch) override;
    
    const char *
    IOHandlerGetCommandPrefix () override;
    
    const char *
    IOHandlerGetHelpPrologue () override;
    
    bool
    IOHandlerIsInputComplete (IOHandler &io_handler,
                              StringList &lines) override;
    
    int
    IOHandlerFixIndentation (IOHandler &io_handler,
                             const StringList &lines,
                             int cursor_position) override;
    
    void
    IOHandlerInputComplete (IOHandler &io_handler,
                            std::string &line) override;
    
    int
    IOHandlerComplete (IOHandler &io_handler,
                       const char *current_line,
                       const char *cursor,
                       const char *last_char,
                       int skip_first_n_matches,
                       int max_matches,
                       StringList &matches) override;

protected:
    static int
    CalculateActualIndentation (const StringList &lines);

    //----------------------------------------------------------------------
    // Subclasses should override these functions to implement a functional REPL.
    //----------------------------------------------------------------------
    
    virtual Error
    DoInitialization () = 0;
    
    virtual ConstString
    GetSourceFileBasename () = 0;
    
    virtual const char *
    GetAutoIndentCharacters () = 0;
    
    virtual bool
    SourceIsComplete (const std::string &source) = 0;
    
    virtual lldb::offset_t
    GetDesiredIndentation (const StringList &lines,
                           int cursor_position,
                           int tab_size) = 0; // LLDB_INVALID_OFFSET means no change
    
    virtual lldb::LanguageType
    GetLanguage () = 0;
    
    virtual bool
    PrintOneVariable (Debugger &debugger,
                      lldb::StreamFileSP &output_sp,
                      lldb::ValueObjectSP &valobj_sp,
                      ExpressionVariable *var = nullptr) = 0;
    
    virtual int
    CompleteCode(const std::string &current_code,
                 StringList &matches) = 0;
    
    OptionGroupFormat m_format_options = OptionGroupFormat(lldb::eFormatDefault);
    OptionGroupValueObjectDisplay m_varobj_options;
    CommandObjectExpression::CommandOptions m_command_options;
    std::string m_compiler_options;
    
    bool m_enable_auto_indent = true;
    std::string m_indent_str; // Use this string for each level of indentation
    std::string m_current_indent_str;
    uint32_t m_current_indent_level = 0;
    
    std::string m_repl_source_path;
    bool m_dedicated_repl_mode = false;
    
    StringList m_code; // All accumulated REPL statements are saved here
    
    Target &m_target;
    lldb::IOHandlerSP m_io_handler_sp;
    LLVMCastKind m_kind;

private:
    std::string
    GetSourcePath();
};

} // namespace lldb_private

#endif // lldb_REPL_h
