function h = harray(base,layout,par)

% harray - create hierarchical axes array
% ---------------------------------------
%
% h = harray(base,layout,par)
%
% Input:
% ------
%  base - base layout
%  layout - hierarchical array layout
%  par - parent figure
%
% Output:
% -------
%  h - harray structure array

%--------------------------------
% Author: Harold Figueroa
%--------------------------------
% $Revision: 976 $
% $Date: 2005-04-25 19:27:22 -0400 (Mon, 25 Apr 2005) $
%--------------------------------

%---------------------------------------------------------------
% HANDLE INPUT
%---------------------------------------------------------------

%--
% set default parent
%--

% NOTE: the use of 'fig' here is the only local dependence

if ((nargin < 3) || isempty(par))
	par = fig;
end

%--
% set default layout
%--

if ((nargin < 2) || isempty(layout))
	
	layout(1) = layout_create(2,3);
	
	layout(2) = layout_create(2,2); 
		
	layout(2).col.frac = [0.75, 0.25]; layout(2).row.frac = [0.75, 0.25];
		
	% NOTE: use this test flag for the various debug chunks
	
	test_flag = 1;
	
else
		
	test_flag = 0;
	
end

%--
% check layout
%--

[flag,str] = layout_check(layout);

if (any(~flag))
		
	if (length(layout) == 1)
		
		disp(' ');
		error(str);
		
	else
		
		disp(' ');
		for k = 1:length(layout)
			disp(str{k});
		end
		disp(' ');
		error('All layouts are not valid.');
		
	end
	
end

%--
% set base layout
%--

if ((nargin < 1) || isempty(base))
	base = layout_create(0,0); 	
end

%---------------------------------------------------------------
% CREATE HIERARCHICAL ARRAY
%---------------------------------------------------------------

%--
% create toolbar axes
%--

% TODO: add capability for multiple toolbars

par_pos = get_size_in(par,base.units);

if (base.tool.on)
	
	pos = [ ...
		0, par_pos(4) - base.tool.size, par_pos(3), base.tool.size ...
	];

	tax = axes( ...
		'parent', par, ...
		'tag', 'HARRAY_TOOLBAR', ...
		'box', 'on', 'xtick', [], 'ytick', [], ...
		'color', get(0,'defaultuicontrolbackgroundcolor'), ...
		'units', base.units, 'position', pos ...
	);

end

%--
% create statusbar axes
%--

if (base.status.on)
	
	pos = [ ...
		0, 0, par_pos(3), base.status.size ...
	];

	sax = axes( ...
		'parent', par, ...
		'tag', 'HARRAY_STATUSBAR', ...
		'box', 'on', 'xtick', [], 'ytick', [], ...
		'color', get(0,'defaultuicontrolbackgroundcolor'), ...
		'units', base.units, 'position', pos ...
	);
	
end

%--
% create colorbar axes
%--

% NOTE: we use a single colorbar since a figure has a single colormap

if (base.color.on)

	pos(1) = par_pos(3) - (base.color.size + base.margin(2));
	
	pos(2) = base.margin(3) + ...
		(base.status.on * base.status.size) + ...
		layout(1).margin(3) ...
	; 

	pos(3) = base.color.size;
	
	pos(4) = par_pos(4) - ( ...
		base.margin(1) + base.margin(3) + ...
		(base.tool.on * base.tool.size) + (base.status.on * base.status.size) + ...
		layout(1).margin(1) + layout(1).margin(3) ...
	);

	cax = axes( ...
		'parent', par, ...
		'tag','HARRAY_COLORBAR', ...
		'box', 'on', 'xtick', [], 'ytick', [], 'color', 'none', ...
		'units', base.units, 'position', pos ...
	);
	
end

%--
% create base axes
%--

pos(1) = base.margin(4);

pos(2) = base.margin(3) + (base.status.on * base.status.size); 

pos(3) = par_pos(3) - ( ...
	base.margin(2) + base.margin(4) + ...
	(base.color.on * (base.color.size + base.color.pad)) ...
);


pos(4) = par_pos(4) - ( ...
	base.margin(1) + base.margin(3) + ...
	(base.tool.on * base.tool.size) + (base.status.on * base.status.size) ...
);

ax = axes( ...
	'parent', par, ...
	'units', base.units, 'position', pos ...
);

h(1) = harray_create;

h(1).level = 0;

