//===-- MICmnLogMediumFile.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 "MICmnLogMediumFile.h"
#include "MICmnResources.h"
#if defined(_MSC_VER)
#include "MIUtilSystemWindows.h"
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__linux__)
#include "MIUtilSystemLinux.h"
#elif defined(__APPLE__)
#include "MIUtilSystemOsx.h"
#endif // defined( _MSC_VER )

//++ ------------------------------------------------------------------------------------
// Details: CMICmnLogMediumFile constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmnLogMediumFile::CMICmnLogMediumFile(void)
    : m_constThisMediumName(MIRSRC(IDS_MEDIUMFILE_NAME))
    , m_constMediumFileNameFormat("lldb-mi-%s.log")
    , m_strMediumFileName(MIRSRC(IDS_MEDIUMFILE_ERR_INVALID_PATH))
    , m_strMediumFileDirectory(MIRSRC(IDS_MEDIUMFILE_ERR_INVALID_PATH))
    , m_fileNamePath(MIRSRC(IDS_MEDIUMFILE_ERR_INVALID_PATH))
    , m_eVerbosityType(CMICmnLog::eLogVerbosity_Log)
    , m_strDate(CMIUtilDateTimeStd().GetDate())
    , m_fileHeaderTxt(MIRSRC(IDS_MEDIUMFILE_ERR_FILE_HEADER))
{
}

//++ ------------------------------------------------------------------------------------
// Details: CMICmnLogMediumFile destructor.
// Type:    Overridden.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmnLogMediumFile::~CMICmnLogMediumFile(void)
{
}

//++ ------------------------------------------------------------------------------------
// Details: Get the singleton instance of *this class.
// Type:    Static.
// Args:    None.
// Return:  CMICmnLogMediumFile - Reference to *this object.
// Throws:  None.
//--
CMICmnLogMediumFile &
CMICmnLogMediumFile::Instance(void)
{
    static CMICmnLogMediumFile instance;

    return instance;
}

//++ ------------------------------------------------------------------------------------
// Details: Initialize setup *this medium ready for use.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::Initialize(void)
{
    m_bInitialized = CMIUtilSystem().GetLogFilesPath(m_strMediumFileDirectory);
    m_bInitialized &= FileFormFileNamePath();

    return m_bInitialized;
}

