#include <log.h>
#include <Containers/DrawQueue.h>
#include <Entities/Components.h>
#include <algorithm>

static container::DrawQueue::iterator operator+(container::DrawQueue::iterator::difference_type offset, container::DrawQueue::iterator it)
{
	return it + offset;
}

namespace container
{
//iterator
	DrawQueue::iterator::iterator(Sprite* input)
		: m_ptr(input)
	{ }

	DrawQueue::iterator& DrawQueue::iterator::operator++()
	{
		m_ptr++;
		return *this;
	}

	DrawQueue::iterator  DrawQueue::iterator::operator++(int)
	{
		auto it = *this;
		m_ptr++;
		return it;
	}

	DrawQueue::iterator& DrawQueue::iterator::operator--()
	{
		m_ptr--;
		return *this;
	}

	DrawQueue::iterator  DrawQueue::iterator::operator--(int)
	{
		auto it = *this;
		m_ptr--;
		return it;
	}

	DrawQueue::iterator DrawQueue::iterator::operator+(difference_type offset) const
	{
		return m_ptr + offset;
	}

	DrawQueue::iterator DrawQueue::iterator::operator-(difference_type offset) const
	{
		return m_ptr - offset;
	}

	DrawQueue::iterator& DrawQueue::iterator::operator+=(difference_type offset)
	{
		m_ptr += offset;
		return *this;
	}

	DrawQueue::iterator& DrawQueue::iterator::operator-=(difference_type offset)
	{
		m_ptr -= offset;
		return *this;
	}

	DrawQueue::iterator::difference_type DrawQueue::iterator::operator-(iterator other) const
	{
		return m_ptr - other.m_ptr;
	}

	Sprite& DrawQueue::iterator::operator*() const
	{
		return *m_ptr;
	}


	Sprite* DrawQueue::iterator::operator->() const
	{
		return m_ptr;
	}

	Sprite& DrawQueue::iterator::operator[](size_type index) const
	{
		return m_ptr[index];
	}

	bool DrawQueue::iterator::operator==(iterator other) const
	{
		return m_ptr == other.m_ptr;
	}

	bool DrawQueue::iterator::operator!=(iterator other) const
	{
		return m_ptr != other.m_ptr;
	}

	bool DrawQueue::iterator::operator>(iterator other) const
	{
		return m_ptr > other.m_ptr;
	}

	bool DrawQueue::iterator::operator<(iterator other) const
	{
		return m_ptr < other.m_ptr;
	}

	bool DrawQueue::iterator::operator>=(iterator other) const
	{
		return m_ptr >= other.m_ptr;
	}

	bool DrawQueue::iterator::operator<=(iterator other) const
	{
		return m_ptr <= other.m_ptr;
	}

	//const_iterator
	DrawQueue::const_iterator::const_iterator(const Sprite* input)
		: m_ptr(input)
	{ }

	DrawQueue::const_iterator& DrawQueue::const_iterator::operator++()
	{
		m_ptr++;
		return *this;
	}

	DrawQueue::const_iterator  DrawQueue::const_iterator::operator++(int)
	{
		auto it = *this;
		m_ptr++;
		return it;
	}

	DrawQueue::const_iterator& DrawQueue::const_iterator::operator--()
	{
		m_ptr--;
		return *this;
	}

	DrawQueue::const_iterator  DrawQueue::const_iterator::operator--(int)
	{
		auto it = *this;
		m_ptr--;
		return it;
	}

	const Sprite& DrawQueue::const_iterator::operator*() const
	{
		return *m_ptr;
	}

	const Sprite* DrawQueue::const_iterator::operator->() const
	{
		return m_ptr;
	}

	const Sprite& DrawQueue::const_iterator::operator[](size_type index) const
	{
		return m_ptr[index];
	}

	bool DrawQueue::const_iterator::operator==(const_iterator other) const
	{
		return m_ptr == other.m_ptr;
	}

	bool DrawQueue::const_iterator::operator!=(const_iterator other) const
	{
		return m_ptr != other.m_ptr;
	}

	DrawQueue::DrawQueue(size_type capacity)
	: m_size(0)
	, m_capacity(capacity)
	{ 
		m_data = new Sprite[m_capacity]{};
	}

	DrawQueue::~DrawQueue()
	{
		delete[] m_data;
	}

	Sprite* DrawQueue::data() const
	{
		return m_data;
	}

	Sprite* DrawQueue::operator->() 
	{
		return m_data;
	}

	Sprite& DrawQueue::operator*() 
	{
		return *m_data;
	}

	Sprite& DrawQueue::operator[](size_type index) 
	{
		return m_data[index];
	}	

	const Sprite* DrawQueue::cdata() const
	{
		return m_data;
	}

	const Sprite* DrawQueue::operator->() const
	{
		return m_data;
	}

	const Sprite& DrawQueue::operator*() const
	{
		return *m_data;
	}

	const Sprite& DrawQueue::operator[](size_type index) const
	{
		return m_data[index];
	}

	inline DrawQueue::size_type DrawQueue::size() const
	{
		return m_size;
	}

	inline DrawQueue::size_type DrawQueue::capacity() const
	{
		return m_capacity;
	}

	void DrawQueue::resize(size_type newCapacity)
	{
		#ifdef CORE_DEBUG
		if (newCapacity <= m_capacity) 
		{
			LOG("Attempting to resize DrawQueue with smaller capacity: current capacity> " << m_capacity << " new capacity> " << newCapacity);
			return;
		}
		#endif

		auto newData = new Sprite[newCapacity];
		int i{};

		for (auto& x : *this)
		{
			newData[i] = x;
			i++;
		}

		m_capacity = newCapacity;
		delete[] m_data;
		m_data = newData;
		newData = nullptr;

	}

	void DrawQueue::push(Sprite input)
	{
		if (m_size == m_capacity)
		{
			resize(m_capacity * 2);
		}

		m_data[m_size] = input;

	}

	void DrawQueue::pushAndSort(Sprite input)
	{
		if (m_size == m_capacity)
		{
			resize(m_capacity * 2);
		}

		m_data[m_size] = input;

		std::sort(begin(), end(), [](Sprite first, Sprite second){ return first.drawDepth < second.drawDepth;});
	}

	void DrawQueue::sort()
	{
		std::sort(begin(), end(), [](Sprite first, Sprite second){ return first.drawDepth < second.drawDepth;});
	}

	DrawQueue::iterator DrawQueue::begin()
	{
		return iterator(m_data);
	}


	DrawQueue::iterator DrawQueue::end()
	{
		return iterator(m_data + m_size);
	}

	DrawQueue::const_iterator DrawQueue::begin() const
	{
		return const_iterator(m_data);
	}

	DrawQueue::const_iterator DrawQueue::end()   const
	{
		return const_iterator(m_data + m_size);
	}

	DrawQueue::const_iterator DrawQueue::cbegin() const
	{
		return const_iterator(m_data);
	}

	DrawQueue::const_iterator DrawQueue::cend()   const
	{
		return const_iterator(m_data + m_size);
	}

}// namespace