/*
 * 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/helpers/cyclicbuffer.h>
#include <log4cxx/helpers/exception.h>
#include "../logunit.h"

#include <log4cxx/logmanager.h>
#include <log4cxx/logger.h>
#include <log4cxx/spi/loggingevent.h>
#include <log4cxx/spi/location/locationinfo.h>
#include "../testchar.h"

using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::spi;

#define MAX 1000

LOGUNIT_CLASS(CyclicBufferTestCase)
{
	LOGUNIT_TEST_SUITE(CyclicBufferTestCase);
	LOGUNIT_TEST(test0);
	LOGUNIT_TEST(test1);
	LOGUNIT_TEST(testResize);
	LOGUNIT_TEST_EXCEPTION(testNegativeSizeRejectedBeforeAllocation, IllegalArgumentException);
	LOGUNIT_TEST(testResizeAfterReadPreservesOrder);
	LOGUNIT_TEST_SUITE_END();

	LoggerPtr logger;
	std::vector<LoggingEventPtr> e;

public:
	void setUp()
	{
		e.reserve(1000);
		LoggingEventPtr event;

		for (int i = 0; i < MAX; i++)
		{
			event = LoggingEventPtr(new LoggingEvent(LOG4CXX_STR("x"), Level::getDebug(), LOG4CXX_STR("e"),
						log4cxx::spi::LocationInfo::getLocationUnavailable()));
			e.push_back(event);
		}
	}

	void tearDown()
	{
		LogManager::shutdown();
	}

	void test0()
	{
		int size = 2;

		CyclicBuffer cb(size);
		LOGUNIT_ASSERT_EQUAL(size, cb.getMaxSize());

		cb.add(e[0]);
		LOGUNIT_ASSERT_EQUAL(1, cb.length());
		LOGUNIT_ASSERT_EQUAL(e[0], cb.get());
		LOGUNIT_ASSERT_EQUAL(0, cb.length());
		LOGUNIT_ASSERT(cb.get() == 0);
		LOGUNIT_ASSERT_EQUAL(0, cb.length());

		CyclicBuffer cb2(size);
		cb2.add(e[0]);
		cb2.add(e[1]);
		LOGUNIT_ASSERT_EQUAL(2, cb2.length());
		LOGUNIT_ASSERT_EQUAL(e[0], cb2.get());
		LOGUNIT_ASSERT_EQUAL(1, cb2.length());
		LOGUNIT_ASSERT_EQUAL(e[1], cb2.get());
		LOGUNIT_ASSERT_EQUAL(0, cb2.length());
		LOGUNIT_ASSERT(cb2.get() == 0);
		LOGUNIT_ASSERT_EQUAL(0, cb2.length());
	}

	void test1()
	{
		for (int bufSize = 1; bufSize <= 128; bufSize *= 2)
		{
			doTest1(bufSize);
		}
	}

	void doTest1(int size)
	{
		//System.out.println("Doing test with size = "+size);
		CyclicBuffer cb(size);

		LOGUNIT_ASSERT_EQUAL(size, cb.getMaxSize());

		int i;

		for (i = -(size + 10); i < (size + 10); i++)
		{
			LOGUNIT_ASSERT(cb.get(i) == 0);
		}

		for (i = 0; i < MAX; i++)
		{
			cb.add(e[i]);

			int limit = (i < (size - 1)) ? i : (size - 1);

			//System.out.println("\nLimit is " + limit + ", i="+i);
			for (int j = limit; j >= 0; j--)
			{
				//System.out.println("i= "+i+", j="+j);
				LOGUNIT_ASSERT_EQUAL(e[i - (limit - j)], cb.get(j));
			}

			LOGUNIT_ASSERT(cb.get(-1) == 0);
			LOGUNIT_ASSERT(cb.get(limit + 1) == 0);
		}
	}

	void testResize()
	{
		for (int isize = 1; isize <= 128; isize *= 2)
		{
			doTestResize(isize, (isize / 2) + 1, (isize / 2) + 1);
			doTestResize(isize, (isize / 2) + 1, isize + 10);
			doTestResize(isize, isize + 10, (isize / 2) + 1);
			doTestResize(isize, isize + 10, isize + 10);
		}

	}

	void doTestResize(int initialSize, int numberOfAdds, int newSize)
	{
		//System.out.println("initialSize = "+initialSize+", numberOfAdds="
		//        +numberOfAdds+", newSize="+newSize);
		CyclicBuffer cb(initialSize);

		for (int i = 0; i < numberOfAdds; i++)
		{
			cb.add(e[i]);
		}

		cb.resize(newSize);

		int offset = numberOfAdds - initialSize;

		if (offset < 0)
		{
			offset = 0;
		}

		int len = (newSize < numberOfAdds) ? newSize : numberOfAdds;
		len = (len < initialSize) ? len : initialSize;

		//System.out.println("Len = "+len+", offset="+offset);
		for (int j = 0; j < len; j++)
		{
			LOGUNIT_ASSERT_EQUAL(e[offset + j], cb.get(j));
		}
	}

	void testNegativeSizeRejectedBeforeAllocation()
	{
		CyclicBuffer cb(-1);
	}

	void testResizeAfterReadPreservesOrder()
	{
		CyclicBuffer cb(5);
		cb.add(e[0]);
		cb.add(e[1]);
		cb.add(e[2]); // first=0, numElems=3

		LOGUNIT_ASSERT_EQUAL(e[0], cb.get()); // first=1, numElems=2

		cb.resize(4);

		LOGUNIT_ASSERT_EQUAL(2, cb.length());
		LOGUNIT_ASSERT_EQUAL(e[1], cb.get());
		LOGUNIT_ASSERT_EQUAL(e[2], cb.get());
		LOGUNIT_ASSERT(cb.get() == 0);
	}
};

LOGUNIT_TEST_SUITE_REGISTRATION(CyclicBufferTestCase);
