//----------------------------------------------------------------------------
//	Copyright (C) 2007, 2008, 2009 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 Timer.cpp

	\author Ronald Kluth

	\date created at 2007/04/04

	\brief Implementation of odemx::sync::Timer

	\sa Timer.h

	\since 2.0
*/

#include <odemx/synchronization/Timer.h>
#include <odemx/base/Process.h>

#include <cassert>

namespace odemx {
namespace synchronization {

const int MAX_PRIORITY = 0xffff;

//------------------------------------------------------construction/destruction

Timer::Timer( base::Simulation& sim, const data::Label& label, TimerObserver* obs )
:	Event( sim, label, obs )
,	data::Observable< TimerObserver >( obs )
,	memory_( sim, getLabel() + ".memory", IMemory::TIMER )
{
	setPriority( MAX_PRIORITY );

	ODEMX_TRACE << log( "create" ).scope( typeid(Timer) );

	// observer
	ODEMX_OBS(TimerObserver, Create(this));
}

Timer::~Timer()
{
	ODEMX_TRACE << log( "destroy" ).scope( typeid(Timer) );

	// destruction of scheduled timers might crash
	// the simulation, hence remove them here
	if( isSet() )
	{
		stop();
	}
}

//--------------------------------------------------------------------scheduling

void Timer::setIn( base::SimTime t )
{
	// cannot schedule Timer
	if( t < 0 )
	{
		error << log( "Timer::setIn(): cannot schedule Timer, invalid SimTime < 0" )
				.scope( typeid(Timer) );
		return;
	}

	ODEMX_TRACE << log( "set in" ).detail( "relative time", t ).scope( typeid(Timer) );

	// observer
	ODEMX_OBS( TimerObserver, SetIn( this, t ) );

	// scheduling
	scheduleIn( t );
}

void Timer::setAt( base::SimTime t )
{
	// cannot schedule Timer
	if ( t < getTime() )
	{
		error << log( "Timer::setAt(): cannot schedule Timer, SimTime already passed" )
				.scope( typeid(Timer) );
		return;
	}

	ODEMX_TRACE << log( "set at" ).detail( "absolute time", t ).scope( typeid(Timer) );

	// observer
	ODEMX_OBS( TimerObserver, SetAt( this, t ) );

	// scheduling
	scheduleAt( t );
}

void Timer::reset( base::SimTime t )
{
	if( ! isScheduled() )
	{
		warning << log( "Timer::reset(): scheduling of idle Timer using reset" )
				.scope( typeid(Timer) );
	}

	// cannot schedule Timer
	if( t < 0 )
	{
		error << log( "Timer::reset(): cannot schedule Timer, invalid SimTime < 0" )
				.scope( typeid(Timer) );
		return;
	}

	ODEMX_TRACE << log( "reset" ).detail( "relative time", t ).scope( typeid(Timer) );

	// observer
	ODEMX_OBS( TimerObserver, Reset( this, getExecutionTime(), t ) );

	// scheduling
	scheduleIn( t );
}

void Timer::stop()
{
	ODEMX_TRACE << log( "stop" ).scope( typeid(Timer) );

	// observer
	ODEMX_OBS( TimerObserver, Stop( this ) );

	// error check done in removeFromSchedule
	removeFromSchedule();

	// remove all waiting processes from Memory
	eraseMemory();
}

bool Timer::isSet()
{
	return isScheduled();
}

//-----------------------------------------------------------Process interaction

bool Timer::registerProcess( base::Process* p )
{
	assert( p );

	ODEMX_TRACE << log( "register process" )
			.detail( "partner", p->getLabel() )
			.scope( typeid(Timer) );

	// observer
	ODEMX_OBS( TimerObserver, RegisterProcess( this, p ) );

	return remember( p );
}

bool Timer::removeProcess( base::Process* p )
{
	assert( p );

	ODEMX_TRACE << log( "remove process" )
			.detail( "partner", p->getLabel() )
			.scope( typeid(Timer) );

	// observer
	ODEMX_OBS( TimerObserver, RemoveProcess( this, p ) );

	return forget( p );
}

//----------------------------------------------------------Event implementation

void Timer::eventAction()
{
	ODEMX_TRACE << log( "timeout" ).scope( typeid(Timer) );

	// observer
	ODEMX_OBS( TimerObserver, Timeout( this ) );

	if( ! memory_.waiting() )
	{
		warning << log( "Timer::eventAction(): memory empty, cannot alert any process" )
				.scope( typeid(Timer) );
	}
	else
	{
		// reschedule registered processes
		alert();
	}
}

//--------------------------------------------------------IMemory implementation

IMemory::Type Timer::getMemoryType() const
{
	return memory_.getMemoryType();
}

bool Timer::isAvailable()
{
	return ! Event::isScheduled();
}

void Timer::alert()
{
	memory_.alert( this );
}

bool Timer::remember( base::Sched* newObject )
{
	return memory_.remember( newObject );
}

bool Timer::forget( base::Sched* rememberedObject )
{
	return memory_.forget( rememberedObject );
}

void Timer::eraseMemory()
{
	memory_.eraseMemory();
}

const base::SchedList& Timer::getWaiting() const
{
	return memory_.getWaiting();
}

} } // namespace odemx::sync
