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

// In-house headers:
#include "MICmnStreamStderr.h"
#include "MICmnLog.h"
#include "MICmnResources.h"
#include "MIDriver.h"

//++ ------------------------------------------------------------------------------------
// Details: CMICmnStreamStderr constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmnStreamStderr::CMICmnStreamStderr(void)
{
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmnStreamStderr destructor.
// Type:    Overridable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmnStreamStderr::~CMICmnStreamStderr(void)
{
    Shutdown();
}

//++ ------------------------------------------------------------------------------------
// Details: Initialize resources for *this stderr stream.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnStreamStderr::Initialize(void)
{
    m_clientUsageRefCnt++;

    if (m_bInitialized)
        return MIstatus::success;

    bool bOk = MIstatus::success;

#ifdef _MSC_VER
// Debugging / I/O issues with client.
// This is only required on Windows if you do not use ::flush(stderr). MI uses
// ::flush(stderr)
// It trys to ensure the process attached to the stderr steam gets ALL the data.
//::setbuf( stderr, NULL );
#endif // _MSC_VER

    m_bInitialized = bOk;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Release resources for *this stderr stream.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnStreamStderr::Shutdown(void)
{
    if (--m_clientUsageRefCnt > 0)
        return MIstatus::success;

    if (!m_bInitialized)
        return MIstatus::success;

    ClrErrorDescription();

    m_bInitialized = false;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Write text data to stderr. Prefix the message with "MI:". The text data does
//          not need to include a carriage line return as this is added to the text. The
//          function also then passes the text data into the CMICmnLog logger.
// Type:    Method.
// Args:    vText       - (R) Text data.
//          vbSendToLog - (R) True = Yes send to the Log file too, false = do not. (Dflt = true)
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnStreamStderr::Write(const CMIUtilString &vText, const bool vbSendToLog /* = true */)
{
    if (vText.length() == 0)
        return MIstatus::failure;

    const CMIUtilString strPrefixed(CMIUtilString::Format("%s: %s", CMIDriver::Instance().GetAppNameShort().c_str(), vText.c_str()));

    return WritePriv(strPrefixed, vText, vbSendToLog);
}

//++ ------------------------------------------------------------------------------------
// Details: Write an LLDB text message to stderr.
//          The text data does not need to include a carriage line return as this is added
//          to the text. The function also then passes the text data into the CMICmnLog
//          logger.
// Type:    Method.
// Args:    vText       - (R) Text data.
//          vbSendToLog - (R) True = Yes send to the Log file too, false = do not. (Dflt = true)
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnStreamStderr::WriteLLDBMsg(const CMIUtilString &vText, const bool vbSendToLog /* = true */)
{
    if (vText.length() == 0)
        return MIstatus::failure;

    const CMIUtilString strPrefixed(CMIUtilString::Format("LLDB: %s", vText.c_str()));

    return WritePriv(vText, strPrefixed, vbSendToLog);
}

//++ ------------------------------------------------------------------------------------
// Details: Write text data to stderr. The text data does not need to
//          include a carriage line return as this is added to the text. The function also
//          then passes the text data into the CMICmnLog logger.
// Type:    Method.
// Args:    vText           - (R) Text data. May be prefixed with MI app's short name.
//          vTxtForLogFile  - (R) Text data.
//          vbSendToLog     - (R) True = Yes send to the Log file too, false = do not. (Dflt = true)
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnStreamStderr::WritePriv(const CMIUtilString &vText, const CMIUtilString &vTxtForLogFile, const bool vbSendToLog /* = true */)
{
    if (vText.length() == 0)
        return MIstatus::failure;

    bool bOk = MIstatus::success;
    {
        // Grab the stderr thread lock while we print
        CMIUtilThreadLock _lock(m_mutex);

        // Send this text to stderr
        const MIint status = ::fputs(vText.c_str(), stderr);
        if (status == EOF)
        {
            const CMIUtilString errMsg(CMIUtilString::Format(MIRSRC(IDS_STDERR_ERR_NOT_ALL_DATA_WRITTEN), vText.c_str()));
            SetErrorDescription(errMsg);
            bOk = MIstatus::failure;
        }
        else
        {
            ::fprintf(stderr, "\n");
            ::fflush(stderr);
        }

        // Send this text to the log
        if (bOk && vbSendToLog)
            bOk &= m_pLog->WriteLog(vTxtForLogFile);
    }

    return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details: Lock the availability of the stream stderr. Other users of *this stream will
//          be stalled until it is available (Unlock()).
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnStreamStderr::Lock(void)
{
    m_mutex.Lock();
    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Release a previously locked stderr.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnStreamStderr::Unlock(void)
{
    m_mutex.Unlock();
    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Take MI Driver text message and send to the stderr stream. Also output to the
//           MI Log file.
// Type:    Static method.
// Args:    vrTxt   - (R) Text.
// Return:  MIstatus::success - Functionality succeeded.
//          MIstatus::failure - Functionality failed.
// Throws:  None.
//--
bool
CMICmnStreamStderr::TextToStderr(const CMIUtilString &vrTxt)
{
    const bool bLock = CMICmnStreamStderr::Instance().Lock();
    const bool bOk = bLock && CMICmnStreamStderr::Instance().Write(vrTxt);
    bLock &&CMICmnStreamStderr::Instance().Unlock();

    return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details: Take an LLDB message and send to the stderr stream. The message is not always
//          an error message. The user has typed a command in to the Eclipse console (by-
//          passing Eclipse) and this is the result message from LLDB back to the user.
//          Also output to the MI Log file.
// Type:    Static method.
// Args:    vrTxt   - (R) Text.
// Return:  MIstatus::success - Functionality succeeded.
//          MIstatus::failure - Functionality failed.
// Throws:  None.
//--
bool
CMICmnStreamStderr::LLDBMsgToConsole(const CMIUtilString &vrTxt)
{
    const bool bLock = CMICmnStreamStderr::Instance().Lock();
    const bool bOk = bLock && CMICmnStreamStderr::Instance().WriteLLDBMsg(vrTxt);
    bLock &&CMICmnStreamStderr::Instance().Unlock();

    return bOk;
}
