/* File Philosopher_modMain_Im.c */
/*
 ##############################################################
 # (C) 2013 BAE SYSTEMS
 #
 # ECOA Dining Philosophers Sample
 #
 # $Id: Philosopher_modMain_Im.c,v 2.2 2017/11/06 15:33:40 ksappleb Exp $
 #
 # Part of the BAE Systems, Rochester, ECOA Samples collection
 #
 # Created by K S Appleby, BAE Systems, Airport Works, Rochester, Kent
 #
 # This software is developed for and on behalf of BAE Systems (Operations)
 # Limited, Dassault Aviation, Bull SAS, Thales Systemes Aeroportes,
 # GE Aviation Systems Limited,
 # General Dynamics United Kingdom Limited and Leonardo MW Ltd,
 # and the copyright is owned by BAE Systems (Operations) Limited,
 # Dassault Aviation, Bull SAS, Thales Systemes Aeroportes, 
 # GE Aviation Systems Limited, General Dynamics United Kingdom
 # Limited and Leonardo MW Ltd
 #
 # This software is developed by BAE Systems (Operations)
 # Limited, Electronic Systems, and is the Intellectual
 # Property of BAE Systems (Operations) Limited, Electronic Systems.
 #
 # The information set out in this document is provided solely
 # on an 'as is' basis and the co-developers of this software make
 # no warranties expressed or implied, including no warranties
 # as to completeness, accuracy or fitness for purpose, with respect
 # to any of the information.
 ##############################################################
*/
#include "Philosopher_modMain_Im.h"
#include "philosopher.h"


#include <stdio.h>
#include <stdlib.h>

#ifndef NULL
#define NULL (void*)0
#endif

/* Runtime lifecycle API */
void Philosopher_modMain_Im__INITIALIZE__received
   (Philosopher_modMain_Im__context* context)
{
	context->user.PhiloState = philosopher__State_UNDEFINED;
	context->user.EatUntil   = context->user.ThinkUntil = (ECOA__hr_time){ 0, 0 };
	context->user.HaveLeftStick = context->user.HaveRightStick = ECOA__FALSE;
}

void Philosopher_modMain_Im__START__received
   (Philosopher_modMain_Im__context* context)
{
	ECOA__uint32    IAm;
	ECOA__log       msg;
	//
	context->user.PhiloState = philosopher__State_GETTINGSTICKS;
	//
	Philosopher_modMain_Im_container__get_Id_value( context, &IAm );
	msg.current_size = sprintf( msg.data, "\n\tPhilosopher %d is ready...\n", IAm );
	Philosopher_modMain_Im_container__log_info( context, msg );
}

void Philosopher_modMain_Im__STOP__received
   (Philosopher_modMain_Im__context* context)
{
   /* User Code Here */
}

void Philosopher_modMain_Im__SHUTDOWN__received
   (Philosopher_modMain_Im__context* context)
{
   /* User Code Here */
}

const char* philosopher__State_value( philosopher__State s )
{
	static const char* philosopher__State_strings[] =
		{"(unknown)", "philosopher__State_UNDEFINED", "philosopher__State_GETTINGSTICKS", "philosopher__State_EATING",
				"philosopher__State_SURRENDERING", "philosopher__State_THINKING" };
	//
	switch( s ){
		case philosopher__State_UNDEFINED:
		case philosopher__State_GETTINGSTICKS:
		case philosopher__State_EATING:
		case philosopher__State_SURRENDERING:
		case philosopher__State_THINKING:
			return philosopher__State_strings[s+1];
		default:
			return philosopher__State_strings[0];
	}
}

int timecmp( ECOA__hr_time l, ECOA__hr_time r )
{
	int sgnsec  = ( l.seconds     - r.seconds );
	int sgnnsec = ( l.nanoseconds - r.nanoseconds );
	if( sgnsec != 0 )
		return sgnsec;
	else
	if( sgnnsec != 0 )
		return sgnnsec;
	else
		return 0;
}

ECOA__hr_time timeAdd( ECOA__hr_time l, ECOA__hr_time r )
{
	ECOA__hr_time res;
	//
	res.seconds     = l.seconds     + r.seconds;
	res.nanoseconds = l.nanoseconds + r.nanoseconds;
	while( res.nanoseconds > 1000000000LL ){
		res.seconds     += 1;
		res.nanoseconds -= 1000000000LL;
	}
	return res;
}

ECOA__double64 timeDiff( ECOA__hr_time l, ECOA__hr_time r )
{
	return (ECOA__double64)(l.seconds-r.seconds)+( (ECOA__double64)(l.nanoseconds-r.nanoseconds)*1.0e-9 );
}

static const ECOA__hr_time EatPeriod   = {  7, 0 };
static const ECOA__hr_time ThinkPeriod = { 11, 0 };

