/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <log4cxx/log4cxx.h>
/* Prevent std::basic_streambuf etc destructor ... already defined in logstream.obj */
#if defined(_MSC_VER) && LOG4CXX_UNICHAR_API
#define __FORCE_INSTANCE
#endif
#include <log4cxx/logstring.h>
#include <log4cxx/stream.h>
#include <log4cxx/helpers/transcoder.h>
#if !defined(LOG4CXX)
	#define LOG4CXX 1
#endif
#include <log4cxx/private/log4cxx_private.h>

using namespace LOG4CXX_NS;

logstream_base::logstream_ios_base::logstream_ios_base(std::ios_base::fmtflags initval,
	int initsize)
{
#if LOG4CXX_MEMSET_IOS_BASE
	//
	//    the destructor for std::ios_base in the MSVC STL
	//        releases a pointer that was not initialized in the constructor.
	//
	memset(this, 0, sizeof(*this));
#endif
#if LOG4CXX_INIT_IOS_BASE
	init(NULL);
#endif
	flags(initval);
	precision(initsize);
	width(initsize);

}


logstream_base::logstream_base(const LoggerPtr& log,
	const LevelPtr& lvl) : initset((std::ios_base::fmtflags) - 1, 1),
	initclear((std::ios_base::fmtflags) 0, 0), fillchar(0), fillset(false), logger(log), level(lvl), location()
{
	enabled = logger->isEnabledFor(level);
}

logstream_base::~logstream_base()
{
}

void logstream_base::insert(std::ios_base & (*manip)(std::ios_base&))
{
	get_stream_state(initclear, initset, fillchar, fillset);
	(*manip)(initset);
	(*manip)(initclear);
	refresh_stream_state();
}

bool logstream_base::set_stream_state(std::ios_base& dest, int& dstchar)
{
	std::ios_base::fmtflags setval = initset.flags();
	std::ios_base::fmtflags clrval = initclear.flags();
	std::ios_base::fmtflags mask = setval ^ (~clrval);
	dest.setf(clrval, mask);

	if (initset.precision() == initclear.precision())
	{
		dest.precision(initset.precision());
	}

	if (initset.width() == initclear.width())
	{
		dest.width(initset.width());
	}

	dstchar = fillchar;
	return fillset;
}

logstream_base& logstream_base::endmsg(logstream_base& stream)
{
	stream.end_message();
	return stream;
}

logstream_base& logstream_base::nop(logstream_base& stream)
{
	return stream;
}

void logstream_base::end_message()
{
	if (isEnabled())
	{
		log(logger, level, location);
	}

	erase();
}



int LOG4CXX_NS::logstream_base::precision(int p)
{
	get_stream_state(initclear, initset, fillchar, fillset);
	initset.precision(p);
	int oldVal = (int)initclear.precision(p);
	refresh_stream_state();
	return oldVal;
}

int LOG4CXX_NS::logstream_base::precision()
{
	get_stream_state(initclear, initset, fillchar, fillset);
	return (int)initclear.precision();
}

int LOG4CXX_NS::logstream_base::width(int w)
{
	get_stream_state(initclear, initset, fillchar, fillset);
	initset.width(w);
	int oldVal = (int)initclear.width(w);
	refresh_stream_state();
	return oldVal;
}

int LOG4CXX_NS::logstream_base::width()
{
	get_stream_state(initclear, initset, fillchar, fillset);
	return (int)initclear.width();
}

int LOG4CXX_NS::logstream_base::fill(int newfill)
{
	get_stream_state(initclear, initset, fillchar, fillset);
	int oldfill = fillchar;
	fillchar = newfill;
	fillset = true;
	refresh_stream_state();
	return oldfill;
}

int logstream_base::fill()
{
	get_stream_state(initclear, initset, fillchar, fillset);
	return fillchar;
}

std::ios_base::fmtflags logstream_base::flags(std::ios_base::fmtflags newflags)
{
	get_stream_state(initclear, initset, fillchar, fillset);
	initset.flags(newflags);
	std::ios_base::fmtflags oldVal = initclear.flags(newflags);
	refresh_stream_state();
	return oldVal;
}

std::ios_base::fmtflags logstream_base::setf(std::ios_base::fmtflags newflags, std::ios_base::fmtflags mask)
{
	get_stream_state(initclear, initset, fillchar, fillset);
	initset.setf(newflags, mask);
	std::ios_base::fmtflags oldVal = initclear.setf(newflags, mask);
	refresh_stream_state();
	return oldVal;
}

std::ios_base::fmtflags logstream_base::setf(std::ios_base::fmtflags newflags)
{
	get_stream_state(initclear, initset, fillchar, fillset);
	initset.setf(newflags);
	std::ios_base::fmtflags oldVal = initclear.setf(newflags);
	refresh_stream_state();
	return oldVal;
}



