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

//++
// File:		MICmnLogMediumFile.cpp
//
// Overview:	CMICmnLogMediumFile implementation.
//
// Environment:	Compilers:	Visual C++ 12.
//							gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
//				Libraries:	See MIReadmetxt. 
//
// Copyright:	None.
//--

// Include compiler configuration
#include "MICmnConfig.h"

// In-house headers:
#include "MICmnLogMediumFile.h"
#include "MICmnResources.h"

#if defined( _MSC_VER )
	#include "MIUtilSystemWindows.h"
#elif defined( __FreeBSD__ ) || defined( __linux__ )
	#include "MIUtilSystemLinux.h"
#elif defined( __APPLE__ )
	#include "MIUtilSystemOsx.h"
#endif // defined( _MSC_VER )

#include "MIUtilDateTimeStd.h"

//++ ------------------------------------------------------------------------------------
// Details:	CMICmnLogMediumFile constructor.
// Type:	Method.
// Args:	None.
// Return:	None.
// Throws:	None.
//--
CMICmnLogMediumFile::CMICmnLogMediumFile( void )
:	m_constThisMediumName( MIRSRC( IDS_MEDIUMFILE_NAME ) )
,	m_constMediumFileName( "lldb-mi-log.txt" )
,	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 = 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 );

	CMIUtilString strPathName;
	if( CMIUtilSystem().GetLogFilesPath( strPathName ) )
	{
		const CMIUtilString strPath = CMIUtilFileStd().StripOffFileName( strPathName );

		// ToDo: Review this LINUX log file quick fix so not hidden
        // AD: 
        //      Linux was creating a log file here called '.\log.txt'.  The '.' on linux
        //      signifies that this file is 'hidden' and not normally visible.  A quick fix
        //      is to remove the path component all together.  Linux also normally uses '/'
        //      as directory separators, again leading to the problem of the hidden log.
#if defined ( _MSC_VER )
        m_fileNamePath = CMIUtilString::Format( "%s\\%s", strPath.c_str(), m_constMediumFileName.c_str() );
#else
        m_fileNamePath = CMIUtilString::Format( "%s", m_constMediumFileName.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_constMediumFileName;
}

//++ ------------------------------------------------------------------------------------
// 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 MIchar verbosityCode( ConvertLogVerbosityTypeToId( veType ) );
	const CMIUtilString dt( CMIUtilString::Format( "%s %s", m_strDate.c_str(), CMIUtilDateTimeStd().GetTime().c_str() ) );
	
	data = CMIUtilString::Format( "%c,%s,%s", verbosityCode, dt.c_str(), vData.c_str() );
	data = ConvertCr( data );

	// Look for EOL...
	const MIint pos = vData.rfind( strCr );
	if( pos == (MIint) 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.
//--
MIchar CMICmnLogMediumFile::ConvertLogVerbosityTypeToId( const CMICmnLog::ELogVerbosity veType ) const
{
	MIchar 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 MIuint nSizeCmpat( rCrCmpat.size() );
	const MIuint nSize( strCr.size() );
	CMIUtilString strConv( vData );
	MIint pos = strConv.find( strCr );
	while( pos != (MIint) 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 begining.
// 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();
}
