:- ensure_loaded('$REGULUS/PrologLib/compatibility').

:- module(stepper_choose,
	  [choose_item_from_menu/2,
	   choose_rule_from_menu/2,
	   choose_cut_node_from_menu/3]
	 ).

%----------------------------------------------------------------------

:- use_module('$REGULUS/Prolog/stepper_read').
%:- use_module('$REGULUS/Prolog/stepper_rule_db').
:- use_module('$REGULUS/Prolog/stepper_item_db').

:- use_module('$REGULUS/Prolog/regulus_utilities').

:- use_module('$REGULUS/PrologLib/utilities').

:- use_module(library(lists)).
'SICSTUS3/4'( ( :- use_module(library(charsio)) ), ( :- use_module('$REGULUS/PrologLib/compatibility_charsio') ) ).

%----------------------------------------------------------------------

choose_item_from_menu([], _ChosenItem) :-
	!,
	format('~NCan\'t find anything~n~n', []),
	fail.
choose_item_from_menu([ChosenItem], ChosenItem) :-
	!.
choose_item_from_menu(PossibleItems, ChosenItem) :-
	items_to_menu_alist(PossibleItems, MenuAlist),
	Question = 'Select item:',
	choose_from_stepper_text_menu(Question, MenuAlist, ChosenItem).

choose_rule_from_menu(PossibleRules, ChosenRule) :-
	choose_item_from_menu(PossibleRules, ChosenRule).

items_to_menu_alist(Items, MenuAlist) :-
	items_to_item_feats_alist(Items, ItemFeatsAlist),
	keep_only_distinguishing_feats(ItemFeatsAlist, ItemFeatsAlist1),
	item_feats_alist_to_menu_alist(ItemFeatsAlist1, MenuAlist),
	!.
items_to_menu_alist(Items, MenuAlist) :-
	format2error('~N*** Error: bad call: ~w~n', [items_to_menu_alist(Items, MenuAlist)]),
	fail.

%----------------------------------------------------------------------

items_to_item_feats_alist(Items, ItemFeatsAlist) :-
	items_to_item_feats_alist1(Items, 1, ItemFeatsAlist).

items_to_item_feats_alist1([], _I, []).
items_to_item_feats_alist1([F | R], I, [F-Feats | R1]) :-
	item_to_feats(F, I, Feats),
	I1 is I + 1,
	!,
	items_to_item_feats_alist1(R, I1, R1).

item_to_feats(Item, I, OrderedFeats) :-
	findall(GroundedFeat,
		(   feat_for_item(Item, Feat),
		    copy_term(Feat, GroundedFeat),
		    make_ground(GroundedFeat)
		),
		GroundedFeats0),
	GroundedFeats = [id=I | GroundedFeats0],
	order_feats(GroundedFeats, OrderedFeats),
	!.

order_feats(Feats, OrderedFeats) :-
	tag_feats_by_priority(Feats, TaggedFeats),
	keysort(TaggedFeats, OrderedTaggedFeats),
	unkey_list(OrderedTaggedFeats, OrderedFeats),
	!.
order_feats(Feats, OrderedFeats) :-
	format2error('~N*** Error: bad call: ~w~n',
		     [order_feats(Feats, OrderedFeats)]),
	fail.

tag_feats_by_priority([], []).
tag_feats_by_priority([(Feat=Value) | R], [Priority-(Feat=Value) | R1]) :-
	priority_for_feat_and_value(Feat, Value, Priority),
	!,
	tag_feats_by_priority(R, R1).

priority_for_feat_and_value(cat, _Val, 1.0) :- !.
priority_for_feat_and_value(subcat, Val, 2.0) :-
	\+ var_or_grounded_var(Val),
	!.
priority_for_feat_and_value(sem, Val, 3.0) :-
	\+ term_contains_functor(Val, concat/2),
	\+ var_or_grounded_var(Val),
	!.
priority_for_feat_and_value(rule, _Val, 6.0) :- !.
priority_for_feat_and_value(line_info, _Val, 7.0) :- !.
priority_for_feat_and_value(sem, Val, 8.0) :-
	term_contains_functor(Val, concat/2),
	!.
priority_for_feat_and_value(tree, _Val, 9.0) :- !.
priority_for_feat_and_value(sem, Val, 10.0) :-
	var_or_grounded_var(Val),
	!.
