/*
 * Copyright 2001-2015, Haiku.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Erik Jaesler (erik@cgsoftware.com)
 */


//! Maintains a global list of all loopers in a given team.


#include "LooperList.h"

#include <Autolock.h>
#include <Looper.h>

#include <algorithm>
#include <string.h>


using std::vector;

namespace BPrivate {

BLooperList gLooperList;


BLooperList::BLooperList()
	:
	fLock("BLooperList lock")
{
}


bool
BLooperList::Lock()
{
	return fLock.Lock();
}


void
BLooperList::Unlock()
{
	fLock.Unlock();
}


bool
BLooperList::IsLocked()
{
	return fLock.IsLocked();
}


void
BLooperList::AddLooper(BLooper* looper)
{
	BAutolock locker(fLock);
	AssertLocked();
	if (!IsLooperValid(looper)) {
		LooperDataIterator i
			= find_if(fData.begin(), fData.end(), EmptySlotPred);
		if (i == fData.end()) {
			fData.push_back(LooperData(looper));
			looper->Lock();
		} else {
			i->looper = looper;
			looper->Lock();
		}
	}
}


bool
BLooperList::IsLooperValid(const BLooper* looper)
{
	BAutolock locker(fLock);
	AssertLocked();

	return find_if(fData.begin(), fData.end(),
		FindLooperPred(looper)) != fData.end();
}


bool
BLooperList::RemoveLooper(BLooper* looper)
{
	BAutolock locker(fLock);
	AssertLocked();

	LooperDataIterator i = find_if(fData.begin(), fData.end(),
		FindLooperPred(looper));
	if (i != fData.end()) {
		i->looper = NULL;
		return true;
	}

	return false;
}


void
BLooperList::GetLooperList(BList* list)
{
	BAutolock locker(fLock);
	AssertLocked();

	for (uint32 i = 0; i < fData.size(); ++i) {
		if (fData[i].looper)
			list->AddItem(fData[i].looper);
	}
}


int32
BLooperList::CountLoopers()
{
	BAutolock locker(fLock);
	AssertLocked();
	return (int32)fData.size();
}


BLooper*
BLooperList::LooperAt(int32 index)
{
	BAutolock locker(fLock);
	AssertLocked();

	BLooper* looper = NULL;
	if (index < (int32)fData.size())
		looper = fData[(uint32)index].looper;

	return looper;
}


BLooper*
BLooperList::LooperForThread(thread_id thread)
{
	BAutolock locker(fLock);
	AssertLocked();

	BLooper* looper = NULL;
	LooperDataIterator i
		= find_if(fData.begin(), fData.end(), FindThreadPred(thread));
	if (i != fData.end())
		looper = i->looper;

	return looper;
}


BLooper*
BLooperList::LooperForName(const char* name)
{
	BAutolock locker(fLock);
	AssertLocked();

	BLooper* looper = NULL;
	LooperDataIterator i
		= find_if(fData.begin(), fData.end(), FindNamePred(name));
	if (i != fData.end())
		looper = i->looper;

	return looper;
}


BLooper*
BLooperList::LooperForPort(port_id port)
{
	BAutolock locker(fLock);
	AssertLocked();

	BLooper* looper = NULL;
	LooperDataIterator i
		= find_if(fData.begin(), fData.end(), FindPortPred(port));
	if (i != fData.end())
		looper = i->looper;

	return looper;
}


void
BLooperList::InitAfterFork()
{
	// We need to reinitialize the locker to get a new semaphore
	new (&fLock) BLocker("BLooperList lock");
	fData.clear();
}


bool
BLooperList::EmptySlotPred(LooperData& data)
{
	return data.looper == NULL;
}


void
BLooperList::AssertLocked()
{
	if (!IsLocked())
		debugger("looperlist is not locked; proceed at great risk!");
}


//	#pragma mark - BLooperList::LooperData


BLooperList::LooperData::LooperData()
	:
	looper(NULL)
{
}


BLooperList::LooperData::LooperData(BLooper* looper)
	:
	looper(looper)
{
}


BLooperList::LooperData::LooperData(const LooperData& other)
{
	*this = other;
}


BLooperList::LooperData&
BLooperList::LooperData::operator=(const LooperData& other)
{
	if (this != &other)
		looper = other.looper;

	return *this;
}


bool
BLooperList::FindLooperPred::operator()(BLooperList::LooperData& data)
{
	return data.looper && looper == data.looper;
}


bool
BLooperList::FindThreadPred::operator()(LooperData& data)
{
	return data.looper && thread == data.looper->Thread();
}


bool
BLooperList::FindNamePred::operator()(LooperData& data)
{
	return data.looper && !strcmp(name, data.looper->Name());
}


bool
BLooperList::FindPortPred::operator()(LooperData& data)
{
	return data.looper && port == _get_looper_port_(data.looper);
}

}	// namespace BPrivate

