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

// Overview:    CMICmdCmdGdbExit                implementation.
//              CMICmdCmdListThreadGroups       implementation.
//              CMICmdCmdInterpreterExec        implementation.
//              CMICmdCmdInferiorTtySet         implementation.

// Third Party Headers:
#include "lldb/API/SBCommandInterpreter.h"
#include "lldb/API/SBThread.h"

// In-house headers:
#include "MICmdCmdMiscellanous.h"
#include "MICmnMIResultRecord.h"
#include "MICmnMIValueConst.h"
#include "MICmnMIOutOfBandRecord.h"
#include "MICmnLLDBDebugger.h"
#include "MICmnLLDBDebugSessionInfo.h"
#include "MIDriverBase.h"
#include "MICmdArgValFile.h"
#include "MICmdArgValNumber.h"
#include "MICmdArgValString.h"
#include "MICmdArgValThreadGrp.h"
#include "MICmdArgValOptionLong.h"
#include "MICmdArgValOptionShort.h"
#include "MICmdArgValListOfN.h"
#include "MICmnStreamStdout.h"
#include "MICmnStreamStderr.h"

//++ ------------------------------------------------------------------------------------
// Details: CMICmdCmdGdbExit constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdGdbExit::CMICmdCmdGdbExit()
{
    // Command factory matches this name with that received from the stdin stream
    m_strMiCmd = "gdb-exit";

    // Required by the CMICmdFactory when registering *this command
    m_pSelfCreatorFn = &CMICmdCmdGdbExit::CreateSelf;
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmdCmdGdbExit destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdGdbExit::~CMICmdCmdGdbExit()
{
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The command does work in this function.
//          The command is likely to communicate with the LLDB SBDebugger in here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdGdbExit::Execute()
{
    CMICmnLLDBDebugger::Instance().GetDriver().SetExitApplicationFlag(true);
    const lldb::SBError sbErr = m_rLLDBDebugSessionInfo.GetProcess().Destroy();
    // Do not check for sbErr.Fail() here, m_lldbProcess is likely !IsValid()

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The command prepares a MI Record Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdGdbExit::Acknowledge()
{
    const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Exit);
    m_miResultRecord = miRecordResult;

    // Prod the client i.e. Eclipse with out-of-band results to help it 'continue' because it is using LLDB debugger
    // Give the client '=thread-group-exited,id="i1"'
    m_bHasResultRecordExtra = true;
    const CMICmnMIValueConst miValueConst2("i1");
    const CMICmnMIValueResult miValueResult2("id", miValueConst2);
    const CMICmnMIOutOfBandRecord miOutOfBand(CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited, miValueResult2);
    m_miResultRecordExtra = miOutOfBand.GetString();

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Required by the CMICmdFactory when registering *this command. The factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *
CMICmdCmdGdbExit::CreateSelf()
{
    return new CMICmdCmdGdbExit();
}

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------

//++ ------------------------------------------------------------------------------------
// Details: CMICmdCmdListThreadGroups constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdListThreadGroups::CMICmdCmdListThreadGroups()
    : m_bIsI1(false)
    , m_bHaveArgOption(false)
    , m_bHaveArgRecurse(false)
    , m_constStrArgNamedAvailable("available")
    , m_constStrArgNamedRecurse("recurse")
    , m_constStrArgNamedGroup("group")
    , m_constStrArgNamedThreadGroup("i1")
{
    // Command factory matches this name with that received from the stdin stream
    m_strMiCmd = "list-thread-groups";

    // Required by the CMICmdFactory when registering *this command
    m_pSelfCreatorFn = &CMICmdCmdListThreadGroups::CreateSelf;
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmdCmdListThreadGroups destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdListThreadGroups::~CMICmdCmdListThreadGroups()
{
    m_vecMIValueTuple.clear();
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The parses the command line options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdListThreadGroups::ParseArgs()
{
    m_setCmdArgs.Add(new CMICmdArgValOptionLong(m_constStrArgNamedAvailable, false, true));
    m_setCmdArgs.Add(
        new CMICmdArgValOptionLong(m_constStrArgNamedRecurse, false, true, CMICmdArgValListBase::eArgValType_Number, 1));
    m_setCmdArgs.Add(new CMICmdArgValListOfN(m_constStrArgNamedGroup, false, true, CMICmdArgValListBase::eArgValType_Number));
    m_setCmdArgs.Add(new CMICmdArgValThreadGrp(m_constStrArgNamedThreadGroup, false, true));
    return ParseValidateCmdOptions();
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The command does work in this function.
//          The command is likely to communicate with the LLDB SBDebugger in here.
//          Synopsis: -list-thread-groups [ --available ] [ --recurse 1 ] [ group ... ]
//          This command does not follow the MI documentation exactly. Has an extra
//          argument "i1" to handle.
//          Ref:
// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Miscellaneous-Commands.html#GDB_002fMI-Miscellaneous-Commands
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdListThreadGroups::Execute()
{
    if (m_setCmdArgs.IsArgContextEmpty())
        // No options so "top level thread groups"
        return MIstatus::success;

    CMICMDBASE_GETOPTION(pArgAvailable, OptionLong, m_constStrArgNamedAvailable);
    CMICMDBASE_GETOPTION(pArgRecurse, OptionLong, m_constStrArgNamedRecurse);
    CMICMDBASE_GETOPTION(pArgThreadGroup, ThreadGrp, m_constStrArgNamedThreadGroup);

    // Got some options so "threads"
    if (pArgAvailable->GetFound())
    {
        if (pArgRecurse->GetFound())
        {
            m_bHaveArgRecurse = true;
            return MIstatus::success;
        }

        m_bHaveArgOption = true;
        return MIstatus::success;
    }
    // "i1" as first argument (pos 0 of possible arg)
    if (!pArgThreadGroup->GetFound())
        return MIstatus::success;
    m_bIsI1 = true;

    CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance());
    lldb::SBProcess sbProcess = rSessionInfo.GetProcess();

    // Note do not check for sbProcess is IsValid(), continue

    m_vecMIValueTuple.clear();
    const MIuint nThreads = sbProcess.GetNumThreads();
    for (MIuint i = 0; i < nThreads; i++)
    {
        //  GetThreadAtIndex() uses a base 0 index
        //  GetThreadByIndexID() uses a base 1 index
        lldb::SBThread thread = sbProcess.GetThreadAtIndex(i);

        if (thread.IsValid())
        {
            CMICmnMIValueTuple miTuple;
            if (!rSessionInfo.MIResponseFormThreadInfo(m_cmdData, thread, CMICmnLLDBDebugSessionInfo::eThreadInfoFormat_NoFrames, miTuple))
                return MIstatus::failure;

            m_vecMIValueTuple.push_back(miTuple);
        }
    }

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The command prepares a MI Record Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdListThreadGroups::Acknowledge()
{
    if (m_bHaveArgOption)
    {
        if (m_bHaveArgRecurse)
        {
            const CMICmnMIValueConst miValueConst(MIRSRC(IDS_WORD_NOT_IMPLEMENTED_BRKTS));
            const CMICmnMIValueResult miValueResult("msg", miValueConst);
            const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult);
            m_miResultRecord = miRecordResult;

            return MIstatus::success;
        }

        const CMICmnMIValueConst miValueConst1("i1");
        const CMICmnMIValueResult miValueResult1("id", miValueConst1);
        CMICmnMIValueTuple miTuple(miValueResult1);

        const CMICmnMIValueConst miValueConst2("process");
        const CMICmnMIValueResult miValueResult2("type", miValueConst2);
        miTuple.Add(miValueResult2);

        CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance());
        if (rSessionInfo.GetProcess().IsValid())
        {
            const lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID();
            const CMIUtilString strPid(CMIUtilString::Format("%lld", pid));
            const CMICmnMIValueConst miValueConst3(strPid);
            const CMICmnMIValueResult miValueResult3("pid", miValueConst3);
            miTuple.Add(miValueResult3);
        }

        const CMICmnMIValueConst miValueConst4(MIRSRC(IDS_WORD_NOT_IMPLEMENTED_BRKTS));
        const CMICmnMIValueResult miValueResult4("num_children", miValueConst4);
        miTuple.Add(miValueResult4);

        const CMICmnMIValueConst miValueConst5(MIRSRC(IDS_WORD_NOT_IMPLEMENTED_BRKTS));
        const CMICmnMIValueResult miValueResult5("cores", miValueConst5);
        miTuple.Add(miValueResult5);

        const CMICmnMIValueList miValueList(miTuple);
        const CMICmnMIValueResult miValueResult6("groups", miValueList);
        const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult6);
        m_miResultRecord = miRecordResult;

        return MIstatus::success;
    }

    if (!m_bIsI1)
    {
        const CMICmnMIValueConst miValueConst1("i1");
        const CMICmnMIValueResult miValueResult1("id", miValueConst1);
        CMICmnMIValueTuple miTuple(miValueResult1);

        const CMICmnMIValueConst miValueConst2("process");
        const CMICmnMIValueResult miValueResult2("type", miValueConst2);
        miTuple.Add(miValueResult2);

        CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance());
        if (rSessionInfo.GetProcess().IsValid())
        {
            const lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID();
            const CMIUtilString strPid(CMIUtilString::Format("%lld", pid));
            const CMICmnMIValueConst miValueConst3(strPid);
            const CMICmnMIValueResult miValueResult3("pid", miValueConst3);
            miTuple.Add(miValueResult3);
        }

        if (rSessionInfo.GetTarget().IsValid())
        {
            lldb::SBTarget sbTrgt = rSessionInfo.GetTarget();
            const char *pDir = sbTrgt.GetExecutable().GetDirectory();
            const char *pFileName = sbTrgt.GetExecutable().GetFilename();
            const CMIUtilString strFile(CMIUtilString::Format("%s/%s", pDir, pFileName));
            const CMICmnMIValueConst miValueConst4(strFile);
            const CMICmnMIValueResult miValueResult4("executable", miValueConst4);
            miTuple.Add(miValueResult4);
        }

        const CMICmnMIValueList miValueList(miTuple);
        const CMICmnMIValueResult miValueResult5("groups", miValueList);
        const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult5);
        m_miResultRecord = miRecordResult;
        return MIstatus::success;
    }

    // Build up a list of thread information from tuples
    VecMIValueTuple_t::const_iterator it = m_vecMIValueTuple.begin();
    if (it == m_vecMIValueTuple.end())
    {
        const CMICmnMIValueConst miValueConst("[]");
        const CMICmnMIValueResult miValueResult("threads", miValueConst);
        const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult);
        m_miResultRecord = miRecordResult;
        return MIstatus::success;
    }
    CMICmnMIValueList miValueList(*it);
    ++it;
    while (it != m_vecMIValueTuple.end())
    {
        const CMICmnMIValueTuple &rTuple(*it);
        miValueList.Add(rTuple);

        // Next
        ++it;
    }

    const CMICmnMIValueResult miValueResult("threads", miValueList);
    const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult);
    m_miResultRecord = miRecordResult;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Required by the CMICmdFactory when registering *this command. The factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *
