:- module(autobuild_build_model,
    [
    start_new/0,
    startw/0,
    build_naive_model/0,
    add_dependencies/2,
    add_dependency/2,
    create_causal_dependency/4,
    create_correspondence/4,
    new_target_model/1,
    build_cluster/2,
    build_super_cluster/2
    ]).

/** <module>  Build a model from Garp representation : Automatic model building

@author Jochem Liem

 * Automatic model building algorithm
 * Inspired by Hylke Buisman's algorithm

*/

test_all :-
	%load tree.hgp
	%simulate
	start_new.

% generate model and "pretty" print output
startw :-
	retractall(model_seen(_, _)),
	% Turn the warning mechanism off
	send(@app, setting, nonessential_warnings, @off),

	start_carsten,
	build_model_list(M),

	% check output?
	% simulate generated model
	% compare behavior graph

	forall(
		member(A, M), 
		writeln(A)),

	% Turn the warning mechanism on
	send(@app, setting, nonessential_warnings, @on).

% generate model and send output to garp
% (warning: may not terminate)
start_new :-
	retractall(model_seen(_, _)),
	% Turn the warning mechanism off
	send(@app, setting, nonessential_warnings, @off),

	new_model,
	start_carsten,
	build_model_list(M),
	once(get_id(0, N)), 
	once(add_mf_to_garp(M, N) ; true),
	
	% check output?
	% simulate generated model
	% compare behavior graph

	% Turn the warning mechanism on
	send(@app, setting, nonessential_warnings, @on).

new_model :-
	% Create a new model
	get(@model, '_value', OldModel),
	send(@app, newModel),
	% Copy the entities
	design:completeEntityList(OldModel, 'Entity', AllEntities),
	send(@copyBuffer, createEntityDefs, OldModel, @model, AllEntities, @nil, @nil, new(chain)),
	% Copy the agents
	design:completeEntityList(OldModel, 'Agent', AllAgents),
	send(@copyBuffer, createEntityDefs, OldModel, @model, AllAgents, @nil, @nil, new(chain)),
	% Copy the assumptions
	design:completeEntityList(OldModel, 'Assumption', AllAssumptions),
	send(@copyBuffer, createEntityDefs, OldModel, @model, AllAssumptions, @nil, @nil, new(chain)),
	% Copy the configurations
	send(@copyBuffer, createConfigurationDefs, @model, OldModel?configurationDefinitions, @nil),
	% Copy the quantity spaces
	send(@copyBuffer, createQuantitySpaceDefs, @model, OldModel?quantitySpaces, @nil),
	% Copy the quantities
	send(@copyBuffer, createQuantityDefs, @model, OldModel?quantityDefinitions, @nil),
	% Copy the scenarios
	send(@copyBuffer, createScenarioDefs, @model, OldModel?inputSystems, @nil, @on)
	.

/* Building a model from the database */
%% return a list containing a possible model
build_model_list(M) :-
	findall(Deps, autobuild_database:cluster(_ID1, _, Deps, _, _), ClusterDeps),
	findall(SDeps, autobuild_database:super_cluster(_ID2, _, SDeps, _, _), SClusterDeps),
	%influences:
	findall(Acts, autobuild_database:actuators(_ID3, Acts), AList),
	%actuator_list(Actuators),
	append([ClusterDeps, SClusterDeps, AList], Deps),
	append(Deps, M).

%predicate that returns a number, which increases on backtracking
get_id(N, Result) :-
	Result = N
	;
        (N1 is N + 1,
        get_id(N1, Result)).

add_mf_to_garp(M, N) :-
	atom_concat('Result', N, MFName),
	%(N = 0 ->
		%add active MF result0
	%;	%add inactive MF resultN
		% Create the target model fragment
		design:getTopAgentFragment(@model, TopAgentFragment),
		chain_list(Parents, [TopAgentFragment]),
		design:addModelFragmentDefinition(
		@model, 
		@nil, 
		MFName,
		'This model fragment is automatically generated',
		Parents,
		@on) %)
	,
        design:getModelFragmentDefinitionName(@model, MFName, MF),
	create_structure(MF), %?
	create_quantities(MF), %?
        %add all correspondences in M to garp resultN
        %add all influences in M to garp resultN
        %add all props in M to garp resultN
	forall( member(Dependency, M),
		add_dependency(Dependency, MF)
	).	

