:- module(autobuild_actuators,
    [
    find_actuators/0
    ]).

/** <module> Actuators : Automatic model building

@author Carsten van Weelden <mailto:cweelden@science.uva.nl>

*/

%% find_actuators
% Finds a set of actuators for each causal group and writes them to the output stream.
find_actuators :-
    findall(
        CausalGroup,
	causal_group(_, CausalGroup),
	CausalGroups
    ),
    find_actuators(CausalGroups, Actuators),
    format("Influences: ~w~n", [Actuators]).

%% find_actuators(+CausalGroups:list, -Actuators:list)
% Finds a set of actuators for each causal group. 
find_actuators([], []).
find_actuators(CausalGroups, NewActuators) :-
    member(CausalGroup, CausalGroups),
    CausalGroupTree = [[CausalGroup, []]],
    actuate_causal_group(CausalGroupTree, Actuators),
    select(CausalGroup, CausalGroups, RestCausalGroups),
    find_actuators(RestCausalGroups, FoundActuators),
    append(FoundActuators, Actuators, NewActuators).

%% actuate_causal_group(+SearchTree:list, -Influences:list)
% Finds a set of actuators for a causal group. SearchTree is a list containing search nodes which are lists with a causal group as first element and a list of possible actuators as second element. Actuators are returned as a list of instantiated relations between quantities.
actuate_causal_group([[CausalGroup, Actuators]|_], Influences) :-
    check_actuator_consistency(CausalGroup, Actuators, Influences),
    format("Found relations: ~w~n~n", [Influences]).
actuate_causal_group(CausalGroupTree, Actuators) :-
    generate_new_nodes(CausalGroupTree, NewCausalGroupTree),
    actuate_causal_group(NewCausalGroupTree, Actuators).

generate_new_nodes(CausalGroupTree, NewCausalGroupTree) :-
    CausalGroupTree = [[CausalGroup, Actuators] | SearchFront],
    findall(
        [NewCausalGroup, NewActuators],
	generate_actuator(CausalGroup, Actuators, NewCausalGroup, NewActuators),
	NewNodes
    ),
    append(SearchFront, NewNodes, NewCausalGroupTree).

%% generate_actuator(+CausalGroup:list, +OldActuators:list, -NewCausalGroup:list, -NewActuators:list)
% Generates a new actuator for a causal group. Actuators are always generated in the following order: exogenous influences, influences, proportionalities.
generate_actuator(CausalGroup, [], CausalGroup, [NewActuator]) :-
    design:exogenous_behaviours(ExogenousBehaviours),
    member(ExogenousActuator, ExogenousBehaviours),
    NewActuator = [exogenous, ExogenousActuator].
generate_actuator(CausalGroup, Actuators, NewCausalGroup, NewActuators) :-
    \+ member([exogenous, _], Actuators),
    ProportionalityTypes = [prop_pos, prop_neg],
    forall(
        member(ProportionalityType, ProportionalityTypes),
	\+ member([ProportionalityType, _], Actuators)
    ),
    get_influence_types(InfluenceTypes),
    get_all_super_clusterIDs(SuperClusters),
    member(InfluenceType, InfluenceTypes),
    member(SuperCluster, SuperClusters),
    \+ member([_, SuperCluster], Actuators),
    Actuator = [InfluenceType, SuperCluster],
    (
        member(SuperCluster, CausalGroup) ->
	    select(SuperCluster, CausalGroup, NewCausalGroup),
	    Feedback = [feedback, SuperCluster],
	    append(Actuators, [Actuator, Feedback], NewActuators)
	    ;
	    NewCausalGroup = CausalGroup,
	    append(Actuators, [Actuator], NewActuators)
    ),
    \+ NewCausalGroup = [].
generate_actuator(CausalGroup, Actuators, CausalGroup, NewActuators) :-
    \+ member([exogenous, _], Actuators),
    get_influence_types(InfluenceTypes),
    forall(
        member(InfluenceType, InfluenceTypes),
	\+ member([InfluenceType, _], Actuators)
    ),
    get_all_super_clusterIDs(SuperClusters),
    member(SuperCluster, SuperClusters),
    \+ member(SuperCluster, CausalGroup),
    \+ member([_, SuperCluster], Actuators),
    ProportionalityTypes = [prop_pos, prop_neg],
    member(ProportionalityType, ProportionalityTypes),
    append(Actuators, [[ProportionalityType, SuperCluster]], NewActuators).

check_actuator_consistency(CausalGroup, Actuators, Influences) :-
    \+ Actuators = [],
    check_exogenous_actuators(CausalGroup, Actuators, RestActuators),
    rewrite_actuators(CausalGroup, RestActuators, Influences),
    check_relation_consistency(Influences),
    format("Found for ~w~n~w~n~n", [CausalGroup, Actuators]).

check_exogenous_actuators(CausalGroup, Actuators, RestActuators) :-
    findall(
        [exogenous, ExogenousActuator],
	member([exogenous, ExogenousActuator], Actuators),
	ExogenousActuators
    ),
    subtract(Actuators, ExogenousActuators, RestActuators),
    forall(
        member([exogenous, ExoActuator], ExogenousActuators),
	(
            findall(
	        SuperClusterQuantities,
		(
		    member(SuperCluster, CausalGroup),
                    findall(
		        SuperClusterQuantity,
			get_super_cluster(SuperClusterQuantity, SuperCluster),
			SuperClusterQuantities
		    )
		),
		[Quantities]
	    ),
            get_partial_state_description(_,3,system_elements(Elements)),
	    member(instance(GarpGenerated, ExoActuator), Elements),
	    member(Quantity, Quantities),
	    get_quantity_owner(Quantity, Entity),
	    member(has_attribute(Entity, has_assumption, GarpGenerated), Elements)
	)
    ).
%% rewrite_actuators(+CausalGroup:list, +Actuators:list, -Influences:list)
% Instantiates a list of actuators, returning the influences between quantities.
rewrite_actuators(_, [], []).
rewrite_actuators(CG, [Actuator | Actuators], [Influence | Influences]) :-
    rewrite_actuator(CG, Actuator, Influence),!,
    rewrite_actuators(CG, Actuators, Influences).
rewrite_actuator(CausalGroup, [feedback, ActuatedSuperCluster], Influence) :-
    last(CausalGroup, ActuatorSuperCluster),
    super_cluster_input_quantity(ActuatedQuantity, ActuatedSuperCluster),
    super_cluster_output_quantity(ActuatorQuantity, ActuatorSuperCluster),
    (
        naive_dependency(prop_pos(ActuatedQuantity, ActuatorQuantity)),
        Influence = prop_pos(ActuatedQuantity, ActuatorQuantity)
	;
        naive_dependency(prop_neg(ActuatedQuantity, ActuatorQuantity)),
        Influence = prop_neg(ActuatedQuantity, ActuatorQuantity)
    ).
rewrite_actuator(CausalGroup, [ActuatorType, ActuatorSuperCluster], Influence) :-
    nth1(1, CausalGroup, ActuatedSuperCluster),
    super_cluster_output_quantity(ActuatorQuantity, ActuatorSuperCluster),
    super_cluster_input_quantity(ActuatedQuantity, ActuatedSuperCluster),
    Influence =.. [ActuatorType, ActuatedQuantity, ActuatorQuantity].

