/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "Be.h"


// create_sem
_IMPEXP_ROOT sem_id create_sem (int32 count, const char *name)
{
	sem_info* info = new sem_info;
	info->sem = winBe::createSemaphore ();
	info->team = 0; // wrong value
	info->name = name;
	info->count = count;
	info->latest_holder = 0;
	return reinterpret_cast<sem_id>(info);
}


// delete_sem
_IMPEXP_ROOT status_t delete_sem (sem_id sem)
{
	sem_info* info = reinterpret_cast<sem_info*>(sem);
	winBe::deleteSemaphore (info->sem);
	delete info;
	return B_NO_ERROR;
}


// acquire_sem
_IMPEXP_ROOT status_t acquire_sem (sem_id sem)
{
	return acquire_sem_etc (sem, 1, B_TIMEOUT, B_INFINITE_TIMEOUT);
}


// acquire_sem_etc
_IMPEXP_ROOT status_t acquire_sem_etc (sem_id sem, int32 count, uint32 flags, bigtime_t microsecond_timeout)
{
	status_t status = B_NO_ERROR;

	if (sem == 0 || sem == -1)
		status = B_BAD_SEM_ID;
	else {
		sem_info* info = reinterpret_cast<sem_info*>(sem);

		if (count < 1)
			status = B_BAD_VALUE;
		else {
			if (info->count - count < 0 && !(flags & B_TIMEOUT))
				status = B_WOULD_BLOCK;
			else {

				// Wait until timeout
				bigtime_t timeWaited = 0;
				while (timeWaited < microsecond_timeout && info->count - count < 0) {
					snooze (100);
					timeWaited += 100;
				}

				// Acquire
				if (info->count - count >= 0) {
					winBe::lockSemaphore (info->sem);
					info->count -= count;
					info->latest_holder = find_thread (0);
					winBe::unlockSemaphore (info->sem);
				} else
					status = B_TIMED_OUT;
			}
		}
	}

	return status;
}


// release_sem
_IMPEXP_ROOT status_t release_sem (sem_id sem)
{
	return release_sem_etc (sem, 1, 0);
}


// release_sem_etc
_IMPEXP_ROOT status_t release_sem_etc (sem_id sem, int32 count, uint32 flags)
{
	status_t status = B_NO_ERROR;
	sem_info* info = reinterpret_cast<sem_info*>(sem);

	if (count < 1)
		status = B_BAD_VALUE;
	else {
		winBe::lockSemaphore (info->sem);
		info->count += count;
		winBe::unlockSemaphore (info->sem);
	}

	return status;
}


// get_sem_count
_IMPEXP_ROOT status_t get_sem_count (sem_id sem, int32 *count)
{
	sem_info* info = reinterpret_cast<sem_info*>(sem);
	*count = info->count;
	return B_NO_ERROR;
}


// _get_sem_info
_IMPEXP_ROOT status_t _get_sem_info (sem_id sem, sem_info *info, size_t size)
{
	status_t status = B_NO_ERROR;
	sem_info* seminfo = reinterpret_cast<sem_info*>(sem);

	if (size == sizeof sem_info)
		memcpy (info, seminfo, sizeof sem_info);
	else
		status = B_BAD_SEM_ID;

	return status;
}


//B_CAN_INTERRUPT     = 1, 	/* semaphore can be interrupted by a signal */
//B_DO_NOT_RESCHEDULE = 2,	/* release() without rescheduling */
//B_CHECK_PERMISSION  = 4,	/* disallow users changing kernel semaphores */
//B_TIMEOUT           = 8,    /* honor the (relative) timeout parameter */
//B_RELATIVE_TIMEOUT	= 8,
//B_ABSOLUTE_TIMEOUT	= 16	/* honor the (absolute) timeout parameter */

/*
_IMPEXP_ROOT status_t set_sem_owner(sem_id sem, team_id team);
_IMPEXP_ROOT status_t _get_next_sem_info(team_id team, int32 *cookie, sem_info *info, size_t size);
*/