%% build_naive_model
build_naive_model :-
    % Turn the warning mechanism off
    send(@app, setting, nonessential_warnings, @off),

    get(@app, visigarp, _VG), % there is an active simulation
    new_target_model(_OldModel),
    design:getModelFragmentDefinitionName(@model, 'Result', MF),
    create_structure(MF),
    create_quantities(MF),

    % Build the clusters
    forall(
	autobuild_database:cluster(ID, _, _, _, _),
	build_cluster(ID, MF)
    ),

    % Build the super clusters
    forall(
	autobuild_database:super_cluster(ID, _, _, _, _),
	build_super_cluster(ID, MF)
    ),
    % Turn the warning mechanism on
    send(@app, setting, nonessential_warnings, @on).


%% build_cluster(+ClusterID, +MF)
%  Creates the dependencies of the cluster
build_cluster(ClusterID, MF) :-
    autobuild_database:cluster(ClusterID, _Quantities, Dependencies, _, _),
    forall(
	member(Dependency, Dependencies),
	add_dependency(Dependency, MF)
    ).

%% build_super_cluster(+SuperClusterID, +MF)
%  Creates the dependencies of the super cluster
build_super_cluster(SuperClusterID, MF) :-
    autobuild_database:super_cluster(SuperClusterID, _ClusterIDs, Dependencies, _, _),
    forall(
	member(Dependency, Dependencies),
	add_dependency(Dependency, MF)
    ).

%% new_target_model(+OldModel)
%  Starts a new model based on another model (same base ingredients, but no scenarios and model fragments)
new_target_model(OldModel) :-
    % Create a new model
    get(@model, '_value', OldModel),
    send(@app, newModel),
    % Copy the entities
    design:completeEntityList(OldModel, 'Entity', AllEntities),
    send(@copyBuffer, createEntityDefs, OldModel, @model, AllEntities, @nil, @nil, new(chain)),
    % Copy the agents
    design:completeEntityList(OldModel, 'Agent', AllAgents),
    send(@copyBuffer, createEntityDefs, OldModel, @model, AllAgents, @nil, @nil, new(chain)),
    % Copy the assumptions
    design:completeEntityList(OldModel, 'Assumption', AllAssumptions),
    send(@copyBuffer, createEntityDefs, OldModel, @model, AllAssumptions, @nil, @nil, new(chain)),
    % Copy the configurations
    send(@copyBuffer, createConfigurationDefs, @model, OldModel?configurationDefinitions, @nil),
    % Copy the quantity spaces
    send(@copyBuffer, createQuantitySpaceDefs, @model, OldModel?quantitySpaces, @nil),
    % Copy the quantities
    send(@copyBuffer, createQuantityDefs, @model, OldModel?quantityDefinitions, @nil),
    % Copy the scenarios
    send(@copyBuffer, createScenarioDefs, @model, OldModel?inputSystems, @nil, @on),
    % Create the target model fragment
    design:getTopAgentFragment(@model, TopAgentFragment),
    chain_list(Parents, [TopAgentFragment]),
    design:addModelFragmentDefinition(
	@model, 
	@nil, 
	'Result',
	'This model fragment is automatically generated',
	Parents,
	@on),
    % Clear the copy buffer mapping
    send(@copyBuffer, clearMapping).