void logstream_base::setLevel(const ::LOG4CXX_NS::LevelPtr& newlevel)
{
	level = newlevel;
	bool oldLevel = enabled;
	enabled = logger->isEnabledFor(level);

	if (oldLevel != enabled)
	{
		erase();
	}
}

bool logstream_base::isEnabledFor(const ::LOG4CXX_NS::LevelPtr& l) const
{
	return logger->isEnabledFor(l);
}


void logstream_base::setLocation(const LOG4CXX_NS::spi::LocationInfo& newlocation)
{
	if (LOG4CXX_UNLIKELY(enabled))
	{
		location = newlocation;
	}
}


logstream::logstream(const LOG4CXX_NS::LoggerPtr& logger,
	const LOG4CXX_NS::LevelPtr& level) : logstream_base(logger, level), stream(0)
{
}

logstream::logstream(const Ch* loggerName,
	const LOG4CXX_NS::LevelPtr& level)
	: logstream_base(LOG4CXX_NS::Logger::getLogger(loggerName), level), stream(0)
{
}


logstream::logstream(const std::basic_string<Ch>& loggerName,
	const LOG4CXX_NS::LevelPtr& level) : logstream_base(LOG4CXX_NS::Logger::getLogger(loggerName), level), stream(0)
{
}

logstream::~logstream()
{
	delete stream;
}

logstream& logstream::operator<<(logstream_base & (*manip)(logstream_base&))
{
	(*manip)(*this);
	return *this;
}

logstream& logstream::operator<<(const LevelPtr& l)
{
	setLevel(l);
	return *this;
}

logstream& logstream::operator<<(const LOG4CXX_NS::spi::LocationInfo& newlocation)
{
	setLocation(newlocation);
	return *this;
}

logstream& logstream::operator>>(const LOG4CXX_NS::spi::LocationInfo& newlocation)
{
	setLocation(newlocation);
	return *this;
}

logstream& logstream::operator<<(std::ios_base & (*manip)(std::ios_base&))
{
	logstream_base::insert(manip);
	return *this;
}

logstream::operator std::basic_ostream<char>& ()
{
	if (stream == 0)
	{
		stream = new std::basic_stringstream<Ch>();
		refresh_stream_state();
	}

	return *stream;
}

void logstream::log(LoggerPtr& log,
	const LevelPtr& lev,
	const LOG4CXX_NS::spi::LocationInfo& loc)
{
	if (stream != 0)
	{
		std::basic_string<Ch> msg = stream->str();

		if (!msg.empty())
		{
			log->log(lev, msg, loc);
		}
	}
}


void logstream::erase()
{
	if (stream != 0)
	{
		std::basic_string<Ch> emptyStr;
		stream->str(emptyStr);
	}
}


void logstream::get_stream_state(std::ios_base& base,
	std::ios_base& mask,
	int& fill,
	bool& fillSet) const
{
	if (stream != 0)
	{
		std::ios_base::fmtflags flags = stream->flags();
		base.flags(flags);
		mask.flags(flags);
		int width = (int)stream->width();
		base.width(width);
		mask.width(width);
		int precision = (int)stream->precision();
		base.precision(precision);
		mask.precision(precision);
		fill = stream->fill();
		fillSet = true;
	}
}

void logstream::refresh_stream_state()
{
	if (stream != 0)
	{
		int ch;

		if (logstream_base::set_stream_state(*stream, ch))
		{
			stream->fill(ch);
		}
	}
}


#if LOG4CXX_WCHAR_T_API

wlogstream::wlogstream(const LOG4CXX_NS::LoggerPtr& logger,
	const LOG4CXX_NS::LevelPtr& level) : logstream_base(logger, level), stream(0)
{
}

wlogstream::wlogstream(const Ch* loggerName,
	const LOG4CXX_NS::LevelPtr& level)
	: logstream_base(LOG4CXX_NS::Logger::getLogger(loggerName), level), stream(0)
{
}


wlogstream::wlogstream(const std::basic_string<Ch>& loggerName,
	const LOG4CXX_NS::LevelPtr& level) : logstream_base(LOG4CXX_NS::Logger::getLogger(loggerName), level), stream(0)
{
}

wlogstream::~wlogstream()
{
	delete stream;
}

wlogstream& wlogstream::operator<<(logstream_base & (*manip)(logstream_base&))
{
	(*manip)(*this);
	return *this;
}

wlogstream& wlogstream::operator<<(const LevelPtr& l)
{
	setLevel(l);
	return *this;
}

wlogstream& wlogstream::operator<<(const LOG4CXX_NS::spi::LocationInfo& newlocation)
{
	setLocation(newlocation);
	return *this;
}

wlogstream& wlogstream::operator>>(const LOG4CXX_NS::spi::LocationInfo& newlocation)
{
	setLocation(newlocation);
	return *this;
}




wlogstream& wlogstream::operator<<(std::ios_base & (*manip)(std::ios_base&))
{
	logstream_base::insert(manip);
	return *this;
}

