:- lib( en_list/2 ).
:- lib( expand_spec/2).

% :- op( 400, fx, (/) ).  % see lib(os)

%% locate( +File, +Exts, -Locations ).
%
%  Find the exact Location of File that may have a number of extensions.
%  This should become the standard way to interface locating of reading in files.
%  =|Exts = any/*|=, is a special case where any file with matching extension
%  is returned. This case is slower than the rest.
%
%  As of 0.2 only existing files are located. Predicate throws error if file does not exist.
%
%==
%  locate( xyw, abc, Loc ).
%  ERROR: Unhandled exception: Cannot locate file with specification: xyw and extensions: abc
%  
%  
%==
% @author nicos angelopoulos
% @version  0.2 2014/4/24
%
locate( File, Exts, Location ) :-
	locate( File, Exts, '', Location),
	!.
locate( File, Exts, _Location ) :-
	throw( locate(cannot_locate(File,Exts)) ).
	% print_message( error, locate(cannot_locate(File,Exts)) ).

locate( FileSpec, Exts, DefExt, Location ) :-
	( Exts == *; Exts == any ),
	!,
	expand_spec( FileSpec, Expanded ),
	locate_expanded( Expanded, [], '', AbsFile ),
	file_directory_name( AbsFile, Dir ),
	directory_contents( Dir, DFiles, _, _ ),
	directory_files( Dir, Entries ),
	include( exists_file, Entries, DFiles ),
	file_base_name( AbsFile, Bname ),
	include( loc_prefix_atom(Bname), DFiles, Candidates ),
	locate_in_prefix_candidates( Candidates, Bname, DefExt, LcBname ),
	directory_file_path( Dir, LcBname, Location ).
locate( File, Exts, DefExt, Location ) :-
	en_list( Exts, ExtsL ),
	expand_spec( File, Expanded ),
	locate_expanded( Expanded, ExtsL, DefExt, Location ).

% when file exists:
locate_expanded( Expanded, Exts, _DefExt, Location ) :-
	append( Exts, [''], Extensions ),
	Opts = [  extensions(Extensions), access(read),
	          file_errors(fail), solutions(first)   ], 
	absolute_file_name( Expanded, Location, Opts ).
/* commended out in version 0.2, fail with error if you cannot find it. this is for existing files.
locate_expanded( Expanded, _Exts, DefExt, Location ) :-
	Opts = [ access(none), solutions(all) ], 
	absolute_file_name( Expanded, Stem, Opts ),
	file_directory_name( Stem, Dir ),
	exists_directory( Dir ),
	!,
	file_name_extension( Stem, DefExt, Location ).
	*/

loc_prefix_atom( Pfx, Atom ) :-
	atom_concat( Pfx, _, Atom ).

% avoid adding the same extension twice
locate_in_prefix_candidates( [], File, DefExt, Location ) :-
	file_name_extension( _, DefExt, File ),
	!,
	Location = File.
locate_in_prefix_candidates( [], File, DefExt, Location ) :-
	!,
	file_name_extension( File, DefExt, Location ).
locate_in_prefix_candidates( [H|_T], File, _DefExt, Location ) :-
	file_name_extension( File, _Ext, H ),
	!,
	Location = H.
locate_in_prefix_candidates( [_H|T], File, DefExt, Location ) :-
	locate_in_prefix_candidates( T, File, DefExt, Location ).

:- multifile prolog:message//1.
:- discontiguous message//1.

% this is crucial for allowing cathcable exceptions that print when 
prolog:message(unhandled_exception(locate(Message))) -->
	{ debug( locate, 'Unhandled ~p', Message ) },
	message(Message).

prolog:message(locate(Message)) -->
	message(Message).

message( cannot_locate(File,Exts) ) -->
	[ 'Cannot locate file with specification: ~q and extensions: ~q'-[File,Exts] ].
