function glout = geolist(match)
%stage info = geolist(matching string)
%
%This function uses the geologicstages.m database and attempts to locate those
%stage names that most closely match the specified "matching string".
%
%It starts this by trying to find all the stages names or aliases that start
%with exactly the characters in matching string.  If exactly one stage is found
%that starts with the requested characters then geowhen is called to give a
%detailed description of that stage.  Otherwise it lists all the stages that
%begin with that string.  Should no stage start with the requested string, this
%function returns a list of the stages which are alphabetically closest.
%
%If output arguments are requested, similar content as shown to the screen is
%also returned in a cell array.  The columns of which are "stage name", "start
%date", "end date" and "duration".
%
%Note that if geolist is called with no arguments, all stages are listed.

%Created by Robert A. Rohde (geowhen@robertrohde.com)
%Last Modified August 26, 2003

%load the geological database structure
stages= geologicstages;

%index counter for number of names, starts at 1.
c = 1;
%loop over all stages in data structure.
for a = 1:length(stages);
	%create Names array with the vital info for the list.
	Names{c,1} = stages(a).Name;
	Names{c,2} = stages(a).Start; 
	Names{c,3} = stages(a).End;
	Names{c,4} = stages(a).Start-stages(a).End;
	%numerical representation of a stage's name, used for sorting.
	gls{c} = lower(stages(a).Name')-'a';
	%increment the counter.
	c = c + 1;
	%Do the same for each name in the AKA list. 
	for b = 1:length(stages(a).AKA)
		Names{c,1} = stages(a).AKA{b};
		Names{c,2} = stages(a).Start;
		Names{c,3} = stages(a).End;
		Names{c,4} = stages(a).Start-stages(a).End;
		gls{c} = lower(stages(a).AKA{b}')-'a';
		c = c +1;
	end
end

%create a single numeric value associated with each string.
%note: this section can misfire on some special characters.
for a = 1:length(gls)
	t(a) = 0;
	for b = 1:length(gls{a})
		t(a) = t(a) + gls{a}(b)/26^(b-1);
	end
end

%sort the numbers to figure out the appropriate order for the list.
[t,re] = sort(t');
t = [t,re];

%if called with arguments, perform the matching ritual and throw away those
%which don't conform.
if (nargin > 0)
	%matching is not case sensitive, so lower it.
	match = lower(match);
	%length of the matching string
	lm = length(match);
	%nt array will hold the indices to the names that start with the matching string.
	nt = [];
	%loop over all possible names.
	for a =1:length(t)
		if (strncmp(lower(Names{t(a,2),1}),match,lm))
			nt = [nt;t(a,:)];
		end
	end
	%if no names start with it, then find the names which are closest alphabetically
	if isempty(nt)
		%ts will be a unique number associated to the matching string.
		%a similar number is associated with each element from computing t above.
		ts = 0;
		match = match - 'a';
		for a = 1:lm
			ts = ts + match(a)/26^(a-1);
		end
	
		%figure out where it fits in t.	 
		k = findk(t(:,1),ts);
		%select adjacent elements, and perform bounds checking.
		mn = k - 8;
		mx = k + 8;
		if (mn < 1)
			mn = 1;
			mx = 16;
		end
		if (mx > length(t))
			mx = length(t);
			mn = length(t) - 16;
		end
		%set nt to be the ones selected	 
		nt = t(mn:mx,:);
	end
	%else if only one element matched call geowhen to display the
	%details of that stage.
	if (length(nt(:,1)) == 1)
		geowhen(Names{nt(1,2),1});
		return;
	end
	%truncate t to just the elements selected. 
	t = nt;   
end

%reuse variable name for new purpose because everyone enjoys recycling.
%gls will now be used to collect the outputs that are returned if output
%was requested.
gls = {[],[],[],[]};
nmax = 0;
ymax = 0;
%figure out the maximum length needed for certain strings.
for a = 1:length(t)
	if (length(Names{t(a,2),1})>nmax)
		nmax = length(Names{t(a,2),1});
	end
	yl = length(num2str(Names{t(a,2),4}));
	if (yl>ymax)
		ymax = yl;
	end
end
%since most durations are short, don't allow more than 5 spaces
if (ymax > 5)
	ymax = 5;
end
disp(' ');
%display the list
for a = 1:length(t)
	%prepare the output returns
	gls{a,1} = Names{t(a,2),1};
	gls{a,2} = Names{t(a,2),3};
	gls{a,3} = Names{t(a,2),2};
	gls{a,4} = Names{t(a,2),4};
	%prepare formatted output for display.
	str = ['   ' gls{a,1} blanks(nmax-length(gls{a,1})) ' (' num2str(gls{a,2}) ' - ' num2str(gls{a,3}) ')'];
	str = [str, blanks(nmax + 20 - length(str)), ' ', num2str(gls{a,4})];
	str = [str, blanks(ymax - length(num2str(gls{a,4}))), ' Myr'];
	disp(str);
	%make a note if any stage has negative duration
	if (gls{a,4} < 0)
		disp(' ----  BAD DATES HERE  ---- ');
	end
end
%note how many stages displayed
disp(' ');
disp(['  ' num2str(length(t)), ' Stages Displayed']);
disp(' ');

%return output, if requested.
if nargout > 0
	glout = gls;
end


function k = findk(value,vector);
% function k = findk(value,vector);
% finds the k such that vector(k) is closest to value

[val,k] = min(abs(vector-value));
