% This file is part of the Attempto Parsing Engine (APE).
% Copyright 2008-2013, Attempto Group, University of Zurich (see http://attempto.ifi.uzh.ch).
%
% The Attempto Parsing Engine (APE) is free software: you can redistribute it and/or modify it
% under the terms of the GNU Lesser General Public License as published by the Free Software
% Foundation, either version 3 of the License, or (at your option) any later version.
%
% The Attempto Parsing Engine (APE) is distributed in the hope that it will be useful, but WITHOUT
% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
% PURPOSE. See the GNU Lesser General Public License for more details.
%
% You should have received a copy of the GNU Lesser General Public License along with the Attempto
% Parsing Engine (APE). If not, see http://www.gnu.org/licenses/.


:- module(drs_to_npace, [
		drs_to_npace/2
	]).

/** <module> Attempto DRS to ACE NP translator

In general we handle only those DRSs which contain only implications, but
there is some support also for objects and predicates in the toplevel.

@author Kaarel Kaljurand
@version 2011-11-09

TODO:

* Support 'should' and 'may' (also in negation etc.)

* DONE: Support for disjunction. Interaction between AND/OR. E.g. Every man works, and eats or sleeps.

* We could use a reflexive pronoun if DeepSubj = DeepObj
problem: himself/herself vs itself, no general reflexive pronoun exists (e.g. 'self').

* Takes very long and fails:
FIXED: Every man sees a dog that sees a cat and that sees a rat and sees a mouse that sees an ant.

* If John waits then he sleeps. (not possible with 'every': Every John ...)
* If there are 2 men then they wait. (not possible with 'every')
* If there is a carnivore then everybody that it eats is an animal. (embedded if-then)
* If a man waits then a dog barks. (no argument-sharing)
* Every animal X hates an animal Y that eats X. (strange paraphrase but it is correct)
* Anaphoric references and looping and new objects via definite NPs. (find an example!!!)
* One idea was to fail whenever there are more than one solutions.
Unfortunately this is almost always the case.

Some notes relocated from the PhD draft:

* Can some of the scope problems that Core ACE has be solved?
* of-constructions and 'whose' and everybody's?
* Loops: are there cases where we have to use a definite NP which contains a relative clause.
Note: we can use variables.


Usage:

* ?- tnp("Every man who likes a dog which sees a rat hates a cat.").
* ?- tnp('Every man who likes a dog hates a cat and sees a rat.').
* ?- tnp('Every man who likes a dog is hated by a cat.').
* ?- tnp("Every man who can see a dog is hated by a cat.").
*/


:- use_module(drs_to_sdrs).

:- use_module(implication_turn, [
		implication_turn/2
	]).

:- use_module(morphgen, [
		clear_vars/0,
		add_var/1,
		remove_singletons/2,
		listlist_listatom/2,
		surface_noun/4,
		surface_verb/3,
		surface_neg_verb/3
	]).


% Operators used in the DRS.
:- op(400, fx, -).
:- op(400, fx, ~).
:- op(500, xfx, =>).
:- op(500, xfx, v).


/*
:- debug(verbose).
:- debug(toplevel).
:- debug(turn).
:- debug(cond).
*/


:- dynamic ref_to_noun/5.


%% drs_to_npace(+Drs:drs, -AceSentenceList:list) is det.
%
% @param Drs is an Attempto DRS
% @param AceSentenceList is a list of NP ACE sentences (atoms) corresponding to the DRS
%
drs_to_npace(Drs, AceSentenceList) :-
	drs_acetext_np_x(Drs, AceList),
	remove_singletons(AceList, AceListPruned),
	listlist_listatom(AceListPruned, AceSentenceList),
	!.

drs_to_npace(_Drs, []).
% TODO: Throw an exception
%drs_to_npace(Drs, _) :-
%	throw(error('Not implemented', context(drs_to_npace/2, Drs))).


%% drs_acetext_np(+Drs:drs, -AceText:list) is det.
%
% Translates a DRS into NP ACE.
%
% 1. Simplify the DRS.
% 2. Assert all nouns and propernames, return referents to toplevel nouns.
% 3. Verbalize the toplevel predicates (John owns a dog.)
% 4. Verbalize the toplevel nouns (There is a man.)
% 5. Preserve only implications.
% 6. Verbalize the implications.
%
% @param Drs is an Attempto DRS
% @param AceText NP ACE text (Prolog list of lists) corresponding to the DRS
%
% BUG: toplevel conditions (e.g. negation, relation/3, etc) which are not supported are ignored,
% but it doesn't cause a failure like in the previous versions.

drs_acetext_np_x(Drs, AceText) :-
	copy_term(Drs, DrsCopy),
	drs_to_sdrs(DrsCopy, DrsSimpleWithNamed),

	% BUG: temporary: remove named-object-conditions
	exclude(is_named, DrsSimpleWithNamed, DrsSimple),

	ape_numbervars(DrsSimple, 0, _),
	retractall(ref_to_noun(_, _, _, _, _)),
	clear_vars,
	retractall(morphgen:name_namespace(_, _)),

	get_toplevel_referents(DrsSimple, toplevel(ToplevelReferents, UnsortedSubjectList, UnsortedObjectList, NamedList)),
	subtract(ToplevelReferents, NamedList, UnnamedReferentList),
	subtract(UnnamedReferentList, UnsortedObjectList, List1),
	append(UnsortedSubjectList, List1, List2),
	list_to_set(List2, SubjectList),
	subtract(SubjectList, NamedList, UnnamedSubjectListWithNamed),
	% BUG: temporary: remove named-object-conditions
	exclude(is_named_ref, UnnamedSubjectListWithNamed, UnnamedSubjectList),
	debug(toplevel, 'Toplevel referents: all: ~w; subjects: ~w; unnamed subjects: ~w~n', [ToplevelReferents, SubjectList, UnnamedSubjectList]),

	individuals_ace(UnnamedSubjectList, AceSubjectList),

	debug(toplevel, 'Individuals (nouns): ~w~n', [AceSubjectList]),

	predicates_ace(DrsSimple, UnnamedSubjectList, RefsOut, AceTextListProperties),

	debug(toplevel, 'Properties: ~w~n', [AceTextListProperties]),

	subtract(UnnamedReferentList, RefsOut, Remaining2WithNamed),
	exclude(is_named_ref, Remaining2WithNamed, Remaining2),

	individuals_ace(Remaining2, AceTextListIndividuals),

	include(is_implication, DrsSimple, Implications),

	% BUG: we don't need the cut here, instead we should make sure that
	% there is no backtracting in the previous statements
	%!,

	drs_ace(Implications, UnnamedReferentList, AceTextListClasses),

	append(AceTextListProperties, AceTextListIndividuals, Tmp),
	append(AceSubjectList, Tmp, Tmp1),
	append(Tmp1, AceTextListClasses, AceText),
	debug(toplevel, 'All: ~w~n', [AceText]).


% BUG: temporary: remove named-object-conditions
is_named(object(named(Name), Name, named, _, _, _)-_).
is_named_ref(named(_)).


%% is_implication(+Condition)
%
% True if condition is an implication.
%
% @param Condition DRS condition
%
is_implication(_ => _).


%% acefragment_acesentence(+Fragment:list, -Sentence) is det.
%
%
acefragment_acesentence(Fragment, FragmentFlat) :-
	flatten(Fragment, FragmentFlat).


%% individuals_ace(+Referents:list, -AceText:list) is det.
%
% Generates a there-is sentence for each referent.

individuals_ace([], []).

individuals_ace([Ref | Refs], [AceSentence | AceTexts]) :-
	get_noun([], yes, no, Ref, Num, SurfaceNoun),
	num_sgpl_copula(Num, _, Copula),
	acefragment_acesentence(['there', Copula, SurfaceNoun], AceSentence),
	individuals_ace(Refs, AceTexts).


%% predicates_ace(+Predicates:list, +RefsIn:list, -RefsOut:list, -AceText:list) is det.
%
% Here we scan all the predicates and also negations and disjunctions
% in the toplevel DRS. We ignore object/6 and the implication.
% All other DRS-conditions (relation/3 etc)
% cause a failure (as NP ACE does not currently support them).
%
predicates_ace([], Refs, Refs, []).

% BUG: this implementation is buggy, we rather fall back to Core ACE.
/*
predicates_ace(
	[-Cond | RestPredicates],
	RefsIn,
	RefsOut,
	[AceSentence | RestAceTexts]
	) :-
	!,
	get_main_subject(Cond, SubjRef),
	get_noun(RefsIn, yes, no, SubjRef, _SubjDom, SurfaceSubj),
	cond_acetext(-Cond, [yes, _Conj, [SubjRef | RefsIn]-RefsTmp, then, Ref, yes:no, Ref:_], AceText),
	acefragment_acesentence([SurfaceSubj, AceText], AceSentence),
	predicates_ace(RestPredicates, RefsTmp, RefsOut, RestAceTexts).
*/

% BUG: this implementation is buggy, we rather fall back to Core ACE.
/*
predicates_ace(
	[Cond1 v Cond2 | RestPredicates],
	RefsIn,
	RefsOut,
	[AceSentence | RestAceTexts]
	) :-
	!,
	get_main_subject(Cond1, SubjRef),
	get_noun(RefsIn, yes, no, SubjRef, _SubjDom, SurfaceSubj),
	cond_acetext(Cond1 v Cond2, [yes, _Conj, [SubjRef | RefsIn]-RefsTmp, then, Ref, yes:no, Ref:_], AceText),
	acefragment_acesentence([SurfaceSubj, AceText], AceSentence),
	predicates_ace(RestPredicates, RefsTmp, RefsOut, RestAceTexts).
*/

predicates_ace(
	[predicate(_, Verb, Subj, Obj)-_ | RestPredicates],
	RefsIn,
	RefsOut,
	[AceSentence | RestAceTexts]
	) :-
	!,
	get_noun(RefsIn, yes, no, Subj, SubjNum, SurfaceSubj),
	get_noun([Subj | RefsIn], yes, no, Obj, _ObjDom, SurfaceObj),
	num_sgpl_copula(SubjNum, SgPl, _Copula),
	surface_verb(SgPl, Verb, SurfaceVerb),
	acefragment_acesentence([SurfaceSubj, SurfaceVerb, SurfaceObj], AceSentence),
	predicates_ace(RestPredicates, [Subj, Obj | RefsIn], RefsOut, RestAceTexts).

predicates_ace(
	[predicate(_, Verb, Subj)-_ | RestPredicates],
	RefsIn,
	RefsOut,
	[AceSentence | RestAceTexts]
	) :-
	!,
	get_noun(RefsIn, yes, no, Subj, SubjNum, SurfaceSubj),
	num_sgpl_copula(SubjNum, SgPl, _Copula),
	surface_verb(SgPl, Verb, SurfaceVerb),
	acefragment_acesentence([SurfaceSubj, SurfaceVerb], AceSentence),
	predicates_ace(RestPredicates, [Subj | RefsIn], RefsOut, RestAceTexts).

predicates_ace([object(_, _, _, na, _, _)-_ | RestPredicates], RefsIn, RefsOut, RestAceTexts) :-
	!,
	predicates_ace(RestPredicates, RefsIn, RefsOut, RestAceTexts).

predicates_ace([[has_part(_, _)-_] => _ | _], _, _, _) :-
	!,
	fail.

% BUG: we should verbalize it now, not later
predicates_ace([_ => _ | RestPredicates], RefsIn, RefsOut, RestAceTexts) :-
	predicates_ace(RestPredicates, RefsIn, RefsOut, RestAceTexts).


%% drs_ace(+Drs:drs, +TopLevelObjects:list, -AceTextList:list) is det.
%
% Just a simplifying wrapper.

drs_ace(Drs, ToplevelObjects, AceTextList) :-
	conds_acetext(Drs, [yes, _, ToplevelObjects-_, _, _, _, _], AceTextList).


%% conds_acetext(+YesNo:atom, +Conj:atom, +Refs:functor, +Box:atom, +MainSubj:atom, +InOut:functor, +Conds:list, -AceTexts:list) is det.
%
% @param Conds list of DRS conditions.
% @param Features A list of features (see =|cond_acetext/3|=).
% @param AceTexts ACE text corresponding to the list of DRS conditions.
%
conds_acetext([], [_, _, Refs-Refs, _, _, F:F, In:In], []).

conds_acetext([Cond | Conds], [YesNo, Conj, RefsIn-RefsOut, Box, MainSubj, FIn:FOut, In:Out], [AceText | AceTexts]) :-
	cond_acetext(Cond, [YesNo, Conj, RefsIn-RefsTmp, Box, MainSubj, FIn:FTmp, In:Tmp], AceText),
	get_coordinator(Cond, Conj, NewConj),
	conds_acetext(Conds, [YesNo, NewConj, RefsTmp-RefsOut, Box, MainSubj, FTmp:FOut, Tmp:Out], AceTexts).



%% cond_acetext(+Cond:term, +Features:list, -AceSentence:list) is det.
%
% @param Cond is a DRS condition.
% @param Features is a list of features.
% @param AceSentence is an ACE sentence corresponding to the DRS condition.
%
% Features:
%
% * YesNo {yes, no}, indicates whether the condition is not negated.
% * Conj {and, ,and}, indicates the conjunction to be used.
% * Refs RefsIn-RefsOut carries around the referents encountered so far.
% * Box {if, then}, indicates whether the condition is in the IF-box or THEN-box.
% * MainSubj discourse referent of the main subject.
% * FInOut Fin:FOut carries around {yes, no} that indicates whether the condition is the first one in the box.
% * InOut In:Out carries around the current subject.
%
% BUG: kaarel: 060805: here we reject stuff, but we should reject even more,
% e.g. everything that is not in if-then. To do this we need levels' support
% like in implication_turn.pl.
%
% BUG: after handling the if-then, we can "cut it out" and verbalize the rest
% with Drace. That should work, since if-then is not reachable for the
% rest of the DRS.

cond_acetext(Condition-_, _Features, _AceText) :-
	functor(Condition, F, Args),
	(
		F = modifier_adv
	;
		F = modifier_pp
	;
		F = has_part
	;
		F = property
	;
		F = relation
	;
		F = query
	;
		F = predicate, Args = 6
	),
	!,
	fail.

cond_acetext(_:_, _, _) :- !, fail.

cond_acetext(~_, _, _) :- !, fail.

% If there is a dog then there is a cat.
% This should fail, since we can't handle it with NPs.
% BUG: But this rule should be made more general to cover e.g. negation and conjunction of object/8.
cond_acetext(_ => [object(_, _, _, _, _, _)-_], _, _) :-
	!,
	fail.

% Here we handle all kinds of implication forms.
% They all produce full sentences.
cond_acetext(A => B, Features, AceSentence) :-
	!,
	implication_turn_acetext(A => B, Features, AceSentence).


% v (disjunction)
cond_acetext(Conds1 v Conds2, [C, Coord, RefsIn-RefsIn, Box, MainSubj, FIn:no, Subj:Subj], [AceText1, or, AceText2]) :-
	!,
	make_comma_and_if_needed(Coord, NewCoord),
	conds_acetext(Conds1, [C, NewCoord, RefsIn-RefsTmp, Box, MainSubj, FIn:no, Subj:_], AceText1),
	conds_acetext(Conds2, [C, [], RefsTmp-_, Box, MainSubj, no:no, Subj:_], AceText2).


% F = -, can, must, should, may (not, can, must, should, may)
% BUG: why not In:In, i.e. why do we expose the embedded subject to the upper level?
% In case of In:In there was one regression. Study it further!
cond_acetext(Cond, [C, Conj, RefsIn-RefsOut, Box, MainSubj, FIn:no, In:Out], [AceText]) :-
	functor(Cond, F, 1),
	arg(1, Cond, Conds),
	modify_first_verb(F, Conds, Subj, NewConds),
	check_conds(Conds, Subj),
	!,
	conds_acetext(NewConds, [C, Conj, RefsIn-RefsOut, Box, MainSubj, FIn:no, In:Out], AceText).


% predicate-condition will be verbalized.
cond_acetext(Predicate, [Neg, Conj, RefsIn-RefsOut, Box, MainSubj, FIn:FOut, A:B], [Subj, Binder, Verb, Obj]) :-
	predicate_acefragment(Predicate, [Conj, Box, MainSubj, FIn:FOut, A:B], [DeepSubj, Binder, DeepVerb, DeepObj]),
	get_noun([], Neg, FIn, DeepSubj, _, Subj),
	get_noun(RefsIn, yes, no, DeepObj, _, Obj),
	ref_to_noun_wrapper(B, _, _, _, Number),
	num_sgpl_copula(Number, SgPl, _),
	get_verb(DeepVerb, SgPl, Verb),
	add_refs(RefsIn, DeepObj, RefsOut).


% Fallback (catches only object/8)
cond_acetext(object(_, _, _, _, _, _)-_, [_C, _Conj, Refs-Refs, _Box, _MSubj, F:F, Subj:Subj], [[], [], [], []]).


% BUG: clean this up, the "1" is not correct,
% in case the named-object is in plural.
ref_to_noun_wrapper(named(_), _, _, _, 1) :- !.

ref_to_noun_wrapper(B, _, _, _, Number) :-
	ref_to_noun(B, _, _, _, Number).



%% implication_turn_acetext(+Implication:term, +Features:list, -AceText:list) is det.
%
% Turns the implication box and verbalizes the result.

implication_turn_acetext(A => B, Features, AceSentence) :-
	implication_turn(A => B, ATurned => BTurned),
	implication_acetext(ATurned => BTurned, Features, AceFragment),
	!,
	acefragment_acesentence(AceFragment, AceSentence).

implication_turn_acetext(_, _, []).



%% implication_acetext(+Cond:functor, +Features:list, -AceText:list) is det.
%
% @param Cond A DRS condition.
% @param Features A list of features (see =|cond_acetext/3|=).
% @param AceText ACE text corresponding to the list of DRS conditions.

% Every man does not wait.
% Every man waits.
implication_acetext([object(Ref, _Lemma, Quant, _, Eq, Number)-_] => Conds2, [NegIn, _, RefsIn-RefsOut, _, _, _, _], [AceText1, AceText2]) :-
	!,
	countable_or_mass_or_dom(Quant),
	(
		Eq = eq,
		Number = 1
	;
		Eq = na,
		Number = na
	),
	(
		Conds2 = [-NotConds]
	->
		NegOut = no,
		Conds2Out = NotConds
	;
		NegOut = yes,
		Conds2Out = Conds2
	),
	get_noun([], NegOut, yes, Ref, _, AceText1),
	%get_noun_x(Ref, new, NegOut, yes, Quant, _Lemma, Eq, Number, AceText1),
	%add_var(Ref),
	conds_acetext(Conds2Out, [NegIn, and, [Ref | RefsIn]-RefsOut, then, Ref, yes:no, Ref:_], AceText2).


% Every man who sleeps does not wait.
% Every man who sleeps waits.
implication_acetext(Conds1 => Conds2, [NegIn, _, RefsIn-RefsOut, _, _, _, _], [AceText1, AceText2]) :-
	select(object(Ref, _, Quant, _, Eq, Number)-_, Conds1, Conds1Out),
	countable_or_mass_or_dom(Quant),
	(
		Eq = eq,
		Number = 1
	;
		Eq = na,
		Number = na
	),
	(
		Conds2 = [-NotConds]
	->
		NegOut = no,
		Conds2Out = NotConds
	;
		NegOut = NegIn,
		Conds2Out = Conds2
	),
	conds_acetext(Conds1Out, [NegOut, and, [Ref | RefsIn]-RefsTmp, if, Ref, yes:no, Ref:_], AceText1),
	conds_acetext(Conds2Out, [NegIn, and, RefsTmp-RefsOut, then, Ref, yes:no, Ref:_], AceText2).


%% predicate_acefragment(+Cond:term, +Features:list, -AceFragment:list) is det.
%
% @param Cond A DRS condition.
% @param Features A list of features.
% @param AceFragment An ACE fragment (part of the sentence) corresponding to the DRS condition.
%	The fragment has the form of [Subject, Coordinator, Verb, Object]
%
% Features:
%
% * Conj {and, ,and}, indicates the conjunction to be used (either the strong or the weak one).
% * Box {if, then}, indicates whether the condition is in the IF-box or THEN-box.
% * MainSubj discourse referent of the main subject.
% * FirstIn:FirstOut {yes, no} Is it the first element to be verbalized in the IF-box or THEN-box.
% * In:Out carries around the discourse referent of the current subject.
%
% Some examples of suported positive and negative sentences:
%
% * OK: Every man sees a cat and sees a dog.
% * NOT OK: Every man sees a cat and that sees a dog.
% * OK: Every man sees a cat that sees a mouse and that sees a dog.

% In THEN-box only.
% Every man [HATES A RAT] ...
% Every man that sees a dog [HATES A RAT] ...
% Every man that sees a dog and that hears a cat that eats a mouse [HATES A RAT] ...
predicate_acefragment(predicate(_, Verb, Subj, Obj)-_, [_, then, Subj, yes:no, Subj:Subj], [[], [], Verb, Obj]) :- !.


% In THEN-box only.
% Every man likes a cat [AND DRINKS SOME BEER].
% Every man likes a cat [OR DRINKS SOME BEER].
predicate_acefragment(predicate(_, Verb, Subj, Obj)-_, [Conj, then, Subj, no:no, _:Subj], [[], Conj, Verb, Obj]) :- !.


% In the IF-box only.
% Every [MAN THAT SEES A DOG] ...
predicate_acefragment(predicate(_, Verb, Subj, Obj)-_, [_, if, Subj, yes:no, Subj:Subj], [Subj, 'that', Verb, Obj]) :- !.


% Every man that sees a dog and that hears a cat [THAT EATS A MOUSE] ...
predicate_acefragment(predicate(_, Verb, NewSubj, Obj)-_, [_, _, _, no:no, Subj:NewSubj], [[], 'that', Verb, Obj]) :- Subj \= NewSubj, !.


% Every man that sees a dog [AND THAT HEARS A CAT] ...
% Every man that sees a dog [OR THAT HEARS A CAT] ...
predicate_acefragment(predicate(_, Verb, Subj, Obj)-_, [Conj, _, _, no:no, Subj:Subj], [[], [Conj, that], Verb, Obj]) :- !.




% Intransitive verb support

% In THEN-box only.
% Every man [WAITS] ...
predicate_acefragment(predicate(_, Verb, Subj)-_, [_, then, Subj, yes:no, Subj:Subj], [[], [], Verb, []]) :- !.

% In THEN-box only.
% Every man waits [AND eats] ...
% Every man waits [OR eats] ...
predicate_acefragment(predicate(_, Verb, Subj)-_, [Conj, then, Subj, no:no, _:Subj], [[], Conj, Verb, []]) :- !.

% In the IF-box only.
% Every man [THAT WAITS] eats ...
predicate_acefragment(predicate(_, Verb, Subj)-_, [_, if, Subj, yes:no, Subj:Subj], [Subj, 'that', Verb, []]) :- !.

predicate_acefragment(predicate(_, Verb, NewSubj)-_, [_, _, _, no:no, Subj:NewSubj], [[], 'that', Verb, []]) :- Subj \= NewSubj, !.

predicate_acefragment(predicate(_, Verb, Subj)-_, [Conj, _, _, no:no, Subj:Subj], [[], [Conj, that], Verb, []]).



%% get_verb(+DeepVerb:functor, +SgPl:atom, -SurfaceVerb:list) is det.
%
% @param DeepVerb Examples: see, not(see), i(see), not(i(see)), ...
% @param SgPl {sg, pl} Shows the number of the subject of the verb.
% @param SurfaceVerb Examples: [sees], [does, not, see], [is, seen, by], [is, not, seen, by]
%
% @bug FIXED: Needs plural support (i.e. 'do not ...', 'are not')
% @bug not(not(be)) not supported: Every man is not no dog. (?)
% @bug not(not(Verb)) not supported: Every man does not see no dog.
% @bug implement/reject can(not(i(be)), ...

get_verb([], _, []) :-
	!.

% BUG: special case if the verb is copula
% this should be handled for other cases as well (can, cannot, ...)
get_verb(not(i(be)), SgPl, IsAreNot) :-
	surface_neg_verb(SgPl, be, IsAreNot),
	!.

get_verb(not(i(Verb)), SgPl, [IsAreNot, VerbEd, by]) :-
	surface_neg_verb(SgPl, be, IsAreNot),
	surface_verb(part, Verb, VerbEd),
	!.

get_verb(can(i(be)), _, [can, be]) :-
	!.

get_verb(can(i(Verb)), _, [can, be, VerbEd, by]) :-
	surface_verb(part, Verb, VerbEd),
	!.

get_verb(must(i(be)), _, [must, be]) :-
	!.

get_verb(must(i(Verb)), _, [must, be, VerbEd, by]) :-
	surface_verb(part, Verb, VerbEd),
	!.

get_verb(should(i(be)), _, [should, be]) :-
	!.

get_verb(should(i(Verb)), _, [should, be, VerbEd, by]) :-
	surface_verb(part, Verb, VerbEd),
	!.

get_verb(may(i(be)), _, [may, be]) :-
	!.

get_verb(may(i(Verb)), _, [may, be, VerbEd, by]) :-
	surface_verb(part, Verb, VerbEd),
	!.

/*
get_verb(not(not(be)), _, [is, bug_not]) :-
	!.

get_verb(not(not(Verb)), _, [does, bug_not, Verb]) :-
	!.
*/


/*
get_verb(not(be), SgPl, IsAreNot) :-
	surface_neg_verb(SgPl, be, IsAreNot),
	!.
*/

get_verb(not(Verb), SgPl, NegVerb) :-
	surface_neg_verb(SgPl, Verb, NegVerb),
	!.

get_verb(can(not(i(Verb))), _, [cannot, be, VerbEd, by]) :-
	surface_verb(part, Verb, VerbEd),
	!.

get_verb(can(not(Verb)), _, [cannot, Verb]) :-
	!.

get_verb(can(Verb), _, [can, Verb]) :-
	!.

get_verb(must(not(i(Verb))), SgPl, [NegHave, to, be, VerbEd, by]) :-
	surface_neg_verb(SgPl, have, NegHave),
	surface_verb(part, Verb, VerbEd),
	!.

get_verb(must(not(Verb)), SgPl, [NegHave, to, Verb]) :-
	surface_neg_verb(SgPl, have, NegHave),
	!.

get_verb(must(Verb), _, [must, Verb]) :- !.
get_verb(should(Verb), _, [should, Verb]) :- !.
get_verb(can(Verb), _, [can, Verb]) :- !.

% BUG: special case if the verb is copula
% this should be handled for other cases as well (can, cannot, ...)
get_verb(i(be), sg, is) :-
	!.

get_verb(i(Verb), sg, [is, VerbEd, by]) :-
	surface_verb(part, Verb, VerbEd),
	!.

get_verb(i(Verb), pl, [are, VerbEd, by]) :-
	surface_verb(part, Verb, VerbEd),
	!.

get_verb(Verb, SgPl, SurfaceVerb) :-
	surface_verb(SgPl, Verb, SurfaceVerb).



%% get_noun(+Refs:list, +Positive:atom, +Pos:atom, +Ref:term, -Number:atomic, -NP:list) is det.
%
% @param Refs is a list of discourse referents (numbervared)
% @param Positive is in {yes, no} depending on whether the NP is positive or under negation
% @param Pos is in {yes, no} depending on whether the word is in the beginning of the sentence or not (BUG?)
% @param Ref is a discourse referent (numbervared)
% @param Number is a positive integer or 'na'
% @param NP is an ACE noun phrase (specifically: a determiner and a noun)

% BUG: is this needed? yes, removing it causes some regression
% BUG: sg is wrong anyway
get_noun(_, _, _, [], 1, []) :- !.

% BUG: The Num-detection does not seem to work.
get_noun(_Refs, _Positive, _Pos, named(Name), Num, NameText) :-
	surface_noun(pn, Name, SgPl, NameText),
	(SgPl = sg -> Num = 1 ; Num = 2),
	!.

get_noun(Refs, Positive, Pos, Ref, Number, NP) :-
	ref_to_noun(Ref, Quantisation, Noun, Operator, Number),
	get_det(Ref, Refs, OldNew),
	add_var(Ref),
	get_noun_x(Ref, OldNew, Positive, Pos, Quantisation, Noun, Operator, Number, NP),
	!.


%% get_noun_x(+Ref:numbervar, +OldNew:atom, +Positive:atom, +Pos:atom, +Quantisation:atom, +Noun:atom, +Operator:atom, +Number:atomic, -NP:list) is det.
%
% @param Ref is a discourse referent (numbervared)
% @param OldNew is in {old, new}
% @param Positive is in {yes, no} depending on whether the NP is positive or under negation
% @param Pos is in {yes, no} depending on whether the word is in the beginning of the sentence or not (BUG?)
% @param Quantisation is in {dom, countable, mass, named}
% @param Noun is a lemma of a noun, proper name, or indefinite pronoun
% @param Operator is in {na, eq, leq, geq, greater, less, exactly}
% @param Number is a positive integer or 'na'
% @param NP is an ACE noun phrase (specifically: a determiner and a noun)
%
% BUG: Note that referents (variables) are not added to plural nouns. Think about it.
% BUG: we should cut here more
%
get_noun_x(Ref, _, yes, yes, countable, somebody, eq, 1, ['everybody', Ref]).
get_noun_x(Ref, _, yes, yes, dom, something, na, na, ['everything', Ref]).
get_noun_x(Ref, _, no, yes, countable, somebody, eq, 1, ['nobody', Ref]).
get_noun_x(Ref, _, no, yes, dom, something, na, na, ['nothing', Ref]).
get_noun_x(Ref, new, yes, _, countable, somebody, eq, 1, [somebody, Ref]).
get_noun_x(Ref, new, yes, _, dom, something, na, na, [something, Ref]).
get_noun_x(Ref, old, yes, _, countable, somebody, eq, 1, [Ref]).
get_noun_x(Ref, old, yes, _, dom, something, na, na, [Ref]).
get_noun_x(Ref, _, yes, yes, countable, Noun, eq, 1, ['every', NounSg, Ref]) :- surface_noun(cn, Noun, sg, NounSg).
%
get_noun_x(Ref, _, yes, yes, mass, Noun, na, na, ['all', NounSg, Ref]) :- surface_noun(cn, Noun, mass, NounSg).
get_noun_x(Ref, _, no, yes, countable, Noun, eq, 1, ['no', NounSg, Ref]) :- surface_noun(cn, Noun, sg, NounSg).
%
get_noun_x(Ref, _, no, yes, mass, Noun, na, na, ['no', NounSg, Ref]) :- surface_noun(cn, Noun, mass, NounSg). % BUG: ambiguity
get_noun_x(Ref, new, yes, _, countable, Noun, eq, 1, [a, NounSg, Ref]) :- surface_noun(cn, Noun, sg, NounSg).
get_noun_x(Ref, new, yes, _, mass, Noun, na, na, [some, NounSg, Ref]) :- surface_noun(cn, Noun, mass, NounSg).
get_noun_x(Ref, old, yes, _, countable, Noun, _, 1, [the, NounSg, Ref]) :- surface_noun(cn, Noun, sg, NounSg).
% BUG: number was 1. why?
get_noun_x(Ref, old, yes, _, mass, Noun, na, na, [the, NounSg, Ref]) :- surface_noun(cn, Noun, mass, NounSg).

% BUG: was group
get_noun_x(_, old, yes, _, countable, Noun, _, _, [the, NounPl]) :- surface_noun(cn, Noun, pl, NounPl).

% Num = {0, 1, ...}
get_noun_x(_, new, yes, _, countable, Noun, eq, Num, [Num, Form]) :- num_num(Num, SgPl), surface_noun(cn, Noun, SgPl, Form).
get_noun_x(_, new, yes, _, countable, Noun, less, Num, [less, than, Num, Form]) :- num_num(Num, SgPl), surface_noun(cn, Noun, SgPl, Form).
get_noun_x(_, new, yes, _, countable, Noun, greater, Num, [more, than, Num, Form]) :- num_num(Num, SgPl), surface_noun(cn, Noun, SgPl, Form).
get_noun_x(_, new, yes, _, countable, Noun, leq, Num, [at, most, Num, Form]) :- num_num(Num, SgPl), surface_noun(cn, Noun, SgPl, Form).
get_noun_x(_, new, yes, _, countable, Noun, geq, Num, [at, least, Num, Form]) :- num_num(Num, SgPl), surface_noun(cn, Noun, SgPl, Form).
get_noun_x(_, new, yes, _, countable, Noun, exactly, Num, [exactly, Num, Form]) :- num_num(Num, SgPl), surface_noun(cn, Noun, SgPl, Form).


%% num_num(+Num:integer, -SgPl:atom) is det.
%
% Examples: 0 cars, 1 car, 4 cars
%
% @param Num is in {0, 1, ...}
% @param SgPl is in {sg, pl}
%
num_num(0, pl) :- !.
num_num(1, sg) :- !.
num_num(N, pl) :- N > 1.


%% get_det(+Element:term, +List:list, -OldNew:atom) is det.
%
%
get_det(El, List, old) :-
	member(El, List),
	!.

get_det(_, _, new).


%% get_toplevel_referents(+Conds:list, -Toplevel:term) is det.
%
% Asserts all common nouns and propernames into ref_to_noun/5.
% Returns a list of toplevel referents for objects.
%
get_toplevel_referents(Conds, toplevel(ReferentList, SubjectList, ObjectList, NamedList)) :-
	store_objects(Conds, toplevel(-([], ReferentList), -([], SubjectList), -([], ObjectList), -([], NamedList))).


%% store_object(+Conds:list, -Toplevel:term) is det.
%
%
store_objects([], toplevel(R-R, S-S, O-O, N-N)).

store_objects([Cond | Conds], toplevel(R1-R2, S1-S2, O1-O2, N1-N2)) :-
	do_cond(Cond, toplevel(R1-RT, S1-ST, O1-OT, N1-NT)),
	store_objects(Conds, toplevel(RT-R2, ST-S2, OT-O2, NT-N2)).


%% do_cond(-Cond:term, -Toplevel:term) is det.
%
%
do_cond(A => B, toplevel(R-R, S-S, O-O, N-N)) :-
	!,
	store_objects(A, _),
	store_objects(B, _).

do_cond(A v B, toplevel(R-R, S-S, O-O, N-N)) :-
	!,
	store_objects(A, _),
	store_objects(B, _).

do_cond(-A, toplevel(R-R, S-S, O-O, N-N)) :-
	!,
	store_objects(A, _).

do_cond(can(A), toplevel(R-R, S-S, O-O, N-N)) :-
	!,
	store_objects(A, _).

do_cond(must(A), toplevel(R-R, S-S, O-O, N-N)) :-
	!,
	store_objects(A, _).

do_cond(should(A), toplevel(R-R, S-S, O-O, N-N)) :-
	!,
	store_objects(A, _).

do_cond(may(A), toplevel(R-R, S-S, O-O, N-N)) :-
	!,
	store_objects(A, _).

do_cond(object(Ref, Noun, Type, _, Eq, na)-_, toplevel(R-[Ref | R], S-S, O-O, N-N)) :-
	!,
	assert(ref_to_noun(Ref, Type, Noun, Eq, na)).

do_cond(object(Ref, Noun, Type, _, Eq, Number)-_, toplevel(R-[Ref | R], S-S, O-O, N-N)) :-
	number(Number),
	!,
	assert(ref_to_noun(Ref, Type, Noun, Eq, Number)).

do_cond(object(Ref, Noun, Type, _, Eq, RawNumber)-_, toplevel(R-[Ref | R], S-S, O-O, N-N)) :-
	atom(RawNumber),
	!,
	atom_number(RawNumber, Number),
	assert(ref_to_noun(Ref, Type, Noun, Eq, Number)).

do_cond(predicate(_, _, Ref)-_, toplevel(R-R, S-[Ref | S], O-O, N-N)) :- !.

do_cond(predicate(_, _, Ref, O1)-_, toplevel(R-R, S-[Ref | S], O-[O1 | O], N-N)) :- !.

do_cond(predicate(_, _, Ref, O1, O2)-_, toplevel(R-R, S-[Ref | S], O-[O1, O2 | O], N-N)) :- !.

do_cond(_, toplevel(R-R, S-S, O-O, N-N)).



%% check_conds(+Conds:list, +Subject:atom) is det.
%
% We can't verbalize coordinations (both AND and OR) under immediate not/must/can,
% therefore we reject such DRSs.
%
% * OR-coordinations are easy to detect and reject.
% * AND-coordinations are harder to detect. We need to check whether there are
% two or more predicates which have the subject which agrees with the upper level subject.
%
% BUG: should we add: Conds \= [object(_, _, _, _, _, _)-_]
%
check_conds([_ v _], _) :- !, fail.

check_conds(Conds, Subject) :-
	findall(Cond, (
		member(Cond-_, Conds),
		functor(Cond, predicate, _),
		arg(4, Cond, Subject)
	), [_, _ | _]),
	!,
	fail.

check_conds(_, _).



%% modify_first_verb(+Functor:atom, +Conds:list, -Subj:nvar, -NewConds:list) is det.
%
% We go deep into the DRS and look for the first predicate. We modify it
% by placing it into a functor, e.g. see -> can(see).
%
modify_first_verb(Functor, Conds, Subject, NewConds) :-
	rep(Functor, Conds, Subject, NewConds).


%% rep(+Functor:atom, +Conds:list, -Subject:nvar, -NewConds:list) is det.
%
%
rep(Functor, Conds, Subject, [ModifiedPredicate | Rest]) :-
	select(Predicate, Conds, Rest),
	pred(Functor, Predicate, Subject, ModifiedPredicate),
	!.

rep(Functor, [-Conds], Subject, [-NewConds]) :-
	rep(Functor, Conds, Subject, NewConds).

rep(Functor, [can(Conds)], Subject, [can(NewConds)]) :-
	rep(Functor, Conds, Subject, NewConds).

rep(Functor, [must(Conds)], Subject, [must(NewConds)]) :-
	rep(Functor, Conds, Subject, NewConds).

rep(Functor, [should(Conds)], Subject, [should(NewConds)]) :-
	rep(Functor, Conds, Subject, NewConds).

rep(Functor, [may(Conds)], Subject, [may(NewConds)]) :-
	rep(Functor, Conds, Subject, NewConds).

pred(Functor, predicate(Ref, Verb, Subject, Object)-Id, Subject, predicate(Ref, ModifiedVerb, Subject, Object)-Id) :-
	verb_modifiedverb(Functor, Verb, ModifiedVerb).

pred(Functor, predicate(Ref, Verb, Subject)-Id, Subject, predicate(Ref, ModifiedVerb, Subject)-Id) :-
	verb_modifiedverb(Functor, Verb, ModifiedVerb).


%% verb_modifiedverb(+Functor:atom, +Verb:atom, -FunctorVerb:term) is semidet.
%
%
verb_modifiedverb('-', Verb, not(Verb)).
verb_modifiedverb(can, Verb, can(Verb)).
verb_modifiedverb(must, Verb, must(Verb)).
verb_modifiedverb(should, Verb, should(Verb)).
verb_modifiedverb(may, Verb, may(Verb)).


%% get_main_subject(+Conds:list, -Subject:atom) is nondet.
%
% Extracts the subject of the condition.
%
get_main_subject(Conds, Subject) :-
	member(predicate(_, _, Subject, _)-_, Conds).

get_main_subject(Conds, Subject) :-
	member(predicate(_, _, Subject)-_, Conds).


%% get_coordinator(+Condition:functor, +OldCoord:atom, -NewCoord:atom) is det.
%
% We find a new coordinator on the basis of the condition that we have
% just verbalized. If this condition was a predicate then the new coordinator
% will be 'and'. If the condition was a disjunction then we need a ',and'.
% Otherwise the old coordinator will be used (which is usually 'and').
% This solution is hackish and doesn't always work, e.g. sometimes we should
% look ahead to be able to decide whether to use 'and' or ',and'.
%
% We added this hackish solution to support:
%
% Every man works or eats and sleeps.
% Every man works or eats ,and sleeps or drinks.

get_coordinator(Cond-_, _, and) :-
	functor(Cond, predicate, _),
	!.

get_coordinator(_ v _, _, ', and') :-
	!.

get_coordinator(_, Old, Old).



%% add_refs(+List:list, +Ref:functor, -List:list) is det.
%
% Updates a list...

add_refs(List, [], List) :- !.
add_refs(List, Ref, [Ref | List]).


%% make_comma_and_if_needed(+Coord:atom, -Coord:list) is det.
%
% BUG: a quick hack...

make_comma_and_if_needed(', and', [',', and]) :- !.
make_comma_and_if_needed(and, [',', and]) :- !.
make_comma_and_if_needed(_, []).


%% num_sgpl_copula(+Number:atomic, -SgPl:atom, -IsAre:atom) is det.
%
%
num_sgpl_copula(1, sg, is) :- !.
num_sgpl_copula(na, sg, is) :- !.
num_sgpl_copula(_, pl, are).


%% countable_or_mass_or_dom(+Quant:atom) is det.
%
%
countable_or_mass_or_dom(countable).
countable_or_mass_or_dom(mass).
countable_or_mass_or_dom(dom).
