/*
	The transportation module utilising the Linda API and
	used by the messaging layer

	Author:		K.Y. Hui
	Date:		15 June 1999
	Version:	1.30

*/


:- module(transport,	[	tp_init_transport/1,		%init the transport layer
				tp_halt_transport/0,		%halt the transport layer
				tp_generate_id/1,		%generate a random no. as id
				tp_send_message/1,		%send a message
				tp_receive_message/1,		%receive a message
				tp_receive_message/2,		%receive a message
				tp_receive_message_noblock/1,	%receive without blocking
				tp_receive_message_noblock/2,	%receive without blocking
				tp_receive_message_noblock/3	%receive without blocking
			]).

:- ['../util/prolog_detect'].	%the Prolog detecting module
:- ['../linda/linda_client'].	%use Prolog Linda-client
:- [message_format].		%use message-format module
:- ['../util/datetime'].	%date & time module
:- ['../util/counter'].		%load counter util module


:- prolog_detect:prolog(Prolog),
	(	(Prolog=sicstus,
		!,
		use_module(library(lists))
		);
		(Prolog=pfdm,
		!,
		use_module(library(lists))
		);
		(Prolog=eclipse,
		!,
		['../util/retractall']
		);
		true
	).


:- dynamic(acknowledgement/1).
:- dynamic(id_mode/1).


%----------
% init the transportation layer w/ platform specific parameters
% The Linda layer requires the parameters "server_host" & "server_port"
%
% Parameters =	a list of platform-dependent parameters in the form:
%		[Name1:Value1, Name2:Value2, ...]
%----------

tp_init_transport(Parameters) :-
	member(server_host:Host,Parameters),			%get linda server host:port
	member(server_port:Port,Parameters),			%compulsory
	init_acknowledgement(Parameters),			%init acknowledgement if needed
	init_id_generator(Parameters),				%also init id generator
	linda_client:linda_client(Host:Port).			%connect to Linda server

%
% init acknowledgement if needed
%
init_acknowledgement(Parameters) :-
	member(acknowledgement:Second,Parameters),	%find the acknowledgement param
	!,
	integer(Second),				%timeout is >= 0
	Second >= 0,
	retractall(acknowledgement(_)),
	asserta(acknowledgement(Second)),		%assert the timeout value
	!.

init_acknowledgement(_) :-
	retractall(acknowledgement(_)),			%if not specified
	asserta(acknowledgement(0)).			%then by default off

%
% init special id generator mode
%

%
% counter mode
%
init_id_generator(Parameters):-
	member(id:counter,Parameters),			%counter mode
	!,
	retractall(id_mode(_)),
	asserta(id_mode(counter)),
	counter:initialize_counter(id,1),		%init counter "id" to 1
	!.

%
% random mode
%
init_id_generator(Parameters):-
	member(id:random,Parameters),			%real random mode (by second)
	!,
	retractall(id_mode(_)),
	asserta(id_mode(random)),
	use_module(library(random)),			%load random no. module
	prolog_detect:prolog(Prolog),			%detect Prolog system
	init_random_generator(random,Prolog),
	!.

%
% very random mode
%
init_id_generator(Parameters):-
	member(id:very_random,Parameters),		%real random mode (by u-second)
	!,
	retractall(id_mode(_)),
	asserta(id_mode(very_random)),
	use_module(library(random)),			%load random no. module
	prolog_detect:prolog(Prolog),			%detect Prolog system
	init_random_generator(very_random,Prolog),
	!.

init_id_generator(_):-					%don't care mode
	retractall(id_mode(_)),
	asserta(id_mode(random)),			%make it random but don't init generator
	use_module(library(random)),			%load random no. module
	!.

%
% init the random number generator
%
init_random_generator(_,swi).			%SWI doesn't need any initialisation

init_random_generator(_,eclipse).		%ECLiPSe doesn't need any initialisation

%
% init random no generator by system timer's "second" part
% (not so random version)
%
init_random_generator(random,sicstus) :-			%SICStus version
	datetime:get_current_datetime(Time_stamp),
	Time_stamp=time_stamp(date(_,_,_),time(_,_,Seed)),	%get second as seed
	getrand(rand(X,Y,_)),					%get current state
	setrand(rand(Seed,X,Y)),				%set new state
	!.



%
% init random no generator by system timer's micro-second
% (very random version)
%
%init_random_generator(very_random,sicstus) :-	%SICStus version
%	gettime:gettime(_,Usec),		%get timer's u-sec
%	Seed is Usec mod 30269,			%make seed from timer
%	getrand(rand(X,Y,_)),			%get current state
%	setrand(rand(Seed,X,Y)),		%set new state
%	!.




%----------
%
% stop the transport layer
%
%----------

tp_halt_transport :-
	linda_client:close_client.


%----------
%
% generate an integer usable as an ID
%
%----------

tp_generate_id(Id) :-
	(	id_mode(random);		%random and very_random mode
		id_mode(very_random)
	),
	random(0,32767,Id),			%an integer from 0-32767
	!.					%cut it to prevent looping

tp_generate_id(Id) :-
	id_mode(counter),
	counter:generate_counter(id,Id).		%counter mode


%----------
%
% send_message(+Message)
%
% send the message
%
%----------

