%%% ============================================================================
%%% Generated by egg

-include_lib("referl_qc/include/prop_based_testing.hrl").

-compile({inline, [include_if/2]}).
include_if(P, E) -> [E || P].

%%% ----------------------------------------------------------------------------
%%% Essential macros and functions

-compile({inline, [getattr/2]}).
getattr(Name, List) ->
    case lists:keysearch(Name, 1, List) of
        {value, {Name, Value}} -> Value;
        false -> []
    end.

-define(setattr(Name, Code, List), lists:keystore(Name, 1, List, {Name, Code})).
-define(getattr(Name, List), getattr(Name, List)).
-define(terminal(Gen, MainAttr),
        ?LET(Terminal, Gen, ?setattr(value, Terminal, MainAttr))).

%% -----------------------------------------------------------------------------
%% Independent lists

%%-compile({inline, [independent_list/2,
%%                   independent_list/3,
%%                   independent_list2/3,
%%                   independent_foreach/3]}).
independent_list(Gen, Attr) ->
    ?LET(Attrs, list(Gen(Attr)), value_to_list(attrmerge(Attrs))).

independent_list(L, Gen, Attr) when is_list(L) -> independent_foreach(L, Gen, Attr);

independent_list(0, _Gen, Attr) -> ?setattr(value, [], Attr);
independent_list(N, Gen, Attr) -> independent_list2(N, Gen, Attr).

independent_list2(N, Gen, Attr) ->
    ?LET(Attrs, vector(N, Gen(Attr)), value_to_list(attrmerge(Attrs))).

%% Optimisation (maybe incorrect? seems to be dependent)
%%independent_list2(0, _GenFun, Attr) -> value_to_list(Attr);
%%independent_list2(N, GenFun, OldAttr) ->
%%    ?LET(Attr,
%%         GenFun(OldAttr),
%%         independent_list2(N-1, GenFun, attrmerge([OldAttr, Attr]))).

%%independent_foreach(L, GenFun, Attr) -> independent_foreach(L, GenFun, Attr).
independent_foreach([], _GenFun, Attr) -> value_to_list(Attr);
independent_foreach([H|T], GenFun, OldAttr) ->
    ?LET(Attr,
         GenFun(?setattr(param, H, OldAttr)),
         independent_foreach(T, GenFun, attrmerge([OldAttr, Attr]))).

%% -----------------------------------------------------------------------------
%% Dependent lists

%%-compile({inline, [dependent_list/2,
%%                   dependent_list/3,
%%                   dependent_list/4,
%%                   dependent_foreach/3]}).
dependent_list(GenFun, Attr) ->
    ?LET(L, list(nat()), dependent_list(length(L), GenFun, Attr)).

dependent_list(L, GenFun, Attr) when is_list(L) -> dependent_foreach(L, GenFun, Attr);

dependent_list(N, GenFun, Attr) -> dependent_list(N, GenFun, Attr, []).
dependent_list(0, _GenFun, Attr, List) -> ?setattr(value, List, Attr);
dependent_list(N, GenFun, OldAttr, List) ->
    ?LET(Attr,
         GenFun(OldAttr),
         dependent_list(N-1, GenFun, Attr, List ++ [?getattr(value, Attr)])).

dependent_foreach(L, GenFun, Attr) -> dependent_foreach(L, GenFun, Attr, []).
dependent_foreach([], _GenFun, Attr, List) -> ?setattr(value, List, Attr);
dependent_foreach([H|T], GenFun, OldAttr, List) ->
    ?LET(Attr,
         GenFun(?setattr(param, H, OldAttr)),
         dependent_foreach(T, GenFun, lists:keydelete(param, 1, Attr), List ++ [?getattr(value, Attr)])).

value_to_list(Attrs) ->
    E = ?getattr(value, Attrs),
    case is_list(E) of
        true  -> Attrs;
        false -> ?setattr(value, [E], Attrs)
    end.

%% -----------------------------------------------------------------------------
%% Attribute merge

attrmerge([]) -> [];
attrmerge([L1|Ls]) when is_list(L1) ->
    %%io:format("attrmerge: ~p -> ~p~n", [[names(L2) || L2 <- L], names(lists:foldl(fun attrmerge/2, [], L))]),
%    lists:foldl(fun attrmerge/2, [], L);
    attrmerge(lists:keydelete(param, 1, L1), attrmerge(Ls));
attrmerge(X) when is_tuple(X) -> attrmerge(tuple_to_list(X));
attrmerge(X) when is_list(X) -> X.


attrmerge([], L) -> L;
attrmerge(L, []) -> L;
attrmerge(L, L) -> L;
attrmerge(L1, L2) ->
    [normalise({K, V}) || {K, V} <-  merge(lists:keysort(1, L1), lists:keysort(1, L2))].

names(L) -> [K || {K, _} <- L].

merge(L, []) -> L;
merge([], L) -> L;
%% same key
merge([{module, V} | T1], [{module, V} | T2]) ->
    [{module, V}|merge(T1, T2)];
merge([{L, V1} | T1], [{L, V2} | T2]) ->
    [{L, merge2(L, V1, V2)}|merge(T1, T2)];

%% only in first list
merge([{L1, V1}|T1], T2 = [{L2, _} |_ ]) when L1 < L2 ->
    [{L1, V1} | merge(T1, T2)];

%% only in second list
merge(T1 = [{L1, _}|_], [{L2, V2}|T2]) when L1 > L2 ->
    [{L2, V2} | merge(T1, T2)].

%% without this line it goes into an infinite computation... perhaps
%% because normalise_list turns a single-element list into that single
%% element
normalise({procs, Procs}) -> {procs, lists:usort(to_list(Procs))};
normalise({etsnames, Ets}) -> {etsnames, lists:usort(to_list(Ets))};
normalise({macros, Macros}) -> {macros, lists:usort(to_list(Macros))};

normalise({vars, Vars}) -> {vars, lists:usort(to_list(Vars))};
normalise({funs, Funs}) -> {funs, lists:usort(to_list(Funs))};
normalise({types, Types}) -> {types,to_list(Types)};
%a({type, [T|_Types]}) -> {type, T};
normalise({value, Elem}) -> {value, Elem};
normalise({headers, Elem}) -> {headers, Elem};
normalise({Key, Elem}) -> {Key, normalise_list(Elem)}.

to_list(L) when is_list(L) -> L;
to_list(E) -> [E].

normalise_list(L) when is_list(L) ->
    case lists:usort(L) of
        [E] -> E;
        _ -> L
    end;
normalise_list(X) -> X.

elem([E]) -> E;
elem(E) -> E.

%% e.g. [{value, [alma]}], [{value, elem}] -> [{value, [alma, elem]}]
merge2(_, L1, L2) when is_list(L1) andalso is_list(L2) -> L1 ++ L2;
merge2(_, L1, X)  when is_list(L1) -> L1 ++ [X];
merge2(_, X, L2)  when is_list(L2) -> [X|L2];
merge2(_, X1, X2) -> [X1, X2].

length2(L) when is_list(L) -> length(L);
length2(_) -> 1.

%%% ----------------------------------------------------------------------------
%%% Generated based on the egg grammar description