void Philosopher_modMain_Im__Tick__received
   (Philosopher_modMain_Im__context* context)
{
	ECOA__uint32    IAm;
	ECOA__log       msg;
	ECOA__return_status     erc;
	ECOA__hr_time   timeNow;
	ECOA__boolean8  Taken, TableLaid;
	ECOA__int32     LeftStick, RightStick;
	Philosopher_modMain_Im_container__ready_handle rdyHndl;
	//
	fflush(stdout); // Make sure that any piped IO is completed
	//
	Philosopher_modMain_Im_container__get_Id_value( context, &IAm );
	//
	LeftStick  = IAm - 1;// IAm is {1..5} therefore this is {0..4}
	RightStick = IAm % 5;// IAm is {1..5} therefore this is {1..4,0}
	//
	Philosopher_modMain_Im_container__get_relative_local_time( context, &timeNow );
	msg.current_size = sprintf( msg.data, "Philosopher %d Tick. State = %s", IAm, philosopher__State_value( context->user.PhiloState ) );
//	Philosopher_modMain_Im_container__log_info( context, msg );
	//
	Philosopher_modMain_Im_container__ready__get_read_access( context, &rdyHndl );
	TableLaid = *(rdyHndl.data);
	Philosopher_modMain_Im_container__ready__release_read_access( context, &rdyHndl );
	if( !TableLaid ){
		// No point in doing anything 'til the Chopsticks are available
		msg.current_size = sprintf( msg.data, "Philosopher %d Tick, but Chopsticks not available...", IAm );
		Philosopher_modMain_Im_container__log_info( context, msg );
		return;
	}
	//
	if( context->user.PhiloState == philosopher__State_GETTINGSTICKS ){
		// **********************************************
		// * Dijkstra's Resource Hierarchy Solution.... *
		// **********************************************
		// The lower numbered chopstick is always taken first...
		//
		if( RightStick < LeftStick ){
			if( !context->user.HaveRightStick ){
				if(( erc = Philosopher_modMain_Im_container__take__request_sync( context, RightStick, IAm, &Taken )) != ECOA__return_status_OK ){
					msg.current_size = sprintf( msg.data, "take__request( RightStick=>%d, by=>%d ) failed with %d", RightStick, IAm, erc );
					Philosopher_modMain_Im_container__log_info( context, msg );
				}
				if( !Taken ){
					return; // We'll have another go on the next tick...
				}else{
					context->user.HaveRightStick = ECOA__TRUE;
				}
			}
		}
		if( !context->user.HaveLeftStick ){
			if(( erc = Philosopher_modMain_Im_container__take__request_sync( context, LeftStick, IAm, &Taken )) != ECOA__return_status_OK ){
				msg.current_size = sprintf( msg.data, "take__request( LeftStick=>%d, by=>%d ) failed with %d", LeftStick, IAm, erc );
				Philosopher_modMain_Im_container__log_info( context, msg );
			}
			if( !Taken ){
				return; // We'll have another go on the next tick...
			}else{
				context->user.HaveLeftStick = ECOA__TRUE;
			}
		}
		if( RightStick > LeftStick ){
			if( !context->user.HaveRightStick ){
				if(( erc = Philosopher_modMain_Im_container__take__request_sync( context, RightStick, IAm, &Taken )) != ECOA__return_status_OK ){
					msg.current_size = sprintf( msg.data, "take__request( RightStick=>%d, by=>%d ) failed with %d", RightStick, IAm, erc );
					Philosopher_modMain_Im_container__log_info( context, msg );
				}
				if( !Taken ){
					return; // We'll have another go on the next tick...
				}else{
					context->user.HaveRightStick = ECOA__TRUE;
				}
			}
		}
		printf( "%d begin eating.\n", IAm );
		context->user.PhiloState = philosopher__State_EATING;
		context->user.EatUntil = timeAdd( timeNow, EatPeriod );
	}
		//
	if( context->user.PhiloState == philosopher__State_EATING ){
		if( timecmp( timeNow, context->user.EatUntil ) < 0 ){
			return; // Still chomping...
		}else{
			printf( "%d finished eating.\n", IAm );
			context->user.PhiloState = philosopher__State_SURRENDERING;
		}
	}
		//
	if( context->user.PhiloState == philosopher__State_SURRENDERING ){
		if(( erc = Philosopher_modMain_Im_container__surrender__request_sync( context, LeftStick, IAm )) != ECOA__return_status_OK ){
			msg.current_size = sprintf( msg.data, "\n\tPhilosopher %d failed to surrender chopstick %d...\n", IAm, LeftStick );
			Philosopher_modMain_Im_container__log_warning( context, msg );
		}
		if(( erc = Philosopher_modMain_Im_container__surrender__request_sync( context, RightStick, IAm )) != ECOA__return_status_OK ){
			msg.current_size = sprintf( msg.data, "\n\tPhilosopher %d failed to surrender chopstick %d...\n", IAm, RightStick );
			Philosopher_modMain_Im_container__log_warning( context, msg );
		}
		context->user.HaveLeftStick = context->user.HaveRightStick = ECOA__FALSE;
		printf( "%d begin thinking.\n", IAm );
		context->user.PhiloState = philosopher__State_THINKING;
		context->user.ThinkUntil = timeAdd( timeNow, ThinkPeriod );
	}
		//
	if( context->user.PhiloState == philosopher__State_THINKING ){
		if( timecmp( timeNow, context->user.ThinkUntil ) < 0 ){
			return; // Still cogitating...
		}
		printf( "%d finished thinking.\n", IAm );
		context->user.PhiloState = philosopher__State_GETTINGSTICKS;
	}
		//
	if( context->user.PhiloState <= philosopher__State_UNDEFINED ||
			context->user.PhiloState > philosopher__State_THINKING ){
		msg.current_size = sprintf( msg.data, "\n\tPhilosopher %d has illegal state %s...\n", IAm, philosopher__State_value( context->user.PhiloState ));
		Philosopher_modMain_Im_container__log_info( context, msg );
		return;
	}
}
