:- lib(stoics_lib:en_list/2).
:- lib(stoics_lib:kv_decompose/3).
:- lib(stoics_lib:current_call/1).
:- lib(stoics_lib:list_frequency/3).

:- lib(suggests(real) ).
:- lib(suggests(b_real)).

mlu_frequency_plot_defaults( Defs ) :-
    ( current_predicate(r_version/3) -> 
        Ifce = gg_bar
        ;
        ( current_predicate(b_real_version/2) ->
            Ifce = gg_bar
            ;
            throw( either_of_required(lib(real),lib(b_real)) )

        )
    ),
    Defs = [ interface(Ifce), sort(false), pop_line(false), pop_breaks(false) ].

/** mlu_frequency_plot( +FreqOrVec, +Opts ).

Make a plot for Data, a pairlist, list or R vector.

Data is one of
  * pairlist
    of the form, Item-Times
  * list
    that is passed, with Opts, to list_frequency/3
  * vector
    the values of which are retrieved with b_real:pl_vector/3, and then \br
    passed, with Opts, to list_frequency/3

Opts
  * interface(Iface)
    _barplot_, or _gg_bar_ interfaces are supported. The first requires lib(real), 
    in addition the latter also requires lib(b_real).
    If b_real is present, the second interface becomes the default.

  * pop_breaks(Breaks=false)
    colour population groups according to given break points. Splits are done with 
    =< so break points go to the left partition. (Currently only for _gg_bar_ interface.)

  * pop_line(PlineAt=false)
    if integer draws a vertical line separating columns with counts less than PlineAt to those with more.
    Only makes sense if Sort is set to _frequency_. (Currently only for _gg_bar_ interface.)

  * sort(Sort=false)
    alternatives to not sorting (default)
     * elem/element
	  sort by element
     * true/freq/frequency
	  sort on frequency

Other options are passed to either gg_bar_plot/2 (if Iface == gg_bar) or to r_call/2 (if Iface == barplot).

==
?- lib(pepl).
?- sload_pe( coin ).


?- [pack(mlu/examples/grouped_freqs)].
?- grouped_freqs.
% a plot with 9 bars and 3 groups should appear on screen

?- mlu_frequency_plot( [1,1,1,2,2,3], true ).
?- mlu_frequency_plot( [1,1,1,2,2,3], interface(barplot) ).
==

==
?- mlu_frequency_plot( [1,1,2,11,12,21,31,33,41], [bins([10,20]),interface(gg_bar)] ).
==

The plot produced has binned Data into 3 bins.

==
?- mlu_frequency_plot( [1,1,2,11,12,21,31,33,41], [bins([bin1-10,bin2-20,bin3-inf]),interface(gg_bar)] ).
==

As previous example, but x tics are custom labelled.

==
?- mlu_frequency_plot( [1,2,10,11,12,21,31,33,41], [bins([0-10]),interface(gg_bar)] ).
==

==
?- lib(pepl).
?- sload_pe( coin ).
?- mlu_sample( scall(coin(Side)), 100, Side, Freqs ), mlu_frequency_plot( Freqs, [interface(barplot),outputs([svg]),las=2] ).
==
Produces file: real_plot.svg

[[doc/html/images/real_plot.svg]]

==
?- mlu_sample( scall(coin(Side)), 100, Side, Freqs ), mlu_frequency_plot( Freqs, [interface(gg_bar),output(png("naku.png"))] ).
==
Produces file: naku.png

[[doc/html/images/naku.png]]

@author nicos angelopoulos
@version  0.1 2016/8/31
@version  0.2 2017/1/13, added option sort(false)
@version  0.3 2017/8/29, added vectors as inputs via b_real:pl_vector/3

*/

mlu_frequency_plot( InFreqPrv, Args ) :-
    options_append( mlu_frequency_plot, Args, Opts ),
    mlu_frequency_plot_pairs( InFreqPrv, InFreq, Opts ),
    options( sort(Sort), Opts ),
    ground( Sort ),  % fixme: type check
    mlu_frequency_plot_sort( Sort, InFreq, Freq ),
    options( interface(Iface), Opts, rem_opts(Ropts) ),
    mlu_frequency_interface( Iface, Freq, Ropts ).

mlu_frequency_plot_pairs( InFreqPrv, InFreq, _Opts ) :-
    \+ var( InFreqPrv ),
    InFreqPrv = [_-_|_],
    !,
    InFreqPrv = InFreq.
mlu_frequency_plot_pairs( InFreqPrv, InFreq, Opts ) :-
     is_list( InFreqPrv ),
     !,
     list_frequency( InFreqPrv, InFreq, Opts ).
mlu_frequency_plot_pairs( InFreqPrv, InFreq, Opts ) :-
    current_call( mlu:pl_vector(InFreqPrv,InVect,Opts) ),
    !,
    list_frequency( InVect, InFreq, Opts ).
mlu_frequency_plot_pairs( InFreqPrv, _InFreq, _Opts ) :-
    throw( pack_error(mlu,mlu_requency_plot/2,arg_enumerate(1,[pairlist,pl_vector],InFreqPrv)) ).

mlu_frequency_interface( barplot, Freqs, Opts ) :-
    kv_decompose( Freqs, Lbls, Vals ),
    % ( select(plot_args(PargS),Opts,CallOpts) -> PargS = [] ; CallOpts = Opts ),
    % en_list( PargS, Pargs ),
    % findall( Name=Val, (member(Parg,Pargs),Parg=..[Name,Val]), Cargs ),
    % Call =.. [barplot,Vals,names,arg=Lbls|Cargs],
    r_call( barplot(Vals,names.arg=Lbls), Opts ).