h(1).axes = ax;

%--
% create axes for layout levels
%--

for k = 1:length(layout)

	%--
	% find parent axes for this level
	%--
		
	lev = cell2mat({h.level}');
	
	ix = find(lev == (k - 1));
	
	for j = 1:length(ix)
	
		%--
		% compute positions of children relative to this parent
		%--
		
		pos = pos_rel(h(ix(j)).axes,layout(k));
		
		%--
		% create children axes and hierarchical array structures
		%--
		
		for i2 = 1:layout(k).col.size
			
		for i1 = 1:layout(k).row.size

			%--
			% create axes
			%--

			% NOTE: the max protects us from non-positive width and height
			
			ax = axes( ...
				'parent', par, ...
				'units', layout(k).units, 'position', max(pos{i1,i2},eps) ...
			);

			if (test_flag)
				
				% DEBUG: by coloring we can distinguish the axes on resize

				set(ax,'color',1 - 0.75 * rand(1,3));

				% DEBUG: display level and position information

				set(get(ax,'title'), ...
					'string',['L', int2str(k), ' (', int2str(i1), ',', int2str(i2), ')'] ...
				);
			
			end
		
			%--
			% add created axes to parent's children
			%--

			h(ix(j)).children(i1,i2) = ax;

			%--
			% add new element to harray array
			%--

			h(end + 1) = harray_create;

			h(end).level = k;

			h(end).index = [h(ix(j)).index, i1, i2];

			h(end).axes = ax;
			
			h(end).parent = h(ix(j)).axes;

		end
			
		end
				
	end
	
end

%--
% pad harray indices 
%--

nix = length(h(end).index);

% NOTE: we can break because the index lengths are increasing

for k = 1:length(h)
	
	ixl = length(h(k).index);
	
	if (ixl < nix)
		h(k).index = [h(k).index, zeros(1,nix - ixl)];
	else
		break;
	end
	
end

%--
% store harray data in tagged axes and make these invisible
%--

data.type = 'harray';

data.base = base;

data.layout = layout;

data.harray = h;

set(h(1).axes, ...
	'tag','HARRAY_BASE_AXES', ...
	'userdata', data, ...
	'visible', 'off', ...
	'hittest', 'off' ...
);

%--
% set resize function
%--

set(par, ...
	'resizefcn',@harray_resize ...
);

if (test_flag)
	
	% NOTE: this produces a display that is clearer to examine

	ax = cell2mat({h.axes}');

	set(ax, ...
		'box', 'on', ...
		'xticklabel', [], ... 
		'yticklabel', [] ...
	);

end


%---------------------------------------------------------------
% HARRAY_RESIZE
%---------------------------------------------------------------

function flag = harray_resize(par,eventdata)

% harray_resize - resize function for harray display figures
% ----------------------------------------------------------
%
% flag = harray_resize(par,eventdata)
%
% Input:
% ------
%  par - parent figure handle
%  eventdata - not currently used
%
% Output:
% -------
%  flag - success flag

%--------------------------------
% Author: Harold Figueroa
%--------------------------------
% $Revision: 976 $
% $Date: 2005-04-25 19:27:22 -0400 (Mon, 25 Apr 2005) $
%--------------------------------

%--
% get harray data
%--

data = harray_data(par);

% NOTE: rename for convenience, due to delayed copy this just references

base = data.base;

layout = data.layout;

% disp(sprintf(to_xml(layout))); 

h = data.harray;

%--
% get parent figure size
%--

par_pos = get_size_in(par,base.units);

%--
% resize toolbar axes
%--

if (base.tool.on)
	
	pos = [ ...
		0, par_pos(4) - base.tool.size, par_pos(3), base.tool.size ...
	];

	set(findobj(par,'tag','HARRAY_TOOLBAR'), ...
		'units', base.units, 'position', pos ...
	);

end

%--
% create status axes
%--

if (base.status.on)
	
	pos = [ ...
		0, 0, par_pos(3), base.status.size ...
	];

	set(findobj(par,'tag','HARRAY_STATUSBAR'), ...
		'units', base.units, 'position', pos ...
	);
	
end

%--
% resize colorbar axes
%--

% NOTE: we use a single colorbar since a figure has a single colormap

if (base.color.on)

	pos(1) = par_pos(3) - (base.color.size + base.margin(2) + layout(1).margin(2));
	
	pos(2) = base.margin(3) + ...
		(base.status.on * base.status.size) + ...
		layout(1).margin(3) ...
	; 

	pos(3) = base.color.size;
	
	pos(4) = par_pos(4) - ( ...
		base.margin(1) + base.margin(3) + ...
		(base.tool.on * base.tool.size) + (base.status.on * base.status.size) + ...
		layout(1).margin(1) + layout(1).margin(3) ...
	);

	set(findobj(par,'tag','HARRAY_COLORBAR'), ...
		'units', base.units, 'position', pos ...
	);

end

%--
% resize base axes
%--

pos(1) = base.margin(4);

pos(2) = base.margin(3) + (base.status.on * base.status.size); 

pos(3) = par_pos(3) - ( ...
	base.margin(2) + base.margin(4) + ...
	(base.color.on * (base.color.size + base.color.pad)) ...
);

pos(4) = par_pos(4) - ( ...
	base.margin(1) + base.margin(3) + ...
	(base.tool.on * base.tool.size) + (base.status.on * base.status.size) ...
);

set(h(1).axes,...
	'units', base.units, 'position', pos ...
);

%--
% resize other axes in a top down way
%--

flag = 1;

for k = 1:length(layout)

	%--
	% find parent axes for this level
	%--
	
	lev = cell2mat({h.level}');
	
	ix = find(lev == (k - 1));
	
	for j = 1:length(ix)
	
		%--
		% compute new positions of children relative to parent
		%--
		
		pos = pos_rel(h(ix(j)).axes,layout(k));
		
		%--
		% update children positions
		%--
		
		for i2 = 1:size(pos,2)
			
			for i1 = 1:size(pos,1)

				if (any(pos{i1,i2} <= 0))
					flag = 0;
				end

				% NOTE: the max protects us from non-positive width and height

				set(h(ix(j)).children(i1,i2), ...
					'units', layout(k).units, 'position', max(pos{i1,i2},eps) ...
				);

			end
			
		end

	end
	
end


%---------------------------------------------------------------
% POS_REL
%---------------------------------------------------------------

% TODO: develop tree based layout descriptions, this function will work

function pos = pos_rel(par,layout)

% pos_rel - compute children positions relative to parent
% -------------------------------------------------------
%
% pos = pos_rel(par,layout)
%
% Input:
% ------
%  par - parent axes
%  layout - children layout
%
% Output:
% -------
%  pos - children positions relative to parent

%--------------------------------
% Author: Harold Figueroa
%--------------------------------
% $Revision: 976 $
% $Date: 2005-04-25 19:27:22 -0400 (Mon, 25 Apr 2005) $
%--------------------------------

%--
% get size of parent in margin units and update using margins
%--

par_pos = get_size_in(par,layout.units);

par_pos(1:2) = par_pos(1:2) + layout.margin([4,3]);

par_pos(3) = par_pos(3) - (layout.margin(2) + layout.margin(4));

par_pos(4) = par_pos(4) - (layout.margin(1) + layout.margin(3));

%--
% compute positions of children based on level configuration
%--

% NOTE: even for uniform layouts pad and frac are vectors

dx = (par_pos(3) - sum(layout.col.pad)) .* layout.col.frac;

dy = (par_pos(4) - sum(layout.row.pad)) .* fliplr(layout.row.frac);

for j = 1:layout.col.size
	
	for i = 1:layout.row.size

		pos{i,j} = [ ...
			par_pos(1) + sum(layout.col.pad(1:j - 1)) + sum(dx(1:j - 1)), ...
			par_pos(2) + sum(layout.row.pad(1:i - 1)) + sum(dy(1:i - 1)), ...
			dx(j), dy(i) ...
		];

	end
	
end

pos = flipud(pos);


%---------------------------------------------------------------
% HARRAY_CREATE
%---------------------------------------------------------------

function h = harray_create

% harray_create - create structure for harray elements
% ----------------------------------------------------
%
% h = harray_create

%--------------------------------
% Author: Harold Figueroa
%--------------------------------
% $Revision: 976 $
% $Date: 2005-04-25 19:27:22 -0400 (Mon, 25 Apr 2005) $
%--------------------------------

h.axes = [];

h.index = [];

% NOTE: a level is stored explicitly for convenience

h.level = [];

% NOTE: the harray elements are linked up and down

h.parent = [];

h.children = [];