//------------------------------------------------------------------------------
//	Copyright (C) 2009, 2010 Humboldt-Universitaet zu Berlin
//
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 of the License, or (at your option) any later version.
//
//	This library 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
//	Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public
//	License along with this library; if not, write to the Free Software
//	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/**
 * @file ucFiber.cpp
 * @author Klaus Ahrens
 * @date created at 2009/03/16
 * @brief Implementation of class ucFiber
 * @sa ucFiber.h
 * @since 3.0
 */

#ifndef _WIN32

#include <odemx/coroutine/ucFiber.h>
#include <cstdio>
#include <cassert>

namespace odemx {
namespace coroutine {

std::map<int, void*> ucFiber::allFibers;
int ucFiber::nFibers = 0;

ucFiber::ucFiber(void* param):
	uc_(new ucontext_t),
	callStack_(0),
	stackState_(mainStack)
{
	getcontext(uc_);
}

ucFiber::ucFiber(std::size_t stacksize, void (*start)(void*), void* param):
	uc_(new ucontext_t),
	callStack_(0),
	stackState_(noStack)
{
	getcontext(uc_);

	uc_->uc_stack.ss_flags = 0;
	if (!stacksize) stacksize = ODEMX_DEFAULT_STACK_SIZE;
	uc_->uc_stack.ss_size  = stacksize;
	callStack_ = std::malloc(stacksize);
	uc_->uc_stack.ss_sp = callStack_;

	if (uc_->uc_stack.ss_sp) {
		stackState_ = okStack;
		tos = uc_->uc_stack.ss_sp;
		allFibers[nFibers]=param;
		makecontext(uc_, (void(*)())start, 1, nFibers++);
	}
}

void* ucFiber::getCoroutine(int nr) {
	return allFibers[nr];
}

ucFiber::~ucFiber()
{
	// assert(uc_->uc_stack.ss_sp == callStack_);
	// fails because ss_sp is the stack pointer
	// points to the current position of the call stack

	// if (stackState_== okStack) std::free(uc_->uc_stack.ss_sp);

	//delete the original stack
	if (stackState_== okStack) std::free(callStack_);

	delete uc_;
}

ucFiber* ucFiber::current = 0;

void* ucFiber::ConvertThreadToFiber(void* param) {
	return current = new ucFiber(param);
}

void* ucFiber::GetCurrentFiber() {
		return current;
}

void* ucFiber::CreateFiber(std::size_t stacksize, void (*start)(void*), void* param) {
	ucFiber* fiber = new ucFiber(stacksize, start, param);
	if (fiber->stackState_ == noStack)
		{ delete fiber;	return 0; } // error, must be handled by caller (Coroutine)
	else
		return fiber;
}

void  ucFiber::DeleteFiber(void* fiber) {
	delete reinterpret_cast<ucFiber*>(fiber);
}

void  ucFiber::SwitchToFiber(void* fiber) {
	ucFiber* from = current;
	ucFiber* to   = reinterpret_cast<ucFiber*>(fiber);

	current = to;

	swapcontext(from->uc_, to->uc_);
}

void  ucFiber::stackcheck()  {
	int probe;
	char* stackAt = (char*) &probe;
	ucFiber* f = reinterpret_cast<ucFiber*>(current);

	if ((stackAt - (char*)f->tos) <1024) throw "Fiber: Stack Overflow ahead ...";
}

} } // namespace odemx::coroutine

#endif /* _WIN32 */