priority_for_feat_and_value(id, _Val, 12.0) :- !.
priority_for_feat_and_value(_Other, _Val, 8.0) :- !.
 
%----------------------------------------------------------------------

keep_only_distinguishing_feats(ItemFeatsAlistIn, ItemFeatsAlistOut) :-
	remove_feats_common_to_all_items(ItemFeatsAlistIn, ItemFeatsAlistNext),
	remove_unnecessary_feats(ItemFeatsAlistNext, ItemFeatsAlistOut).

remove_feats_common_to_all_items(ItemFeatsAlistIn, ItemFeatsAlistOut) :-
	remove_feat_common_to_all_items(ItemFeatsAlistIn, ItemFeatsAlistNext),
	!,
	remove_feats_common_to_all_items(ItemFeatsAlistNext, ItemFeatsAlistOut).
remove_feats_common_to_all_items(ItemFeatsAlistIn, ItemFeatsAlistIn).

remove_feat_common_to_all_items(ItemFeatsAlistIn, ItemFeatsAlistOut) :-
	ItemFeatsAlistIn = [_Item-Feats | Rest],
	member(Feat=Val, Feats),
	feat_val_pair_is_in_all_items(Feat=Val, Rest),
	remove_feat_val_pair_from_all_items(ItemFeatsAlistIn, Feat=Val, ItemFeatsAlistOut),
	!.

feat_val_pair_is_in_all_items(_Feat=_Val, []).
feat_val_pair_is_in_all_items(Feat=Val, [F | R]) :-
	feat_val_pair_is_in_item(Feat=Val, F),
	!,
	feat_val_pair_is_in_all_items(Feat=Val, R).

feat_val_pair_is_in_item(Feat=Val, _Item-Pairs) :-
	member(Feat=Val1, Pairs),
	Val == Val1,
	!.

remove_feat_val_pair_from_all_items([], _Feat=_Val, []).
remove_feat_val_pair_from_all_items([F | R], Feat=Val, [F1 | R1]) :-
	remove_feat_val_pair_from_item(F, Feat=Val, F1),
	!,
	remove_feat_val_pair_from_all_items(R, Feat=Val, R1).

remove_feat_val_pair_from_item(Item-Pairs, Feat=Val, Item-Pairs1) :-
	remove_feat_val_pair_from_pairs(Pairs, Feat=Val, Pairs1),
	!.

remove_feat_val_pair_from_pairs([Feat=Val1 | R], Feat=Val, R) :-
	Val == Val1,
	!.
remove_feat_val_pair_from_pairs([F | R], Feat=Val, [F | R1]) :-
	remove_feat_val_pair_from_pairs(R, Feat=Val, R1).

%----------------------------------------------------------------------

remove_unnecessary_feats(ItemFeatsAlistIn, ItemFeatsAlistOut) :-
	remove_unnecessary_feats1(ItemFeatsAlistIn, DifferentiatedFeatsAlist),
	collapse_differentiated_feats_alist(DifferentiatedFeatsAlist, ItemFeatsAlistOut).

remove_unnecessary_feats1([Item-_Feats], item(Item)) :-
	!.
remove_unnecessary_feats1(ItemFeatsAlistIn, DifferentiatedFeatsAlist) :-
	split_by_first_feature(ItemFeatsAlistIn, DifferentiatedFeatsAlist0),
	!,
	(   DifferentiatedFeatsAlist0 = [_KeyVal-ItemFeatsAlistNext] ->
	    remove_unnecessary_feats1(ItemFeatsAlistNext, DifferentiatedFeatsAlist) ;
	    
	    remove_unnecessary_feats2(DifferentiatedFeatsAlist0, DifferentiatedFeatsAlist)
	).

split_by_first_feature(ItemFeatsAlistIn, DifferentiatedFeatsAlist) :-
	empty_assoc_generic(AssocIn),
	split_by_first_feature1(ItemFeatsAlistIn, AssocIn-AssocOut),
	assoc_generic_to_list(AssocOut, DifferentiatedFeatsAlist),
	!.
	
