
:- use_module(library(lists)).     % append/3, member/2, flatten/2.
:- use_module(library(system)).    % file_exists/1.

:- lib(stoics_lib:en_list/2).
:- lib(stoics_lib:break_nth/4).
:- lib(stoics_lib:break_on_list/4).

:- lib(file_exists/1).
:- lib(option_in_values_or_else_default/6).

/*
testo_1 :- 
     unique_filename( abc__test, Unique ),
     testo_touch( Unique ),
     unique_filename( abc__test, Second ), 
     testo_touch( Second ).
testo_2 :- 
     unique_filename( abc__test, Unique ),
     testo_touch( Unique ),
     unique_filename( abc__test, [report(false),version(n)], Second ), 
     testo_touch( Second ).
testo_3 :- 
     unique_filename( abc__test, [extension(dot)], Unique ),
     testo_touch( Unique ),
     unique_filename( abc__test, [report(false),separator(+)], Second ), 
     testo_touch( Second ).
testo_4 :- 
     unique_filename( abc__test, [extension(dot),add(testo_call,3)], Unique ),
     testo_touch( Unique ),
     unique_filename( abc__test, [report(false)], Second ), 
     testo_touch( Second ).
testo_call( Res ) :-
     pl( swi(_), testo_call_swi(Res), testo_call_yap(Res) ).
testo_call_swi( Host ) :-
     getenv( 'HOSTNAME', Host ).
testo_call_yap( Host ) :-
     environ( 'HOSTNAME', Host ).
     */

testo_touch( File ) :-
     atom_concat( 'touch ', File, Touch ),
     write( touching(File) ), nl,
     shell( Touch ).
testo_clean :- shell( 'rm abc__test*' ).

safe_unique_filename( Stream, Stream ) :-
     is_a_stream( Stream ), !.
safe_unique_filename( FileAtom, UniqueFile ) :-
     unique_filename( FileAtom, UniqueFile ).

unique_filename_defaults( Defs, RepVals ) :-
     Defs = [  version(v),
               separator('_'),
               % add(Call,Lgt),          no default adds
               report(true),
               type(file),              % else dir
               extension(null__)],  % not '' ?
     RepVals = [true,false].

unique_filename( FileAtom, UniqueFile ) :-
     unique_filename( FileAtom, UniqueFile, [] ).

% Opt in {report([true,false]),version(AtomicVrs),extension(Ext)}
% I guess we can ask for type([integer,upper,lower,letter]),
% and length of extension bits
%
unique_filename( FileIn, UnqFile, OptsIn ) :-
     unique_filename_defaults( Defs, RepVls ),
     en_list( OptsIn, Opts ),
     append( Opts, Defs, All ),
     memberchk( version(VrsIn), All ),
     to_codes( VrsIn, VrsPfx ),
     memberchk( separator(SepIn), All ),
     % to_codes( SepIn, Sep ),
     atom_codes( SepIn, Sep ),
     memberchk( extension(ExtOpt), All ),
     option_in_values_or_else_default( report, Opts, RepVls, error, Defs, Rep ),
     file_and_extension( FileIn, ExtOpt, Stem, Ext ),
     findall( add(Add,Lgt), member(add(Add,Lgt),Opts), Additions ),
     atom_codes( Stem, StemCs ),
     uf_unique_additions( Additions, Sep, StemCs, UnqStemCs ),
     atom_codes( UnqStem, UnqStemCs ),
     file_name_extension( UnqStem, Ext, Test ),
     ( type_exists(Test,All) ->
          append( Sep, VrsPfx, Vrs ),
          append( UnqStemCs, Vrs, VrsRnmCs ),
          append( VrsRnmCs, [0'0,0'1], FirstRnmCs ),
          atom_codes( First, FirstRnmCs ),
          uf_ex_err( Rep, Test, First ),
          unique_filename_1( First, Ext, Vrs, Rep, UnqFile, Opts )
          ;
          UnqFile = Test
     ),
     !.