//++ ------------------------------------------------------------------------------------
// Details: Unbind detach or release resources used by *this medium.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::Shutdown(void)
{
    if (m_bInitialized)
    {
        m_bInitialized = false;
        m_file.Close();
    }
    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Retrieve the name of *this medium.
// Type:    Overridden.
// Args:    None.
// Return:  CMIUtilString - Text data.
// Throws:  None.
//--
const CMIUtilString &
CMICmnLogMediumFile::GetName(void) const
{
    return m_constThisMediumName;
}

//++ ------------------------------------------------------------------------------------
// Details: The callee client calls the write function on the Logger. The data to be
//          written is given out to all the mediums registered. The verbosity type parameter
//          indicates to the medium the type of data or message given to it. The medium has
//          modes of verbosity and depending on the verbosity set determines which data is
//          sent to the medium's output.
// Type:    Method.
// Args:    vData       - (R) The data to write to the logger.
//          veType      - (R) Verbosity type.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::Write(const CMIUtilString &vData, const CMICmnLog::ELogVerbosity veType)
{
    if (m_bInitialized && m_file.IsOk())
    {
        const bool bDoWrite = (m_eVerbosityType & veType);
        if (bDoWrite)
        {
            bool bNewCreated = false;
            bool bOk = m_file.CreateWrite(m_fileNamePath, bNewCreated);
            if (bOk)
            {
                if (bNewCreated)
                    bOk = FileWriteHeader();
                bOk = bOk && FileWriteEnglish(MassagedData(vData, veType));
            }
            return bOk;
        }
    }

    return MIstatus::failure;
}

//++ ------------------------------------------------------------------------------------
// Details: Retrieve *this medium's last error condition.
// Type:    Method.
// Args:    None.
// Return:  CString & -  Text description.
// Throws:  None.
//--
const CMIUtilString &
CMICmnLogMediumFile::GetError(void) const
{
    return m_strMILastErrorDescription;
}

//++ ------------------------------------------------------------------------------------
// Details: Set the verbosity mode for this medium.
// Type:    Method.
// Args:    veType  - (R) Mask value.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::SetVerbosity(const MIuint veType)
{
    m_eVerbosityType = veType;
    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Get the verbosity mode for this medium.
// Type:    Method.
// Args:    veType  - (R) Mask value.
// Return:  CMICmnLog::ELogVerbosity - Mask value.
// Throws:  None.
//--
MIuint
CMICmnLogMediumFile::GetVerbosity(void) const
{
    return m_eVerbosityType;
}

//++ ------------------------------------------------------------------------------------
// Details: Write data to a file English font.
// Type:    Method.
// Args:    vData   - (R) The data to write to the logger.
// Return:  None.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::FileWriteEnglish(const CMIUtilString &vData)
{
    return m_file.Write(vData);
}

//++ ------------------------------------------------------------------------------------
// Details: Determine and form the medium file's directory path and name.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::FileFormFileNamePath(void)
{
    ClrErrorDescription();

    m_fileNamePath = MIRSRC(IDS_MEDIUMFILE_ERR_INVALID_PATH);

    if (m_strMediumFileDirectory.compare(MIRSRC(IDS_MEDIUMFILE_ERR_INVALID_PATH)) != 0)
    {
        CMIUtilDateTimeStd date;
        m_strMediumFileName = CMIUtilString::Format(m_constMediumFileNameFormat.c_str(), date.GetDateTimeLogFilename().c_str());

#if defined(_MSC_VER)
        m_fileNamePath = CMIUtilString::Format("%s\\%s", m_strMediumFileDirectory.c_str(), m_strMediumFileName.c_str());
#else
        m_fileNamePath = CMIUtilString::Format("%s/%s", m_strMediumFileDirectory.c_str(), m_strMediumFileName.c_str());
#endif // defined ( _MSC_VER )

        return MIstatus::success;
    }

    SetErrorDescription(MIRSRC(IDE_MEDIUMFILE_ERR_GET_FILE_PATHNAME_SYS));

    return MIstatus::failure;
}

//++ ------------------------------------------------------------------------------------
// Details: Retrieve the medium file's directory path and name.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString & - File path.
// Throws:  None.
//--
const CMIUtilString &
CMICmnLogMediumFile::GetFileNamePath(void) const
{
    return m_fileNamePath;
}

//++ ------------------------------------------------------------------------------------
// Details: Retrieve the medium file's name.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString & - File name.
// Throws:  None.
//--
const CMIUtilString &
CMICmnLogMediumFile::GetFileName(void) const
{
    return m_strMediumFileName;
}

//++ ------------------------------------------------------------------------------------
// Details: Massage the data to behave correct when submitted to file. Insert extra log
//          specific text. The veType is there to allow in the future to parse the log and
//          filter in out specific types of message to make viewing the log more manageable.
// Type:    Method.
// Args:    vData   - (R) Raw data.
//          veType  - (R) Message type.
// Return:  CMIUtilString - Massaged data.
// Throws:  None.
//--
CMIUtilString
CMICmnLogMediumFile::MassagedData(const CMIUtilString &vData, const CMICmnLog::ELogVerbosity veType)
{
    const CMIUtilString strCr("\n");
    CMIUtilString data;
    const char verbosityCode(ConvertLogVerbosityTypeToId(veType));
    const CMIUtilString dt(CMIUtilString::Format("%s %s", m_strDate.c_str(), m_dateTime.GetTime().c_str()));

    data = CMIUtilString::Format("%c,%s,%s", verbosityCode, dt.c_str(), vData.c_str());
    data = ConvertCr(data);

    // Look for EOL...
    const size_t pos = vData.rfind(strCr);
    if (pos == vData.size())
        return data;

    // ... did not have an EOL so add one
    data += GetLineReturn();

    return data;
}

//++ ------------------------------------------------------------------------------------
// Details: Convert the Log's verbosity type number into a single char character.
// Type:    Method.
// Args:    veType  - (R) Message type.
// Return:  wchar_t - A letter.
// Throws:  None.
//--
char
CMICmnLogMediumFile::ConvertLogVerbosityTypeToId(const CMICmnLog::ELogVerbosity veType) const
{
    char c = 0;
    if (veType != 0)
    {
        MIuint cnt = 0;
        MIuint number(veType);
        while (1 != number)
        {
            number = number >> 1;
            ++cnt;
        }
        c = 'A' + cnt;
    }
    else
    {
        c = '*';
    }

    return c;
}

//++ ------------------------------------------------------------------------------------
// Details: Retrieve state of whether the file medium is ok.
// Type:    Method.
// Args:    None.
// Return:  True - file ok.
//          False - file has a problem.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::IsOk(void) const
{
    return m_file.IsOk();
}

//++ ------------------------------------------------------------------------------------
// Details: Status on the file log medium existing already.
// Type:    Method.
// Args:    None.
// Return:  True - Exists.
//          False - Not found.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::IsFileExist(void) const
{
    return m_file.IsFileExist(GetFileNamePath());
}

//++ ------------------------------------------------------------------------------------
// Details: Write the header text the logger file.
// Type:    Method.
// Args:    vText   - (R) Text.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::FileWriteHeader(void)
{
    return FileWriteEnglish(ConvertCr(m_fileHeaderTxt));
}

//++ ------------------------------------------------------------------------------------
// Details: Convert any carriage line returns to be compatible with the platform the
//          Log fiel is being written to.
// Type:    Method.
// Args:    vData   - (R) Text data.
// Return:  CMIUtilString - Converted string data.
// Throws:  None.
//--
CMIUtilString
CMICmnLogMediumFile::ConvertCr(const CMIUtilString &vData) const
{
    const CMIUtilString strCr("\n");
    const CMIUtilString &rCrCmpat(GetLineReturn());

    if (strCr == rCrCmpat)
        return vData;

    const size_t nSizeCmpat(rCrCmpat.size());
    const size_t nSize(strCr.size());
    CMIUtilString strConv(vData);
    size_t pos = strConv.find(strCr);
    while (pos != CMIUtilString::npos)
    {
        strConv.replace(pos, nSize, rCrCmpat);
        pos = strConv.find(strCr, pos + nSizeCmpat);
    }

    return strConv;
}

//++ ------------------------------------------------------------------------------------
// Details: Set the header text that is written to the logger file at the beginning.
// Type:    Method.
// Args:    vText   - (R) Text.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::SetHeaderTxt(const CMIUtilString &vText)
{
    m_fileHeaderTxt = vText;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Retrieve the file current carriage line return characters used.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString & - Text.
// Throws:  None.
//--
const CMIUtilString &
CMICmnLogMediumFile::GetLineReturn(void) const
{
    return m_file.GetLineReturn();
}

//++ ------------------------------------------------------------------------------------
// Details: Set the directory to place the log file.
// Type:    Method.
// Args:    vPath   - (R) Path to log.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMICmnLogMediumFile::SetDirectory(const CMIUtilString &vPath)
{
    m_strMediumFileDirectory = vPath;

    return FileFormFileNamePath();
}
