//----------------------------------------------------------------------------------
// <copyright file="LqrSMACFileSetLogger.cpp" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
//   Implementation of FileSetLogger, a Logger-derived class that implements logging
//   to a file set.
// </summary>
//
// <owner>Mike Cookson</owner>
//----------------------------------------------------------------------------------

#include "LqrSMACFileSetLogger.h"
#include "LqrSMACFileSetLoggerSettings.h"
#include "LqrSMACFileSet.h"
#include "LqrSMACException.h"
#include "LqrSMACPrintfBuffer.h"
#include <sstream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include "nsextend.h"

using namespace std;
using namespace boost;

namespace LiquidRobotics {
namespace SMACFramework {

FileSetLogger::FileSetLogger(FileSet& _fileSet, const char* _processName, LogType _level)
: fileSet(_fileSet)
, level(_level)
, processName(_processName)
{
	hostName = GetHostName();
}

FileSetLogger::FileSetLogger(FileSet& _fileSet, const FileSetLoggerSettings& _settings)
: fileSet(_fileSet)
, level(_settings.GetLogLevel())
, processName(_settings.GetProcessName())
{
	hostName = GetHostName();
}

FileSetLogger::Ptr FileSetLogger::Create(const FileSetSettings& fsSettings, const FileSetLoggerSettings& fslSettings)
{
	Ptr result;
	result.reset(dynamic_cast<FileSetLogger*>(CreateLogger(fsSettings, fslSettings)));
	return result;
}

Logger* FileSetLogger::CreateLogger(const FileSetSettings& fsSettings, const FileSetLoggerSettings& fslSettings)
{
	FileSetLogger* fslp = NULL;
	FSPtr theFileSet;
	theFileSet.reset(new FileSet(fsSettings));
	theFileSet->Init();

	try
	{
		fslp = new FileSetLogger(*theFileSet, fslSettings);
		fslp->fsPtr = theFileSet;
	}
	catch(...)
	{
		theFileSet->Shutdown();
		throw;
	}

	return fslp;
}

FileSetLogger::~FileSetLogger()
{
	if (fsPtr)
		fsPtr->Shutdown();
}

bool FileSetLogger::IsOpen(const LogType logType) const
{
	return (level != LT_NONE) && (logType <= level) && IsConfigured();
}

bool FileSetLogger::IsConfigured() const
{
	return fileSet.Initialized();
}

void FileSetLogger::Write(const char* data)
{
	Lock lock_(*this);			// synchronize this object

	// if the log parameters are reasonable and the data is present and not empty ...
	if (IsConfigured() && data && *data)
		fileSet.Write(string(data));
}

void FileSetLogger::Write(const LogType logType, const char* data)
{
	if (data)
	{
		stringstream sstm;
		sstm << GetLineHeader();

		if (logPrefix.length())
			sstm << " " << logPrefix << ":";

		sstm << " " << data;

		reportSignal(*this, logType, sstm.str());

		if (IsOpen(logType))
			fileSet.WriteLine(sstm.str());
	}
}

void FileSetLogger::Format(const LogType logType, const char* format, ...)
{
	if (IsOpen(logType))
	{
		va_list argptr;

		va_start(argptr, format);
		FormatV(logType, format, argptr);
		va_end(argptr);
	}
}

void FileSetLogger::FormatV(const LogType logType, const char* format, va_list argptr)
{
	if (IsOpen(logType))
		Write(PrintfBuffer(format, argptr).AsPtr());
}

std::string FileSetLogger::GetTimestamp() const
{
	stringstream result;

	timeval tv;
	if (gettimeofday(&tv, NULL) == -1)
		tv.tv_sec = tv.tv_usec = 0;

	tm comp;
	localtime_r(&tv.tv_sec, &comp);

	result << ultos(comp.tm_year+1900,10,4) << "/" << ultos(comp.tm_mon+1,10,2) << "/" << ultos(comp.tm_mday,10,2) << " "
	       << ultos(comp.tm_hour,10,2) << ":" << ultos(comp.tm_min,10,2) << ":" << ultos(comp.tm_sec,10,2)
	       << "." << ultos(tv.tv_usec,10,6);

	string returnValue = result.str();
	return returnValue;
}

std::string FileSetLogger::GetHostName() const
{
	char buffer[128];

	if (gethostname(buffer, sizeof(buffer)-1) == 0)
		return buffer;
	else
		return "-";
}

std::string FileSetLogger::GetLineHeader() const
{
	stringstream sstm;
	sstm << GetTimestamp() << " " << hostName << " " << processName << "[" << getpid() << "]";

	return sstm.str();
}

bool FileSetLogger::LogEnabled(const LogType logType) const
{
	return IsOpen(logType);
}

void FileSetLogger::SetLevel(LogType newLevel)
{
	level = newLevel;
}

}
}