unique_filename_1( Stem, Ext, Vrs, Rep, UnqFile, Opts ) :-
     file_name_extension( Stem, Ext, File ),
     type_exists( File, Opts ),
     !,
     atom_codes( Stem, StemCs ),
     next_filename( StemCs, Vrs, NextStemCs ),
     atom_codes( NextStem, NextStemCs ),
     uf_ex_err( Rep, File, NextStem ),
     unique_filename_1( NextStem, Ext, Vrs, Rep, UnqFile, Opts ).
unique_filename_1( Stem, Ext, _Vrs, _Rep, File, _Opts ) :-
     file_name_extension( Stem, Ext, File ).

type_exists( File, Opts ) :-
     memberchk( type(Type), Opts ),
     type_exists_1( Type, File ).

type_exists_1( file, File ) :-
     exists_file( File ).
type_exists_1( dir, Dir ) :-
     exists_directory( Dir ).

uf_ex_err( true, Exists, New ) :-
     write( user_error,
          file_name_change_due_to_existance(Exists, New) ),
     nl( user_error ).
uf_ex_err( false, _Exists, _New ).

next_filename( CharListIn, Vrs, CharListOu_T ) :-
     append( Proper, "/", CharListIn ),
     !,
     next_filename( Proper, Vrs, CharListOut1 ),
     append( CharListOut1, "/", CharListOu_T ).
next_filename( Stem, Vrs, NxtStem ) :-
     % i dont think this necessary any longer
     % ( (break_on_list( CharListIn, ".", Main, Vrss ),
          %    \+ member( 0'/, Vrss ) ) ->
          %    append( ".", Vrss, DotVrss )
          %    ; 
          %    ( append( Main, "/", CharListIn ) ->
          %     DotVrss = "/"
          %     ;
          %     Main = CharListIn, DotVrss = []
          %    )
     % ),
     ( break_on_list( Stem, Vrs, Trunk, Vers ) ->
          next_version( Vers, NextVers )
          ;   
          Trunk = Stem, NextVers = "01"
     ),
     flatten( [Trunk,Vrs,NextVers], NxtStem ).

next_version( [F,S], [F1,S1] ) :-
     next_digit( S, S1, C ),
     ( C =:= 1 ->
          next_digit( F, F1, _ )
          ;
          F1 is F
     ).

next_digit( Din, Dout, Carry ) :-
     Cd9 is 0'9,
     ( Din < Cd9 ->
             Dout is Din + 1,
             Carry is 0
             ;
             ( Din=:=Cd9 -> 
                    Dout is 0'0, 
                    Carry is 1
                    ;
                    Here = unique_filename/3,
                    atom_codes( Atm, [Din] ),
                    throw( error(type_error(digit,Atm),Here) )
               )
     ).

uf_unique_additions( [], _Sep, Stem, Stem ).
uf_unique_additions( [add(Call,Lgt)|T], Sep, Acc, Stem ) :-
     ( call( Call, Res ) ->
          to_codes( Res, ResCs ),
          length( ResCs, ResLgt ),
          ( ResLgt=<Lgt -> 
               AddCs = ResCs
               ;
               break_nth( Lgt, ResCs, AddCs, _ )
          ),
          append( Sep, AddCs, Sfx ),
          append( Acc, Sfx, Nxt )
          ;
          Nxt = Acc
     ),
     uf_unique_additions( T, Sep, Nxt, Stem ).

file_and_extension( FileIn, ExtOpt, Stem, Ext ) :-
     ( file_name_extension(StemHas,ExtHas,FileIn) ->
          ( ExtOpt==ExtHas -> 
               Stem = StemHas,
               Ext = ExtHas
               ;
               ( ExtOpt==null__ -> 
                    Stem = StemHas,
                    Ext = ExtHas
                    ;
                    Stem = FileIn,
                    Ext = ExtOpt
               )
          )
          ;
          Stem = FileIn,
          ( ExtOpt==null__ -> 
               Ext = ''
               ;
               Ext = ExtOpt
          )
     ).

to_codes( Atom, Codes ) :-
     atom( Atom ),
     !,
     atom_codes( Atom, Codes ).
to_codes( Number, Codes ) :-
     number( Number ),
     !,
     number_codes( Number, Codes ).
