/*
 * Logger.h
 *
 *  Created on: Mar 29, 2016
 *      Author: dzhou
 */

#ifndef LOGGER_H_
#define LOGGER_H_

#include <sstream>
#include <iomanip>

#include "SmartPointer.h"
#include "LocklessContainer.h"

namespace ddb {

enum class severity_type{DEBUG, INFO, WARNING, ERR};

inline uint16_t shortThreadId() {
#ifdef LINUX
	uint64_t tid = pthread_self();
#else
	uint64_t tid = GetCurrentThreadId();
#endif
	tid = tid ^ (tid >> 16) ^ (tid >> 32) ^ (tid >> 48);
	return tid & 0xffff;
}

class SWORDFISH_API Logger {
public:
	Logger() :  level_(severity_type::INFO){}
	~Logger(){}
	bool start(const string& fileName, long long sizeLimit);
	void stop();
	void setLogLevel(severity_type level) { level_ = level;}
    severity_type getLogLevel() { return level_; }

	template <severity_type level>
	struct SeverityTypeToString;

	template<severity_type severity , typename...Args>
	void print(const Args&...args ) {
		try {
			std::stringstream stream;
			stream << getTime()
				<< std::hex
				<< std::setfill('0')
				<< std::setw(4)
				<< ','
				<< shortThreadId()
				<< std::dec
				<< std::setw(0)
				<< SeverityTypeToString<severity>::value;

			//unpack parameters by initializer list
			//https://en.cppreference.com/w/cpp/language/parameter_pack
			std::initializer_list<int>{(stream << args, 0)...};

			buffer_->push(stream.str());
		} catch (...) {
			// ignore call exceptions, usually OOM
		}
	}

private:
	string getTime();

private:
	severity_type level_;
	SmartPointer<BlockingBoundlessQueue<string>> buffer_;
	ThreadSP thread_;
};

template <> struct Logger::SeverityTypeToString<severity_type::DEBUG> {
	static constexpr const char *const value = " <DEBUG> :";
};
template <> struct Logger::SeverityTypeToString<severity_type::INFO> {
	static constexpr const char *const value = " <INFO> :";
};
template <> struct Logger::SeverityTypeToString<severity_type::WARNING> {
	static constexpr const char *const value = " <WARNING> :";
};
template <> struct Logger::SeverityTypeToString<severity_type::ERR> {
	static constexpr const char *const value = " <ERROR> :";
};

extern SWORDFISH_API Logger log_inst;

#ifdef VERBOSE_LOGGING
#include <cstring>
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define XLOG log_inst.print<severity_type::DEBUG>
#define XLOG_ERR log_inst.print<severity_type::ERR>
#define XLOG_INFO log_inst.print<severity_type::INFO>
#define XLOG_WARN log_inst.print<severity_type::WARNING>

#define LOG(...) XLOG("[", __FILENAME__, ":", __LINE__, "] ", __VA_ARGS__)
#define LOG_ERR(...) XLOG_ERR("[", __FILENAME__, ":", __LINE__, "] ", __VA_ARGS__)
#define LOG_INFO(...) XLOG_INFO("[", __FILENAME__, ":", __LINE__, "] ", __VA_ARGS__)
#define LOG_WARN(...) XLOG_WARN("[", __FILENAME__, ":", __LINE__, "] ", __VA_ARGS__)
#else
#define LOG(...) do { if (log_inst.getLogLevel() <= severity_type::DEBUG) {log_inst.print<severity_type::DEBUG>(__VA_ARGS__);} } while(0)
#define LOG_ERR(...) do { log_inst.print<severity_type::ERR>(__VA_ARGS__); } while(0)
#define LOG_INFO(...) do { if (log_inst.getLogLevel() <= severity_type::INFO) {log_inst.print<severity_type::INFO>(__VA_ARGS__);} } while(0)
#define LOG_WARN(...) do { if (log_inst.getLogLevel() <= severity_type::WARNING) {log_inst.print<severity_type::WARNING>(__VA_ARGS__);} } while(0)
#endif

}

#endif /* LOGGER_H_ */
