// ================================================================================
// ==      This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås      ==
// ==                     See tb_core.h for more information.                    ==
// ================================================================================

#include "tb_linklist.h"

namespace tb {

// == TBLinkListIterator ====================================================================================

TBLinkListIterator::TBLinkListIterator(TBLinkList *linklist, TBLink *current_link, bool forward)
	: m_linklist(linklist)
	, m_current_link(current_link)
	, m_forward(forward)
{
	Register();
}

TBLinkListIterator::TBLinkListIterator(const TBLinkListIterator &iter)
	: m_linklist(iter.m_linklist)
	, m_current_link(iter.m_current_link)
	, m_forward(iter.m_forward)
{
	Register();
}

TBLinkListIterator::~TBLinkListIterator()
{
	Unregister();
}

void TBLinkListIterator::Register()
{
	m_prev = nullptr;
	m_next = m_linklist->first_iterator;
	if (m_linklist->first_iterator)
		m_linklist->first_iterator->m_prev = this;
	m_linklist->first_iterator = this;
}

void TBLinkListIterator::Unregister()
{
	if (!m_linklist) // Already unregistered
		return;
	if (m_prev)
		m_prev->m_next = m_next;
	if (m_next)
		m_next->m_prev = m_prev;
	if (m_linklist->first_iterator == this)
		m_linklist->first_iterator = m_next;
}

void TBLinkListIterator::UnregisterAndClear()
{
	Unregister();
	m_linklist = nullptr;
	m_current_link = nullptr;
	m_prev = nullptr;
	m_next = nullptr;
}

const TBLinkListIterator& TBLinkListIterator::operator = (const TBLinkListIterator &iter)
{
	if (m_linklist != iter.m_linklist)
	{
		// Change where we are registered if we change linklist.
		Unregister();
		m_linklist = iter.m_linklist;
		Register();
	}
	m_linklist = iter.m_linklist;
	m_current_link = iter.m_current_link;
	m_forward = iter.m_forward;
	return *this;
}

void TBLinkListIterator::Reset()
{
	if (m_linklist)
		m_current_link = m_forward ? m_linklist->first : m_linklist->last;
	else
		m_current_link  = nullptr;
}

TBLink *TBLinkListIterator::GetAndStep()
{
	if (!m_current_link)
		return nullptr;
	TBLink *current = m_current_link;
	m_current_link = m_forward ? m_current_link->next : m_current_link->prev;
	return current;
}

void TBLinkListIterator::RemoveLink(TBLink *link)
{
	// If the current link is being removed, step away from it
	if (m_current_link == link)
		GetAndStep();
}

// == TBLinkList ============================================================================================

TBLinkList::~TBLinkList()
{
	RemoveAll();

	// Make sure any live iterators for this linklist are cleared!
	while (first_iterator)
		first_iterator->UnregisterAndClear();
}

void TBLinkList::AddFirst(TBLink *link)
{
	assert(!link->linklist); // Link is already in some list!
	link->linklist = this;
	link->next = first;
	if (first)
		first->prev = link;
	first = link;
	if (!last)
		last = link;
}

void TBLinkList::AddLast(TBLink *link)
{
	assert(!link->linklist); // Link is already in some list!
	link->linklist = this;
	link->prev = last;
	if (last)
		last->next = link;
	last = link;
	if (!first)
		first = link;
}

void TBLinkList::AddBefore(TBLink *link, TBLink *reference)
{
	assert(reference->linklist == this); // Reference is not added to this list!
	link->linklist = this;
	link->prev = reference->prev;
	link->next = reference;
	if (reference->prev)
		reference->prev->next = link;
	else
		first = link;
	reference->prev = link;
}

void TBLinkList::AddAfter(TBLink *link, TBLink *reference)
{
	assert(reference->linklist == this); // Reference is not added to this list!
	link->linklist = this;
	link->prev = reference;
	link->next = reference->next;
	if (reference->next)
		reference->next->prev = link;
	else
		last = link;
	reference->next = link;
}

void TBLinkList::Remove(TBLink *link)
{
	assert(link->linklist == this); // Link is not added to this list!

	// Go through iterators and make sure there are no pointers
	// to the link we remove.
	TBLinkListIterator *iter = first_iterator;
	while (iter)
	{
		iter->RemoveLink(link);
		iter = iter->m_next;
	}
	// Remove the link
	if (link->next)
		link->next->prev = link->prev;
	if (link->prev)
		link->prev->next = link->next;
	if (first == link)
		first = link->next;
	if (last == link)
		last = link->prev;
	link->linklist = nullptr;
	link->prev = nullptr;
	link->next = nullptr;
}

void TBLinkList::RemoveAll()
{
	// Reset all iterators.
	TBLinkListIterator *iter = first_iterator;
	while (iter)
	{
		iter->m_current_link = nullptr;
		iter = iter->m_next;
	}
	// Remove all links
	TBLink *link = first;
	while (link)
	{
		TBLink *next = link->next;
		link->linklist = nullptr;
		link->prev = nullptr;
		link->next = nullptr;
		link = next;
	}
	first = nullptr;
	last = nullptr;
}

int TBLinkList::CountLinks() const
{
	int count = 0;
	for (TBLink *link = first; link; link = link->next)
		count++;
	return count;
}

} // namespace tb