mlu_frequency_interface( gg_bar, Freqs, Opts ) :-
    options( pop_line(Pline), Opts ),
    mlu_freq_interface_gg_bar_pop_line( Pline, Freqs, Opts, POpts ),
    options( pop_breaks(Pbreaks), Opts ),
    mlu_freq_interface_gg_bar_pop_groups( Pbreaks, Freqs, POpts, GOpts ),
    gg_bar_plot( Freqs, GOpts ).

mlu_freq_interface_gg_bar_pop_groups( false, _Freqs, Opts, GOpts ) :-
    !,
    GOpts = Opts.
mlu_freq_interface_gg_bar_pop_groups( BrkS, Freqs, Opts, GOpts ) :-
    reverse( Freqs, Rreqs ),
    en_list( BrkS, Brks ),
    sort( Brks, Orks ),
    % reverse( Orks, Srks ),
    Orks = [Brk|Tlk],
    atom_concat( '# =< ', Brk, ClrAtm ),
    atom_string( ClrAtm, Clr ),
    mlu_freq_interface_gg_bar_pop_breaks( Rreqs, Brk, Tlk, Clr, Plrs ),
    append( Opts, [level_colours(Plrs),level_colours_title('counts')], GOpts ).

mlu_freq_interface_gg_bar_pop_breaks( [], _Brk, _Brks, _Clr, [] ).
mlu_freq_interface_gg_bar_pop_breaks( [Lvl-Tms|T], Brk, Brks, Clr, Plrs ) :-
    Tms =< Brk,
    !,
    Plrs = [Lvl-Clr|Tlrs],
    mlu_freq_interface_gg_bar_pop_breaks( T, Brk, Brks, Clr, Tlrs ).
mlu_freq_interface_gg_bar_pop_breaks( [H|T], Brk, Brks, _Clr, Plrs ) :-
    mlu_freq_interface_gg_bar_pop_breaks_cont( Brks, Brk, [H|T], Plrs ).

mlu_freq_interface_gg_bar_pop_breaks_cont( [], LastBrk, Freqs, Plrs ) :-
    atom_concat( '#  > ', LastBrk, ClrAtm ),
    atom_string( ClrAtm, Clr ),
    findall( Lvl-Clr, member(Lvl-_Tms,Freqs), Plrs ).
mlu_freq_interface_gg_bar_pop_breaks_cont( [Brk|Brks], _LastBrk, Freqs, Plrs ) :-
    atom_concat( '# =< ', Brk, ClrAtm ),
    atom_string( ClrAtm, Clr ),
    mlu_freq_interface_gg_bar_pop_breaks( Freqs, Brk, Brks, Clr, Plrs ).

mlu_freq_interface_gg_bar_pop_line( false, _Freqs, Opts, POpts ) :- 
    !,
    POpts = Opts.
mlu_freq_interface_gg_bar_pop_line( PlineAt, Freqs, Opts, POpts ) :- 
    integer( PlineAt ),
    nth1( NCnt, Freqs, _-Cnt ),
    Cnt < PlineAt,
    !,
    At is NCnt - 0.5,
    Vline = geom_vline(xintercept = At),
    ( select(gg_terms(GTins),Opts,ROpts) ->
        en_list( GTins, GTinsL ),
        % HalfToneDown is PlineAt - 0.5,
        atom_concat( 'y<', PlineAt, XeqText ),
        findall( ACnt, member(_-ACnt,Freqs), AllCnts ),
        max_list( AllCnts, MaxCnt ),
        Xpos is At + (MaxCnt / 400),
        % Vtext = annotate(+text, x=Xpos, y=MaxCnt, hjust=0, label= +XeqText),
        Ypos is 0 - (MaxCnt / 50),
        Vtext = annotate(+text, x=Xpos, y= Ypos, hjust=0, vjust=1, label= +XeqText),
        POpts = [gg_terms([Vline,Vtext|GTinsL])|ROpts]
        ;
        POpts = [gg_terms([Vline])|Opts]
    ).
mlu_freq_interface_gg_bar_pop_line( PopLine, Freqs, _Opts, _POpts ) :- 
    !,
    throw( fixme(pop_line_failure(PopLine,Freqs)) ).

mlu_frequency_plot_sort( false, InFreq, Freq ) :-
	Freq = InFreq.
mlu_frequency_plot_sort( frequency, InFreq, Freq ) :-
	findall( Freq-Elem, member(Elem-Freq,InFreq), Trans ),
	keysort( Trans, Srans ),
	findall( Elem-Freq, member(Freq-Elem,Srans), FreqRev ),
	reverse( FreqRev, Freq ).
mlu_frequency_plot_sort( freq, InFreq, Freq ) :-
	mlu_frequency_plot_sort( frequency, InFreq, Freq ).
mlu_frequency_plot_sort( true, InFreq, Freq ) :-
	mlu_frequency_plot_sort( frequency, InFreq, Freq ).
mlu_frequency_plot_sort( element, InFreq, Freq ) :-
	keysort( InFreq, Freq ).
mlu_frequency_plot_sort( elem, InFreq, Freq ) :-
	mlu_frequency_plot_sort( element, InFreq, Freq ).