%% create_structure(+ModelFragment)
%  Create the structure (entities, agents, assumptions, and configurations of the simulated model in a model fragment.
%  Todo: assumptions (has_assumption) and attributes.
create_structure(ModelFragment) :-
    % Create all the entities and agents
    autobuild_input:get_all_entities(InternalEntities),
    autobuild_input:get_all_agents(InternalAgents),
    merge(InternalEntities, InternalAgents, InternalEntitiesAndAgents),
    forall(
	(
	    member(InternalEntity, InternalEntitiesAndAgents)
	),
	(
	    autobuild_garp_build_interface:internal_entity_instance_to_entity_definition(InternalEntity, EntityDef),
	    autobuild_garp_build_interface:internal_name_to_string(InternalEntity, EntityName),
	    design:addEntityInstance(@model, ModelFragment, EntityDef, EntityName, '', 'condition', @nil)
	)
    ),

    % Create the assumptions
    /*
    autobuild_input:get_all_assumptions(InternalAssumptions),
    forall(
	(
	    member(InternalAssumptions, InternalAssumptions)
	),
	(
	    autobuild_garp_build_interface:internal_entity_instance_to_entity_definition(InternalEntity, EntityDef),
	    autobuild_garp_build_interface:internal_name_to_string(InternalEntity, EntityName),
	    design:addEntityInstance(@model, ModelFragment, EntityDef, EntityName, '', 'condition', @nil)
	)
    ),
    */

    % Create the configurations (ADD ATTRIBUTES)
    autobuild_input:get_all_attributes(Configurations),
    forall(
	(
	    member([InternalConfiguration, InternalEntity1, InternalEntity2], Configurations),
	    not(member(InternalConfiguration, ['has_assumption'])) % FIX ME GLOBALLY
	),
	(
	    autobuild_garp_build_interface:internal_configuration_to_configuration_definition(InternalConfiguration, ConfigurationDef),
	    autobuild_garp_build_interface:internal_entity_instance_to_entity_instance(InternalEntity1, ModelFragment, Entity1),
	    autobuild_garp_build_interface:internal_entity_instance_to_entity_instance(InternalEntity2, ModelFragment, Entity2),
	    design:addConfigurationInstance(
		@model, 
		ModelFragment,
		ConfigurationDef, 
		Entity1, new(chain),
		Entity2, new(chain),
		'', 
		condition,
		@nil)
	)
    ).


create_quantities(ModelFragment) :-
    autobuild_input:get_all_quantity_names(InternalQuantities),
    forall(
	member(InternalQuantity, InternalQuantities),
	(
	    % Get the entity instance
	    get_quantity_owner(InternalQuantity, InternalEntity),
	    internal_entity_instance_to_entity_instance(InternalEntity, ModelFragment, EntityInstance),

	    % Attach the quantity
	    autobuild_garp_build_interface:internal_quantity_instance_to_quantity_and_quantity_space_definition(InternalQuantity, QuantityDef, QuantitySpaceDef),
	    design:addQuantityInstance(
		@model,
		ModelFragment,
		QuantityDef, 
		QuantitySpaceDef,
		EntityInstance,
		new(chain),
		new(chain),
		'',
		consequence,
		@nil)
	)
    ).

delete_dependencies(ModelFragment) :-
    send(ModelFragment?elements, for_all,
	if(
	    message(@prolog, ==, @arg1?class_name, 'garpQuantityRelation'),
	    message(@arg1, free),
	    message(@prolog, true)
	)
    ).

%% add_dependencies(+Dependencies:List, +ModelFragment)
%  Add a set of dependencies to the model
add_dependencies(Dependencies, ModelFragment) :-
    forall(
	member(Dependency, Dependencies),
	add_dependency(Dependency, ModelFragment)
    ).


%% add_dependency(+Dependency, +ModelFragment)
add_dependency(Dependency, ModelFragment) :-
    Dependency =.. [Type, Target, Source],
    internal_quantity_instance_to_quantity_instance(Source, ModelFragment, SourceQuantity, _SourceEntity), 
    internal_quantity_instance_to_quantity_instance(Target, ModelFragment, TargetQuantity, _TargetEntity), 
    (
	member(Type, [inf_pos_by, inf_neg_by, prop_pos, prop_neg]) ->
	create_causal_dependency(Type, SourceQuantity, TargetQuantity, ModelFragment)
    ;
	member(Type, [q_correspondence, mirror_q_correspondence]) ->
	create_correspondence(Type, SourceQuantity, TargetQuantity, ModelFragment)
    ).

    
%% create_causal_dependency
create_causal_dependency(Type, SourceQuantity, TargetQuantity, ModelFragment) :-
    (
	inf_pos_by == Type ->
	design:addCausalDependency(@model, ModelFragment, inf, plus, SourceQuantity, new(chain), TargetQuantity, new(chain), '', @nil)
    ;
	inf_neg_by == Type ->
	design:addCausalDependency(@model, ModelFragment, inf, min, SourceQuantity, new(chain), TargetQuantity, new(chain), '', @nil)
    ;
	prop_pos == Type ->
	design:addCausalDependency(@model, ModelFragment, prop, plus, SourceQuantity, new(chain), TargetQuantity, new(chain), '', @nil)
    ;
	prop_neg == Type ->
	design:addCausalDependency(@model, ModelFragment, prop, min, SourceQuantity, new(chain), TargetQuantity, new(chain), '', @nil)
    ).

create_correspondence(Type, SourceQuantity, TargetQuantity, ModelFragment) :-
    (
	q_correspondence == Type ->
	design:addCorrespondenceInstance(@model, ModelFragment, 
	    @off, @off, @off, @off,
	    SourceQuantity, new(chain), @nil,
	    TargetQuantity, new(chain), @nil,
	    '', @nil)
	;
	mirror_q_correspondence == Type ->
	design:addCorrespondenceInstance(@model, ModelFragment, 
	    @off, @off, @on, @off,
	    SourceQuantity, new(chain), @nil,
	    TargetQuantity, new(chain), @nil,
	    '', @nil)
    ).


