%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Hlavní soubor algoritmu
% main.pl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% test nastaveneho modu
test_mode(Modes) :-
  rule_configuration_dynamic(s_mode, Mode), 
  member(Mode, Modes).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% kompletní inicializace algoritmu
% pouziva se v PrologWrapper
init :-
  assert(rule_configuration_user(0, 1, 2)),
  retractall(rule_configuration_user(0, 1, 2)),
  assert(rule_configuration_user(0, 1)),
  retractall(rule_configuration_user(0, 1)),

  init_import,
  import,
  forall(custom_init, true),
  init_head,
  init_hand,
  init_persons,
  init_virtual_cameras,
  init_person_activity,
  init_main.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
custom_init :-
  true.	  

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
custom_real_time_generate(Time) :-
  true.	  

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pouze inicializace střihu
init_main :-
  init_history,
  retractall(temp_configuration(_, _)),
  set_model_state(undefined),
  ignore((setup_events(Params) -> export('setup', 0, Params))),
  ignore((test_mode([half_realtime, realtime]) -> retractall(person_activity(_, _, _)))).
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% konfiguraci udalosti
% pouziva se v PrologWrapper
setup_events(Params) :-
  bagof((Name, Value), rule_configuration(setup, Name, Value), Params).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% spuštení střihu