split_by_first_feature1([], AssocIn-AssocIn).
split_by_first_feature1([F | R], AssocIn-AssocOut) :-
	F = Item-[Feat=Val | RestFeats],
	get_assoc_generic_or_empty_list(Feat=Val, AssocIn, InList),
	NextList = [Item-RestFeats | InList],
	put_assoc_generic(Feat=Val, AssocIn, NextList, AssocNext),
	!,
	split_by_first_feature1(R, AssocNext-AssocOut).

remove_unnecessary_feats2([], []).
remove_unnecessary_feats2([F | R], [F1 | R1]) :-
	remove_unnecessary_feats3(F, F1),
	!,
	remove_unnecessary_feats2(R, R1).

remove_unnecessary_feats3((Feat=Val)-ItemFeatsAlist, (Feat=Val)-DifferentiatedFeatsAlist) :-
	remove_unnecessary_feats1(ItemFeatsAlist, DifferentiatedFeatsAlist).

collapse_differentiated_feats_alist(DifferentiatedFeatsAlist, ItemFeatsAlist) :-
	findall(Item-FeatValPairs,
		path_through_differentiated_feats_alist(DifferentiatedFeatsAlist, FeatValPairs, Item),
		ItemFeatsAlist).

path_through_differentiated_feats_alist(Pair-item(Item), [Pair], Item) :-
	!.
path_through_differentiated_feats_alist(Pair-List, [Pair | RestPairs], Item) :-
	!,
	member(DifferentiatedFeatsAlist, List),
	path_through_differentiated_feats_alist(DifferentiatedFeatsAlist, RestPairs, Item).
path_through_differentiated_feats_alist(List, Pairs, Item) :-
	is_list(List),
	!,
	member(DifferentiatedFeatsAlist, List),
	path_through_differentiated_feats_alist(DifferentiatedFeatsAlist, Pairs, Item).

get_assoc_generic_or_empty_list(Key, Assoc, Val) :-
	get_assoc_generic(Key, Assoc, Val),
	!.
get_assoc_generic_or_empty_list(_Key, _Assoc, []).

%----------------------------------------------------------------------

item_feats_alist_to_menu_alist([], []).
item_feats_alist_to_menu_alist([Item-Feats | R], [Item-Text | R1]) :-
	feats_to_text(Feats, Text),
	!,
	item_feats_alist_to_menu_alist(R, R1).

feats_to_text([], '').
feats_to_text([F], FText) :-
	feat_to_text(F, FText),
	!.
feats_to_text([F | R], Result) :-
	feat_to_text(F, FText),
	feats_to_text(R, RText),
	format_to_atom('~w, ~w', [FText, RText], Result).

feat_to_text(rule=rule(RuleSummary, line_info(_ItemNumber, _Cut, LineFrom-LineTo, File)), Text) :-
	(   LineFrom = LineTo ->
	    format_to_atom('~w~n    line ~d~n    ~w', [RuleSummary, LineFrom, File], Text) ;
	    format_to_atom('~w~n    lines ~d to ~d~n    ~w', [RuleSummary, LineFrom, LineTo, File], Text)
	),
	!.
feat_to_text(line_info=line_info(_ItemNumber, _Cut, LineFrom-LineTo, File), Text) :-
	(   LineFrom = LineTo ->
	    format_to_atom('line ~d in ~w', [LineFrom, File], Text) ;
	    format_to_atom('lines ~d to ~d in ~w', [LineFrom, LineTo, File], Text)
	),
	!.
feat_to_text(tree=Tree, Text) :-
	with_output_to_chars(prettyprint_parse_tree(Tree, dont_print_line_info),
			     TreeString0),
	append("\n", TreeString0, TreeString),
	atom_codes(Text, TreeString),
	!.
feat_to_text(Feat, Text) :-
	format_to_atom('~w', [Feat], Text).	

%----------------------------------------------------------------------

choose_cut_node_from_menu([], _Item, _NodeID) :-
	!,
	format('~N~nNo cut nodes found in upper item~n~n', []),
	fail.
choose_cut_node_from_menu([SingleCutNode], _Item, SingleCutNode) :-
	!.
choose_cut_node_from_menu(CutNodes, Item, NodeID) :-
	format('~N~n', []),
	feat_for_item(Item, tree=Tree),
	prettyprint_parse_tree(Tree, print_line_info_and_numbers),
	choose_number_from_list('~N~nChoose one of the following nodes to join on: ~w ',
				'~N"~w" is not an available cut node',
				CutNodes,
				NodeID).