wlogstream::operator std::basic_ostream<wchar_t>& ()
{
	if (stream == 0)
	{
		stream = new std::basic_stringstream<Ch>();
		refresh_stream_state();
	}

	return *stream;
}

void wlogstream::log(LoggerPtr& log,
	const LevelPtr& lev,
	const LOG4CXX_NS::spi::LocationInfo& loc)
{
	if (stream != 0)
	{
		std::basic_string<Ch> msg = stream->str();

		if (!msg.empty())
		{
			log->log(lev, msg, loc);
		}
	}
}


void wlogstream::erase()
{
	if (stream != 0)
	{
		std::basic_string<Ch> emptyStr;
		stream->str(emptyStr);
	}
}


void wlogstream::get_stream_state(std::ios_base& base,
	std::ios_base& mask,
	int& fill,
	bool& fillSet) const
{
	if (stream != 0)
	{
		std::ios_base::fmtflags flags = stream->flags();
		base.flags(flags);
		mask.flags(flags);
		int width = (int)stream->width();
		base.width(width);
		mask.width(width);
		int precision = (int)stream->precision();
		base.precision(precision);
		mask.precision(precision);
		fill = stream->fill();
		fillSet = true;
	}
}

void wlogstream::refresh_stream_state()
{
	if (stream != 0)
	{
		int ch;

		if (logstream_base::set_stream_state(*stream, ch))
		{
			stream->fill(ch);
		}
	}
}
#endif

#if LOG4CXX_UNICHAR_API
ulogstream::ulogstream(const Ch* loggerName,
	const LOG4CXX_NS::LevelPtr& level)
	: logstream_base(LOG4CXX_NS::Logger::getLogger(loggerName), level), stream(0)
{
}


ulogstream::ulogstream(const std::basic_string<Ch>& loggerName,
	const LOG4CXX_NS::LevelPtr& level) : logstream_base(LOG4CXX_NS::Logger::getLogger(loggerName), level), stream(0)
{
}
#endif

#if LOG4CXX_UNICHAR_API && LOG4CXX_CFSTRING_API
ulogstream::ulogstream(const CFStringRef& loggerName,
	const LOG4CXX_NS::LevelPtr& level)
	: logstream_base(LOG4CXX_NS::Logger::getLogger(loggerName), level), stream(0)
{
}

#endif


#if LOG4CXX_UNICHAR_API

ulogstream::ulogstream(const LOG4CXX_NS::LoggerPtr& logger,
	const LOG4CXX_NS::LevelPtr& level) : logstream_base(logger, level), stream(0)
{
}



ulogstream::~ulogstream()
{
	delete stream;
}

ulogstream& ulogstream::operator<<(logstream_base & (*manip)(logstream_base&))
{
	(*manip)(*this);
	return *this;
}

ulogstream& ulogstream::operator<<(const LevelPtr& level)
{
	setLevel(level);
	return *this;
}

ulogstream& ulogstream::operator<<(const LOG4CXX_NS::spi::LocationInfo& newlocation)
{
	setLocation(newlocation);
	return *this;
}

ulogstream& ulogstream::operator>>(const LOG4CXX_NS::spi::LocationInfo& newlocation)
{
	setLocation(newlocation);
	return *this;
}




ulogstream& ulogstream::operator<<(std::ios_base & (*manip)(std::ios_base&))
{
	logstream_base::insert(manip);
	return *this;
}

ulogstream::operator std::basic_ostream<UniChar>& ()
{
	if (stream == 0)
	{
		stream = new std::basic_stringstream<Ch>();
		refresh_stream_state();
	}

	return *stream;
}

void ulogstream::log(LoggerPtr& logger,
	const LevelPtr& level,
	const LOG4CXX_NS::spi::LocationInfo& location)
{
	if (stream != 0)
	{
		std::basic_string<Ch> msg = stream->str();

		if (!msg.empty() && logger->isEnabledFor(level))
		{
			LOG4CXX_DECODE_UNICHAR(lsmsg, msg);
			logger->forcedLogLS(level, lsmsg, location);
		}
	}
}


void ulogstream::erase()
{
	if (stream != 0)
	{
		std::basic_string<Ch> emptyStr;
		stream->str(emptyStr);
	}
}


void ulogstream::get_stream_state(std::ios_base& base,
	std::ios_base& mask,
	int& fill,
	bool& fillSet) const
{
	if (stream != 0)
	{
		std::ios_base::fmtflags flags = stream->flags();
		base.flags(flags);
		mask.flags(flags);
		int width = stream->width();
		base.width(width);
		mask.width(width);
		int precision = stream->precision();
		base.precision(precision);
		mask.precision(precision);
		fill = stream->fill();
		fillSet = true;
	}
}

void ulogstream::refresh_stream_state()
{
	if (stream != 0)
	{
		int fillchar;

		if (logstream_base::set_stream_state(*stream, fillchar))
		{
			stream->fill(fillchar);
		}
	}
}
#endif


