/*				APPENDIX A
			Section A.2  An Execution Monitor
*/
/***********************************************************************/
/*          The execution monitor calls a recovering procedure         */
/*            to counter-balance external disturbances.                */
/*          The recovery procedure uses a simple planner.              */
/***********************************************************************/
/* :- nodbgcomp.   */
:- pragma(debug). 

/* We allow EXOs be arbitrary Golog programs; hence, if Exo happens
   in S, then S1 is the situation resulting after termination of Exo */

monitor(DeltaOld,S,DeltaNew,S1) :- printMessages(DeltaOld,S),
       sense(Exo,S), 
        ( Exo=noOp -> (S1=S, DeltaNew=DeltaOld); /*No exogenous disturbances */
             /* Otherwise, simulate execution of exogenous */
          offline(Exo,S,S1), 
             /* Exogenous program cannot be redone */
          !, 
          ( relevant(DeltaOld,S,S1) -> 
                            ( write('Start recovering...'), nl,
                              recover(1,4,DeltaOld,S1,DeltaNew) 
                             );
             /* otherwise, continue */
             nl, write(' No recovery is necessary. '),
             write('Proceeding with the next step of program. '), nl,
             DeltaNew=DeltaOld
            )
         ).


/* Determine whether the remainder of the program will
	    terminate in the goal situation  */

relevant(DeltaOld,S,S1) :- 
            not offline(DeltaOld,S1,Sg).


printMessages(Delta,S) :- nl, write('  Program state = '), write(Delta), nl,
	     write('  Current situation: '), write(S), nl.

sense(E,S):- 
	nl, write('>Enter: an exogenous program or noOp if none occurs.'), 
           nl, read(E1),

 /* IF an exogenous is  noOp,  or a terminating (hence, legal) Golog program 
   THEN  this simulator will consider it as just happened.  */

	( (E1 = noOp; offline(E1,S,Sres)) -> E=E1 ;

 /* ELSE  print error message, and try again.  */

    write(">> Your program is not possible. Try again."), nl, sense(E,S)).


recover(M,N,PrOld,S,PrNew) :- M =< N, straightLineProgram(Plan,S,M),
	offline(Plan : PrOld, S, Sg),   /* Can we recover using Plan? */
	PrNew=(Plan : PrOld),      /* Yes: prefix old program by Plan */
	nl, 
	write('  New program = '), write(PrNew), 
	nl;
	/* Otherwise, try to find a longer sequence of actions */
	M < N, 
	/* Complexity bound is not exceeded yet  */
	M_inc is M+1, 
	recover(M_inc,N,PrOld,S,PrNew).

recover(M,N,PrOld,S,PrNew) :- M > N, nl,
		write('-------------------------'),
		write('|  Recovery procedure FAILED  |'), 
		write('-------------------------'),nl,
		PrNew=nil.

straightLineProgram(Plan,S,K) :- K >= 1, 
	 primitive_action(A), poss(A,S), 
		goodAction(Plan,A),
		( K =:= 1  -> Plan=A ;
		  K_dec is K -1,
		  straightLineProgram(TailPlan,do(A,S),K_dec),
		    Plan=(A : TailPlan)
		). 

/* Elaborate in the future: 
	A is a good action to perform after Plan if A does not undo 
	the results of the last action in Plan. Otherwise, A is futile.
*/
goodAction(Plan,A).  

/*--------------------- An execution example ----------------------       

Example below demonstrates that exos can be counterbalanced up to
the moment when  ?(goal) was not yet performed.

>Enter: an exogenous program or noOp if none occurs.
	move(s7,r1).
Start recovering...

  New program = moveToTable(s7) : nil : ?(goal)

  Program state = nil : nil : ?(goal)
  Current situation: do(moveToTable(s7), do(move(s7, r1), do(move(r1, o1), 
do(moveToTable(r1), do(move(r1, o1), do(move(o1, m1), do(move(m1, e1), 
do(move(f, r2), do(move(r2, n), do(moveToTable(n), do(moveToTable(s7), 
do(moveToTable(r2), do(move(r2, s7), do(move(s7, n), do(move(n, e1), 
s0)))))))))))))))

>Enter: an exogenous program or noOp if none occurs.
	noOp.

  Program state = nil
  Current situation: do(moveToTable(s7), do(move(s7, r1), do(move(r1, o1), 
do(moveToTable(r1), do(move(r1, o1), do(move(o1, m1), do(move(m1, e1), 
do(move(f, r2), do(move(r2, n), do(moveToTable(n), do(moveToTable(s7), 
do(moveToTable(r2), do(move(r2, s7), do(move(s7, n), do(move(n, e1), 
s0)))))))))))))))

>Enter: an exogenous program or noOp if none occurs.
	moveToTable(r1).

 No recovery is necessary. Proceeding with the next step of program. 

yes.
*/