tp_send_message(Message) :-
	message_format:mf_decompose_message(Context,_,Message),
	message_format:mf_decompose_context(_,Cid,Sender,Receiver,Time,Context),
	(	(var(Cid),
		tp_generate_id(Cid),
		!
		);
		true
	),
	(	(var(Time),				%fill in time-stamp if needed
		datetime:get_current_datetime(Time),
		!
		);
		true
	),
	linda_client:out(Message),
	(	(acknowledgement(Second),				%if acknowledgement is needed
		Second > 0,
		!,
		message_format:mf_compose_context(_,Cid,Receiver,Sender,_,Ack_context),	%compose context
		Core=system(ack,Receiver,Sender,Cid),					%compose system core w/ ack from receiver to sender
		message_format:mf_compose_message(Ack_context,Core,Template),		%compose message template
		linda_client:in_noblock(Template,Second:0)				%wait for acknowledgement
		);
		true
	).


%----------
%
% receive_message(?Message)
%
% receive a message according to a message-template
%
%----------

tp_receive_message(Message) :-
	linda_client:in(Message),
	(	(acknowledgement(Second),
		Second > 0,
		!,
		message_format:mf_decompose_message(Context,_,Message),
		message_format:mf_decompose_context(_,Cid,Sender,Receiver,_,Context),
		datetime:get_current_datetime(Time),
		message_format:mf_compose_context(1,Cid,Receiver,Sender,Time,Ack_context),
		Core=system(ack,Receiver,Sender,Cid),					%compose acknowledgement
		message_format:mf_compose_message(Ack_context,Core,Ack),
		linda_client:out(Ack)
		);
		true
	).

%
% receive a message according to my name
%
tp_receive_message(Myname,Message) :-
	message_format:mf_compose_message(_,Myname,_,_,Message),			%compose message template from my name
	linda_client:in(Message),
	(	(acknowledgement(Second),
		Second > 0,
		!,
		message_format:mf_decompose_message(Context,_,Message),
		message_format:mf_decompose_context(_,Cid,Sender,Receiver,_,Context),
		datetime:get_current_datetime(Time),
		message_format:mf_compose_context(1,Cid,Receiver,Sender,Time,Ack_context),
		Core=system(ack,Receiver,Sender,Cid),					%compose acknowledgement
		message_format:mf_compose_message(Ack_context,Core,Ack),
		linda_client:out(Ack)
		);
		true
	).



%
% receive a message without blocking
%
tp_receive_message_noblock(Message) :-
	linda_client:in_noblock(Message),
	(	(acknowledgement(Second),
		Second > 0,
		!,
		message_format:mf_decompose_message(Context,_,Message),
		message_format:mf_decompose_context(_,Cid,Sender,Receiver,_,Context),
		datetime:get_current_datetime(Time),
		message_format:mf_compose_context(1,Cid,Receiver,Sender,Time,Ack_context),
		Core=system(ack,Receiver,Sender,Cid),					%compose acknowledgement
		message_format:mf_compose_message(Ack_context,Core,Ack),
		linda_client:out(Ack)
		);
		true
	).

%
% receive noblock w/ timeout
%
tp_receive_message_noblock(timeout(Second),Message) :-
	!,
	linda_client:in_noblock(Message,Second:0),		%call in_noblock/2 w/ timeout
	(	(acknowledgement(Second),
		Second > 0,
		!,
		message_format:mf_decompose_message(Context,_,Message),
		message_format:mf_decompose_context(_,Cid,Sender,Receiver,_,Context),
		datetime:get_current_datetime(Time),
		message_format:mf_compose_context(1,Cid,Receiver,Sender,Time,Ack_context),
		Core=system(ack,Receiver,Sender,Cid),					%compose acknowledgement
		message_format:mf_compose_message(Ack_context,Core,Ack),
		linda_client:out(Ack)
		);
		true
	).





%
% receive a message without blocking by my name
%
tp_receive_message_noblock(Myname,Message) :-
	message_format:mf_compose_message(_,Myname,_,_,Message),			%compose message template from my name
	linda_client:in_noblock(Message),
	(	(acknowledgement(Second),
		Second > 0,
		!,
		message_format:mf_decompose_message(Context,_,Message),
		message_format:mf_decompose_context(_,Cid,Sender,Receiver,_,Context),
		datetime:get_current_datetime(Time),
		message_format:mf_compose_context(1,Cid,Receiver,Sender,Time,Ack_context),
		Core=system(ack,Receiver,Sender,Cid),					%compose acknowledgement
		message_format:mf_compose_message(Ack_context,Core,Ack),
		linda_client:out(Ack)
		);
		true
	).


%
% receive a message without blocking w/ timeout
%
tp_receive_message_noblock(timeout(Second),Myname,Message) :-
	message_format:mf_compose_message(_,Myname,_,_,Message),			%compose message template from my name
	linda_client:in_noblock(Message,Second:0),
	(	(acknowledgement(Second),
		Second > 0,
		!,
		message_format:mf_decompose_message(Context,_,Message),
		message_format:mf_decompose_context(_,Cid,Sender,Receiver,_,Context),
		datetime:get_current_datetime(Time),
		message_format:mf_compose_context(1,Cid,Receiver,Sender,Time,Ack_context),
		Core=system(ack,Receiver,Sender,Cid),					%compose acknowledgement
		message_format:mf_compose_message(Ack_context,Core,Ack),
		linda_client:out(Ack)
		);
		true
	).

