function geotimeline(varargin)
%geotimeline(start date,end date,'stages' OR 'all') OR geotimeline(stage name,'stages' OR 'all')
%
%This function produces a schematic timeline showing the ordering and
%relative hierarchy of stages in the geologic timescale.  This function
%can be called either with start and end dates so that it shows the
%divisions in that time interval, or with the name of a particular stage
%in which case it display that stage and the surrounding ones.  If called
%with no arguments, it displays the whole length of geologic time.
%
%If the last argument is the word 'stages', this routine will include the
%lowest level ICS stages/ages.  If the last argument is 'all', the routine
%will include all stages including regional and archaic usages.  If both 
%words are omitted, the timeline displayed will only include divisions down
%to Sub-Epochs and higher.
%
%Note: the columns only indicate relative heirarchy, they do not conform to
%particular categories of time.  This is because different part of the record
%are divided to differing degrees of detail (e.g. sub-era, sub-epochs, etc).

%Created by Robert A. Rohde (geowhen@robertrohde.com)
%Last Modified January 18, 2005

%load the geologic database
stages = geologicstages;
%define the heirarchy of standard categories
divisions = {'Eon','Era','Period','Epoch','Sub-Epoch','ICS Stage'};

%faunal is a indicator of whether to display the finest level of stages
%0 = no, 1 = yes.  Defaults to no.
faunal = 0;
%stageprint is a indicator of whether to display the ICS stages
%0 = no, 1 = yes.  Defaults to no.
stageprint = 0;

%store the inputs in editable variables.
nargs = nargin;
va = varargin;

%if there is input, check for the word 'stages','faunal', or 'all'
if nargs > 0
	if (strcmp(lower(va{nargs}),'faunal'))
		%if true, set indicator to yes.
		faunal = 1;
		%set to ignore final argument.
		nargs = nargs - 1;
	elseif (strcmp(lower(va{nargs}),'all'))
		%if true, set indicator to yes.
		faunal = 1;
		%set to ignore final argument.
		nargs = nargs - 1;
   elseif (strcmp(lower(va{nargs}),'stages'))
		%if true, set indicator to yes.
		stageprint = 1;
		%set to ignore final argument.
		nargs = nargs - 1;
   end
end
%this is an indicator of whether to limit the display to an interval of time
%0 = no, 1 = yes.  No is default.
interval = 0;
%if there are 2 arguments, use them to set the start and end time and set
%interval to true
if nargs > 1
	starttime = va{1};
	endtime = va{2};
	%Type Checking
	if ~isnumeric(starttime)
		if (faunal == 1)
			error('First Argument is Not Numeric');
		else
			error('Two or more arguments which are not numeric, and/or word ''stages'' not recognized');
		end
	end
	if ~isnumeric(endtime)
		error('Second argument is neither numeric, nor the word ''stages''.');
	end
	%if out of order, switch them
	if starttime < endtime
	t = starttime;
	starttime = endtime;
	endtime = t;
end
%set interval to true.
interval = 1;
end
%Else If there is one argument, look for matching stage name and set interval around it. 
if nargs == 1
	%if the one argument is numeric, use geowhen to display stages during that date.
	if isnumeric(va{1})
		geowhen(va{1});
		return;
		%else look for the stage matching the given name
	elseif ischar(va{1})
		%the location in the stages array of the record we want.  Set to 0 until found.
		fnd = 0;
		%loop over all stages looking for a match
		for a = 1:length(stages)
			if strcmp(stages(a).Name,va{1})
				fnd = a;
				break;
			else
				%loop over the aliases
				for b = 1:length(stages(a).AKA)
					if strcmp(stages(a).AKA{b},va{1})
						fnd = a;
						break;
					end
				end
				%If stated name was an alias, display message indicating primary name.
				if fnd~=0
					disp(' ');
					disp(['  ---  ' va{1} ' Also Known As ' stages(a).Name '  ---']);
					break;
				end
			end
		end
		%if not found, give an error.
		if fnd==0
			error(['Stage Not Recognized: ' va{1}]);
		end
		%Type of category for this stage.
		CatName = stages(fnd).Category;
		%Find this category name in the list of divisions and record it's number
		%if not in list, indicate as length of list + 1.
		CatNum = length(divisions) + 1;
		for a = 1:length(divisions)
			if strcmp(lower(CatName),lower(divisions{a}))
				CatNum = a;
			end
		end
	
		%if division is Epoch or lower, force it to use the stage list.
		if CatNum >= length(divisions)-2
			stageprint = 1;
		end
	
		%indicate the index numbers in the data structure of the stages
		%before and after the current stage.  0 until found.
		before = 0;
		follow = 0;
		%if category was in the divisions list, we want the stages of same category
		%that come before and after.
		if CatNum <= length(divisions)
			%loop over stages looking for same category
			for b = 1:length(stages)
				if strcmp(stages(b).Category,CatName)
					if stages(b).Start == stages(fnd).End
						follow = b;
					elseif stages(b).End == stages(fnd).Start
						before = b;
					end
				end
			end
			%Duration of stage, used if before or after not found.
			td = stages(fnd).Start - stages(fnd).End;
			%set start of interval
			if before ~= 0
				starttime = stages(before).Start;
			else
				starttime = stages(fnd).Start + td;
			end
			%set end of interval
			if follow ~= 0
				endtime = stages(follow).End + 0.0001;
			else
				endtime = stages(fnd).End - td;
			end
			%set indicator of interval to true.
			interval = 1;
			%if category was not on divisions list then simply take it's length
			%and add to time before and after   
		else
			td = stages(fnd).Start - stages(fnd).End;
			starttime = stages(fnd).Start + td;
			endtime = stages(fnd).End - td;
			%set indicator of interval to true.
			interval = 1;
		end
	end				
