/*
 * 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/ndc.h>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/threadspecificdata.h>

using namespace log4cxx;
using namespace log4cxx::helpers;

NDC::NDC(const std::string& message)
{
	push(message);
}

NDC::~NDC()
{
	pop();
}


LogString& NDC::getMessage(NDC::DiagnosticContext& ctx)
{
	return ctx.first;
}

LogString& NDC::getFullMessage(NDC::DiagnosticContext& ctx)
{
	return ctx.second;
}

void NDC::clear()
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		while (!stack.empty())
		{
			stack.pop();
		}

		data->recycle();
	}
}

NDC::Stack* NDC::cloneStack()
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			return new Stack(stack);
		}
	}

	return new Stack();
}

void NDC::inherit(NDC::Stack* stack)
{
	if (stack != NULL)
	{
		ThreadSpecificData::inherit(*stack);
		delete stack;
	}
}


bool NDC::get(LogString& dest)
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			dest.append(getFullMessage(stack.top()));
			return true;
		}

		data->recycle();
	}

	return false;
}

int NDC::getDepth()
{
	int size = 0;
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		size = (int)data->getStack().size();

		if (size == 0)
		{
			data->recycle();
		}
	}

	return size;
}

LogString NDC::pop()
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			LogString value(getMessage(stack.top()));
			stack.pop();
			data->recycle();
			return value;
		}

		data->recycle();
	}

	return LogString();
}

bool NDC::pop(std::string& dst)
{
	bool retval = false;
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			Transcoder::encode(getMessage(stack.top()), dst);
			stack.pop();
			retval = true;
		}

		data->recycle();
	}

	return retval;
}

LogString NDC::peek()
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			return getMessage(stack.top());
		}

		data->recycle();
	}

	return LogString();
}

bool NDC::peek(std::string& dst)
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			Transcoder::encode(getMessage(stack.top()), dst);
			return true;
		}

		data->recycle();
	}

	return false;
}

void NDC::pushLS(const LogString& message)
{
	ThreadSpecificData::push(message);
}

void NDC::push(const std::string& message)
{
	LOG4CXX_DECODE_CHAR(msg, message);
	pushLS(msg);
}

void NDC::remove()
{
	clear();
}

bool NDC::empty()
{
	bool empty = true;
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();
		empty = stack.empty();

		if (empty)
		{
			data->recycle();
		}
	}

	return empty;
}

#if LOG4CXX_WCHAR_T_API
NDC::NDC(const std::wstring& message)
{
	push(message);
}

void NDC::push(const std::wstring& message)
{
	LOG4CXX_DECODE_WCHAR(msg, message);
	pushLS(msg);
}

bool NDC::pop(std::wstring& dst)
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			Transcoder::encode(getMessage(stack.top()), dst);
			stack.pop();
			data->recycle();
			return true;
		}

		data->recycle();
	}

	return false;
}

bool NDC::peek(std::wstring& dst)
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			Transcoder::encode(getMessage(stack.top()), dst);
			return true;
		}

		data->recycle();
	}

	return false;
}

#endif


#if LOG4CXX_UNICHAR_API
NDC::NDC(const std::basic_string<UniChar>& message)
{
	push(message);
}

void NDC::push(const std::basic_string<UniChar>& message)
{
	LOG4CXX_DECODE_UNICHAR(msg, message);
	pushLS(msg);
}

bool NDC::pop(std::basic_string<UniChar>& dst)
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			Transcoder::encode(getMessage(stack.top()), dst);
			stack.pop();
			data->recycle();
			return true;
		}

		data->recycle();
	}

	return false;
}

bool NDC::peek(std::basic_string<UniChar>& dst)
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			Transcoder::encode(getMessage(stack.top()), dst);
			return true;
		}

		data->recycle();
	}

	return false;
}

#endif


#if LOG4CXX_CFSTRING_API
NDC::NDC(const CFStringRef& message)
{
	push(message);
}

void NDC::push(const CFStringRef& message)
{
	LOG4CXX_DECODE_CFSTRING(msg, message);
	pushLS(msg);
}

bool NDC::pop(CFStringRef& dst)
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			dst = Transcoder::encode(getMessage(stack.top()));
			stack.pop();
			data->recycle();
			return true;
		}

		data->recycle();
	}

	return false;
}

bool NDC::peek(CFStringRef& dst)
{
	ThreadSpecificData* data = ThreadSpecificData::getCurrentData();

	if (data != 0)
	{
		Stack& stack = data->getStack();

		if (!stack.empty())
		{
			dst = Transcoder::encode(getMessage(stack.top()));
			return true;
		}

		data->recycle();
	}

	return false;
}

#endif