run_main :-
  begin(B),
  end(E),
  step(S),
  ignore(do_step(B, E, S)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% spuštění střihu s inicializací
run :-
  init_main,
  run_main.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
runf(File) :-
  tell(File),
  run,
  told.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
runf :-
  runf('output.pl').

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% hlavní smyčka algoritmu
do_stepXXX(Time, End, Step) :-
  (   Time =< End
  ->  ignore((real_time_generate(Time),
	      find_best_camera(Time, Camera, Cameras),
	      insert_output(Time, Camera, Cameras)
	     )),
      T is Time + Step,
      do_step(T, End, Step)
  ).

do_step(Time, End, Step) :-
  (   Time =< End
  ->  ignore(main_step(Time, _)),
      T is Time + Step,
      do_step(T, End, Step)
  ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pouziva se v PrologWrapper
main_step(Time, Params) :-
	real_time_generate(Time),
	find_best_camera(Time, Camera, Cameras),
	insert_output(Time, Camera, Cameras, Params).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pouziva se v TRTEditor v realtime modu
% real_time_generate se nevola zde, protoze se vola v TRTEditor
realtime_step(Time, Params) :-
	find_best_camera(Time, Camera, Cameras),
	insert_output(Time, Camera, Cameras, Params).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% export položky scénáře
insert_output(Time, Camera, Cameras, Params) :-
  findall(Name, output_rule_order(Name), Names),
  evaluate_output_step(Time, Camera, [], Params, Names, Cameras),

  (   last_output_parameters(Time, C, P)
  ->  true
  ;   (C = [], P = [])
  ),

  (   (Camera \= C; Params \= P)
  ->  asserta(output(Time, Camera, Params)),
      export('output', Time, Params),
      flush_output
  ;   fail
  ).
  
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% vyhodnocení váhy jedné kamery - aplikace kamerových pravidel
evaluate_camera(Time, Camera, Weight) :-
  bagof(W, Name^add_camera_rule(Name, Time, Camera, W), Weights1),
  sumlist(Weights1, WS),
  (   bagof(W, Name^mult_camera_rule(Name, Time, Camera, W), Weights2)
  ->  multlist(Weights2, WM),
      Weight is WS * WM
  ;   Weight is WS
  ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% vyhodnocení vah kamer ze seznamu
evaluate_cameras(_, [], []).
evaluate_cameras(Time, [Camera|Cameras], Results):-
  evaluate_cameras(Time, Cameras, NextResults),
  (   evaluate_camera(Time, Camera, Weight), Weight > 0
  ->  append(NextResults, [(Camera, Weight)], Results)
  ;   Results = NextResults
  ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% vyhodnocení vah všech kamer
evaluate_cameras(Time, Results) :-
  ignore((test_mode([realtime]) -> init_moving_virtual_cameras(Time))),
  ignore((test_mode([realtime]) -> init_silverscreen_virtual_cameras(Time))),
  ignore((test_mode([half_realtime, realtime]) -> generate_person_activity(Time))),
  update_last_output_optimized(Time),
  update_model_state(Time),
  set_virtual_camera_time(Time),
  bagof(C, camera(C), Cameras),
  %Results = [].
  evaluate_cameras(Time, Cameras, Results),
  ignore(garbage_collector(Time)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% vytvoření seznamu kamer s maximální vahou
find_max_cameras([], []).
find_max_cameras([CW|CWs], Results) :-
  find_max_cameras(CWs, NextResults),
  (   NextResults == []
  ->  Results = [CW]
  ;   (_, W) = CW,
      nth0(0, NextResults, NCW),
      (_, NW) = NCW,
      (	  NW < W
      ->  Results = [CW]
      ;	  NW > W
      ->  Results = NextResults
      ;	  append(NextResults, [CW], Results)
      )
  ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% nalezení kamery s maximální vahou
find_best_camera(Time, Camera) :-
  find_best_camera(Time, Camera, _).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
find_best_camera(Time, Camera, Cameras) :-
%  write(Time), nl,
  evaluate_cameras(Time, Cameras),
  
  find_max_cameras(Cameras, MaxCameras),
  length(MaxCameras, Count),
  (   Count =:= 0
  ->  fail
  ;   Count =:= 1
  ->  nth0(0, MaxCameras, (Camera, _))
  ;   (   (last_output_optimized(Time, LastCamera), member((LastCamera, _), MaxCameras))
      ->  Camera = LastCamera
%      ;	  Index is 0,
      ;	  Index is random(Count),
	  nth0(Index, MaxCameras, (Camera, _))
      )
  ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% nastavení stavu modelu pořadu
set_model_state(State) :-
  retractall(model_state(_)),
  assert(model_state(State)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% aplikace pravidel pro řízení stavu modelu
update_model_state(Time) :-
  model_state(Old),
  (   model_rule(_, Time, Old, New)
  *-> set_model_state(New)
  ;   true
  ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% aplikace výstupních pravidel
evaluate_output_step(_, _, InOut, InOut, [], _).
evaluate_output_step(Time, Camera, In, Out, [Name|Names], Cameras) :-
  (   output_rule(Name, Time, Camera, In, Out2, Cameras)
  ->  evaluate_output_step(Time, Camera, Out2, Out, Names, Cameras)
  ;   evaluate_output_step(Time, Camera, In, Out, Names, Cameras)
  ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% příprava dat při realtime zpracování
real_time_generate(Time) :-
	ignore(forall(custom_real_time_generate(Time), true)),
	ignore((   test_mode([realtime])
	       ->  step(Step),
		   generate_all_head_move(Time, Step),
		   generate_all_hand_move(Time, Step)
	       )).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
do_realtime_step(Time, Params) :-
%  generate_person_activity(Time),
%  fail,

  once(find_best_camera(Time, Camera)),
  findall(Name, output_rule_order(Name), Names),
  evaluate_output_step(Time, Camera, [], Params, Names),
  (   last_output_parameters(Time, C, P)
  ->  true
  ;   (C = [], P = [])
  ),

  (   (Camera \= C; Params \= P)
  ->  asserta(output(Time, Camera, Params))
  ;   fail
  ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
garbage_collector(Time) :-
	rule_configuration(s_garbage_collector, Length),
	T is Time - Length,
	% vystup
	(   output(T2, _, _), T2 >= T
	->  forall((output(T3, _, _), T3 < T),
		   retractall(output(T3, _, _))
		  )
	;   true
	),

	(   slide(T7, _), T7 >= T
	->  forall((slide(T7, _), T7 < T),
		   retractall(slide(T7, _))
		  )
	;   true
	),

	forall((head(T4, _, _, _, _, _, _, _, _, _), T4 < T),
	       retractall(head(T4, _, _, _, _, _, _, _, _, _))
	      ),
	      
	forall((head_move(T5, _, _, _), T5 < T),
	       retractall(head_move(T5, _, _, _))
	      ),
	      
	forall((hand_move(T6, _, _, _), T6 < T),
	       retractall(hand_move(T6, _, _, _))
	      )
	.