CMICmdCmdListThreadGroups::CreateSelf()
{
    return new CMICmdCmdListThreadGroups();
}

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------

//++ ------------------------------------------------------------------------------------
// Details: CMICmdCmdInterpreterExec constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdInterpreterExec::CMICmdCmdInterpreterExec()
    : m_constStrArgNamedInterpreter("interpreter")
    , m_constStrArgNamedCommand("command")
{
    // Command factory matches this name with that received from the stdin stream
    m_strMiCmd = "interpreter-exec";

    // Required by the CMICmdFactory when registering *this command
    m_pSelfCreatorFn = &CMICmdCmdInterpreterExec::CreateSelf;
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmdCmdInterpreterExec destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdInterpreterExec::~CMICmdCmdInterpreterExec()
{
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The parses the command line options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdInterpreterExec::ParseArgs()
{
    m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgNamedInterpreter, true, true));
    m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgNamedCommand, true, true, true));
    return ParseValidateCmdOptions();
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The command does work in this function.
//          The command is likely to communicate with the LLDB SBDebugger in here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdInterpreterExec::Execute()
{
    CMICMDBASE_GETOPTION(pArgInterpreter, String, m_constStrArgNamedInterpreter);
    CMICMDBASE_GETOPTION(pArgCommand, String, m_constStrArgNamedCommand);

    // Handle the interpreter parameter by do nothing on purpose (set to 'handled' in
    // the arg definition above)
    const CMIUtilString &rStrInterpreter(pArgInterpreter->GetValue());
    MIunused(rStrInterpreter);

    const CMIUtilString &rStrCommand(pArgCommand->GetValue());
    CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance());
    const lldb::ReturnStatus rtn =
        rSessionInfo.GetDebugger().GetCommandInterpreter().HandleCommand(rStrCommand.c_str(), m_lldbResult, true);
    MIunused(rtn);

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The command prepares a MI Record Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdInterpreterExec::Acknowledge()
{
    if (m_lldbResult.GetOutputSize() > 0)
    {
        CMIUtilString strMsg(m_lldbResult.GetOutput());
        strMsg = strMsg.StripCREndOfLine();
        CMICmnStreamStdout::TextToStdout(strMsg);
    }
    if (m_lldbResult.GetErrorSize() > 0)
    {
        CMIUtilString strMsg(m_lldbResult.GetError());
        strMsg = strMsg.StripCREndOfLine();
        CMICmnStreamStderr::LLDBMsgToConsole(strMsg);
    }

    const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done);
    m_miResultRecord = miRecordResult;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Required by the CMICmdFactory when registering *this command. The factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *
CMICmdCmdInterpreterExec::CreateSelf()
{
    return new CMICmdCmdInterpreterExec();
}

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------

//++ ------------------------------------------------------------------------------------
// Details: CMICmdCmdInferiorTtySet constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdInferiorTtySet::CMICmdCmdInferiorTtySet()
{
    // Command factory matches this name with that received from the stdin stream
    m_strMiCmd = "inferior-tty-set";

    // Required by the CMICmdFactory when registering *this command
    m_pSelfCreatorFn = &CMICmdCmdInferiorTtySet::CreateSelf;
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmdCmdInferiorTtySet destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdInferiorTtySet::~CMICmdCmdInferiorTtySet()
{
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The command does work in this function.
//          The command is likely to communicate with the LLDB SBDebugger in here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdInferiorTtySet::Execute()
{
    // Do nothing

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: The invoker requires this function. The command prepares a MI Record Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmdCmdInferiorTtySet::Acknowledge()
{
    const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error);
    m_miResultRecord = miRecordResult;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Required by the CMICmdFactory when registering *this command. The factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *
CMICmdCmdInferiorTtySet::CreateSelf()
{
    return new CMICmdCmdInferiorTtySet();
}
