//------------------------------------------------------------------------------
//	Copyright (C) 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 Scheduler.cpp
 * @author Ronald Kluth
 * @date created at 2009/03/12
 * @brief Implementation of odemx::base::Scheduler
 * @sa Scheduler.h
 * @since 3.0
 */

#include <odemx/base/Scheduler.h>
#include <odemx/base/Sched.h>
#include <odemx/base/Simulation.h>
#include <odemx/base/Process.h>
#include <odemx/util/Exceptions.h>

#include <cassert>

namespace odemx {
namespace base {

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

Scheduler::Scheduler( Simulation& sim, const data::Label& label )
:	data::Producer( sim, label )
,	sim_( sim )
,	executionList_( sim, sim.getLabel() + " execution list" )
{
	ODEMX_TRACE << log( "create" ).scope( typeid(Scheduler) );
}

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

//---------------------------------------------------------------execution modes

void Scheduler::run()
{
	ODEMX_TRACE << log( "run" ).scope( typeid(Scheduler) );

	while( ! executionList_.isEmpty() )
	{
		executeNextSched();
	}
}

void Scheduler::runUntil( SimTime stopTime )
{
	ODEMX_TRACE << log( "run until" ).detail( "time", stopTime )
					.scope( typeid(Scheduler) );

	while( ! executionList_.isEmpty()
			&& executionList_.getNextExecutionTime() <= stopTime )
	{
		executeNextSched();
	}
	sim_.setCurrentTime( stopTime );
}

void Scheduler::step()
{
	ODEMX_TRACE << log( "step" ).scope( typeid(Scheduler) );

	if( ! executionList_.isEmpty() )
	{
		executeNextSched();
	}
}

//---------------------------------------------------------------sched execution

void Scheduler::executeNextSched()
{
	// next sched removes itself from list
	Sched* next = executionList_.getNextSched();
	assert( next );

	if( next->getExecutionTime() < sim_.getTime() )
	{
		fatal << log( "Scheduler::executeNextSched(): "
				"execution list corrupted; invalid execution time" )
				.scope( typeid(Scheduler) );

		executionList_.logToChannel( fatal );

		throw SchedulingException( "Execution list corrupted; "
				"next execution time < current time" );
	}

	data::StringLiteral recordText;
	if( next->getSchedType() == Sched::PROCESS )
	{
		recordText = "execute process";
	}
	else if( next->getSchedType() == Sched::EVENT )
	{
		recordText = "execute event";
	}

	ODEMX_TRACE << log( recordText )
			.detail( "partner", next->getLabel() )
			.scope( typeid(Scheduler) );

	sim_.setCurrentTime( next->getExecutionTime() );

	// objects remove themselves from schedule when calling scheduling methods
	next->execute();
	sim_.resetCurrentSched();
	sim_.resetCurrentProcess();
  if( next->getSchedType() == Sched::PROCESS ) {
    Process* p = static_cast<Process*>(next);
    if (p->getProcessState() == Process::TERMINATED)
      p->freeStack();
  }
	sim_.checkAutoDelete();
}

//------------------------------------------------------execution list interface

void Scheduler::addSched( Sched* s )
{
	executionList_.addSched( s );
}

void Scheduler::insertSched( Sched* s )
{
	executionList_.insertSched( s );
}

void Scheduler::insertSchedBefore( Sched* s, Sched* partner )
{
	executionList_.insertSchedBefore( s, partner );
}

void Scheduler::insertSchedAfter( Sched* s, Sched* partner )
{
	executionList_.insertSchedAfter( s, partner );
}

void Scheduler::removeSched( Sched* s )
{
	executionList_.removeSched( s );
}

void Scheduler::inSort( Sched* s )
{
	executionList_.inSort( s );
}

const ExecutionList& Scheduler::getExecutionList() const
{
	return executionList_;
}

} } // namespace odemx::base