end

%Figures out the category number for all of the stages in the database.
%Used for sorting.
cats = lower(strvcat(stages(:).Category));
[stages.CatNum] = deal(length(divisions)+1);
for a = 1:length(divisions);
	stn = strmatch(lower(divisions{a}),cats);
	[stages(stn).CatNum] = deal(a);
end

%if we don't want the lowest stages, then remove them from the record.
if ~faunal
	ct = cat(1,stages(:).CatNum);
	f = find(ct <= length(divisions));
	stages = stages(f);
end
if ~stageprint & ~faunal
	ct = cat(1,stages(:).CatNum);
	f = find(ct < length(divisions));
	stages = stages(f);
end

%Create lists of the start and end times of each stage
tslist = cat(1,stages(:).Start);
telist = cat(1,stages(:).End);
%create a superlist that emphasizes the start date but
%also gives weight to the end date so that longer stages
%appear before short stages.
tlist = tslist + telist*1e-8;
%use this list to sort the stage list.
[tlist2,re] = sort(tlist);
stages = stages(re(end:-1:1));

%create a list of the start times, for stages that we want to include
%tlast is the last time added to the list, to avoid repetitions.
%initial value set to impossibly long ago.
tlast = 1e6;
%cell array of start times and their errors
times = {};
%loop over all stages
for a = 1:length(stages)
	%if we haven't already added this time...
	if stages(a).Start ~= tlast
		%if set to an interval, check bounds
		if interval
			if ((stages(a).Start > starttime)&(stages(a).End < endtime))|(stages(a).End < starttime)
				times{end+1,1} = stages(a).Start;
				times{end,2} = stages(a).dStart;
				tlast = stages(a).Start;
			end
			%if beyond the endtime, set endtime to the start of first following stage and
			%exit the loop.
			if stages(a).Start < endtime
			endtime = stages(a).Start;
			break;
		end
		%else, add every time.
	else
		times{end+1,1} = stages(a).Start;
		times{end,2} = stages(a).dStart;
		tlast = stages(a).Start;
	end			
end
end

%if set to only a specific interval, remove all stages that
%don't overlap on the interval
if interval
	a = 1;
	while a <= length(stages)
		if ~((stages(a).End <= starttime) & (stages(a).Start >= endtime))
			stages = stages([1:(a-1),(a+1):end]);
			a = a - 1;
		end
		a = a +1;
	end	
end	

%FINALLY... we start the output.
disp(' ');
%loop over all the transition times.
for a = 1:length(times)
	%b is an index to the position in the stages array while seraching
	b = 1;
	%current is an array of stages that are present at the current time.
	%c is an index in the current array.
	current = stages(1);
	c = 1;
	%loop over stages to find those active during the current time.
	%loop exits when stages are starting after the current time
	%since stages list was time sorted on start times.
	while (stages(b).Start >= times{a,1})
		%if stage ends after the current time then add it to the list.
		if (stages(b).End < times{a,1})
			current(c) = stages(b);
			c = c + 1;
			%else remove it from the stages list as no longer neccesary.
		else
			stages = stages([1:(b-1),(b+1):end]);
			b = b - 1;
		end
		b = b + 1;
		%if out of stages to sort, then break.
		if (b > length(stages))
			break;
		end
	end
	%loop over the current array and sort it by position
	%in the heirarchy of stages.
	for j = 1:length(current)
		t1 = current(j);
		for b = 1:length(current)
			t2 = current(b);
			if t1.CatNum < t2.CatNum
				t = current(j);
				current(j) = current(b);
				current(b) = t;
				t1 = t2;
			end
		end
	end
	%str is the string to be printed.
	str = '';
	%loop over the current array and add the names to str.
	for c = 1:length(current)
		%if name was previously printed and it is a defined heirarchy stage
		%then we print only blanks in it's place.
		if current(c).CatNum <= length(divisions) & current(c).Start~=times{a,1}
			str = [str, blanks(length(current(c).Name)),' - '];
			%else, just add the name   
		else
			str = [str, current(c).Name,' - '];
		end
	end
	%dst is the error associated with the current time
	dst = times{a,2};
	%process the possible formats for the error.
	if isempty(dst)
		dst = '';
	end
	if iscell(dst)
		d1 = dst{1};
		d2 = dst{2};
		if isnumeric(d1)
			d1 = num2str(abs(d1));
		end
		if isnumeric(d2)
			d2 = num2str(abs(d2));
		end
		dst = ['+' d1 '/-' d2];
	end
	if isnumeric(dst)
		dst = ['+/- ' num2str(dst)];
	end

	%drop the last three characters ' - ' which aren't needed after the last name.
	str = str(1:(end-3));
	%st equals the string version of the time.
	st = num2str(times{a,1});
	%if there is an error estimate, add a seperator space.
	if ~isempty(dst)
		dst = [dst,' '];
	end
	%format the final string.
	stra = [st ' ' dst];
	str = [blanks(20 - length(stra)) stra 'Ma: ' str];
	%display it.
	disp(str);
end
disp(' ');
