function a_plot = plot_bars(mid_vals, lo_vals, hi_vals, n_vals, x_labels, y_labels, ...
			    title_str, axis_limits, props)

% plot_bars - Bar plot with error lines in individual axes for each variable.
%
% Usage:
% a_plot = plot_bars(mid_vals, lo_vals, hi_vals, n_vals, x_labels, y_labels, ...
%		     title_str, axis_limits, props)
%
% Description:
%   Additional rows of data will result in grouped bars in each axis. If all
% data is given as a column vector, then they will appear in a single
% axis. plot_bars is a subclass of plot_stack. The plot_abstract/plot
% command can be used to plot this data. Rows of *_vals will create grouped
% bars, columns will create new axes.
%
% Parameters:
%   mid_vals: Middle points of error bars.
%   lo_vals: Low points of error bars as difference from mid_vals.
%   hi_vals: High points of error bars as difference from mid_vals.
%   n_vals: Number of samples used for the statistic (Optional).
%   x_labels, y_labels: Axis labels for each bar group. Must match with data columns.
%   title_str: Plot description.
%   axis_limits: If given, all plots contained will have these axis limits.
%   props: A structure with any optional properties.
%     dispBarsLines: Choose between using 'bars' or 'lines' to connect the errorbars.
%     dispErrorbars: If 1, display errorbars for lo_vals and hi_vals deviation from mid_vals 
%		     (default=1).
%     dispInnerBars: If 1, an inner bar extends from the base to hi_vals
%     		     (default=0). Mutually exclusive with
%     		     dispInnerBars. It will make the larger bars blank.
%     dispNvals: If 1, display n_vals on top of each bar (default=1).
%     groupValues: List of within-group labels passed to XTickLabels,
%	  	instead of just a sequence of numbers.
%     groupValuesLoc: If 1, use specified group values as the location of bars
%     		      or errorbars. By default locations are set arbitrarily as 1:n.
%     truncateDecDigits: Truncate labels to this many decimal digits.
%     barAxisProps: props passed to plot_abstract objects with bar commands
%     barWidth: Controls spacing between bars (see width argument for the
%     		bar command; default=0.8).
%		
% Returns a structure object with the following fields:
%   plot_abstract
%
% General operations on plot_bars objects:
%   plot_bars	- Construct a new plot_bars object.
%
% Additional methods:
%	See methods('plot_bars')
%
% See also: plot_abstract, plot_abstract/plot
%
% $Id$
%
% Author: Cengiz Gunay <cgunay@emory.edu>, 2004/10/07

% Copyright (c) 2007 Cengiz Gunay <cengique@users.sf.net>.
% This work is licensed under the Academic Free License ("AFL")
% v. 3.0. To view a copy of this license, please look at the COPYING
% file distributed with this software or visit
% http://opensource.org/licenses/afl-3.0.php.

% TODO: save mids, lo, and his to look for axis ranges later

if nargin == 0 % Called with no params
   a_plot = struct;
   a_plot = class(a_plot, 'plot_bars', plot_stack);
 elseif isa(mid_vals, 'plot_bars') % copy constructor?
   a_plot = mid_vals;
 else
   if ~ exist('props', 'var')
     props.rotateXLabel = 45; % Degrees
     %props.XTickLabel = 1;
   end
   
   if isfield(props, 'groupValuesLoc')
     if isfield(props, 'groupValues')
       group_locs = props.groupValues;
     else
       error('Using groupValuesLoc requires setting groupValues.');
     end
   else
     % otherwise use arbitrary values as group_locs and change XTicks to
     % display values if selected
     group_locs = 1:size(mid_vals, 1);
     if isfield(props, 'groupValues') && ...
	 ~(isfield(props, 'XTickLabel') && isempty(props.XTickLabel))
       if isnumeric(props.groupValues) 
         if isfield(props, 'truncateDecDigits') 
           dig_exp = 10^props.truncateDecDigits;
           props.groupValues = round(dig_exp * props.groupValues) / dig_exp;
         end
         props.groupValues = num2cell(props.groupValues);
       end
       props.XTickLabel = props.groupValues;
       props.XTick = group_locs;
     end
   end

   if ~ exist('axis_limits', 'var')
     axis_limits = []; % Degrees
   end

   a_plot = struct;

   num_plots = size(mid_vals, 2);
   plots = cell(1, num_plots);
   if isfield(props, 'barAxisProps')
       bar_axis_props = mergeStructs(props.barAxisProps, props);
   else
       bar_axis_props = props;
   end
   
   if isfield(props, 'dispInnerBars') && props.dispInnerBars == 1
     bar_axis_props = ...
         mergeStructs(bar_axis_props, ...
                      struct('plotProps', struct('FaceColor', 'none')));
   end

   if isempty(x_labels)
     [ x_labels(1:num_plots) ] = deal({''});
   end

   if isempty(y_labels)
     [ y_labels(1:num_plots) ] = deal({''});
   end

   % Loop for each item and create a horizontal stack of plots
   for plot_num=1:num_plots
     if ~isfield(props, 'dispBarsLines') || ...
         strcmp(props.dispBarsLines, 'bars')
       plot_mid_vals = permute(mid_vals(:, plot_num, :), [1, 3, 2]);
       bar_width = getFieldDefault(props, 'barWidth', 0.8);
       plot_components = ...
           {plot_abstract({group_locs, plot_mid_vals, bar_width}, ...
                          {x_labels{plot_num}, y_labels{plot_num}}, '', ...
                          {title_str}, 'bar', ...
                          bar_axis_props)};
       linestyle = 'none';
     elseif strcmp(props.dispBarsLines, 'lines')
       % Enforce errorbar display then
       props.dispErrorbars = 1;
       plot_components = {};
       linestyle = '-';
     else
       error([ 'Optional argument dispBarsLines has unknown value: ' ...
               props.dispBarsLines ]);
     end

     if isfield(props, 'dispInnerBars') && props.dispInnerBars == 1
       props.dispErrorbars = 0; % mutually exclusive with errorbars
       plot_components = ...
	   {plot_components{:}, ...
	    plot_abstract({group_locs, ...
                           mid_vals(:,plot_num) + hi_vals(:,plot_num), 0.2}, ...
			  {x_labels{plot_num}, y_labels{plot_num}}, ...
                          '', {}, 'bar', props)};
     end

     if (~isfield(props, 'dispErrorbars') || props.dispErrorbars == 1) && ...
           ~ isempty(lo_vals) && ~ isempty(hi_vals)
       plot_components = ...
	   {plot_components{:}, ...
	    plot_abstract({group_locs, mid_vals(:,plot_num), ...
			   lo_vals(:,plot_num), hi_vals(:,plot_num), 'LineStyle', linestyle}, ... % '+'
			  {x_labels{plot_num}, y_labels{plot_num}}, '', {title_str}, 'errorbar', props)};
     end

     if (~isfield(props, 'dispNvals') || props.dispNvals == 1) && ...
           ~ isempty(n_vals)
       if size(n_vals, 2) ~= num_plots
         error(['Argument n_vals does not have ' num2str(num_plots) ' elements.']);
       end
       plot_components = ...
	   {plot_components{:}, ...
	    plot_abstract({group_locs, ...
			   mid_vals(:,plot_num) + hi_vals(:,plot_num), ...
                           cellfun(@(x) sprintf('n=%d', x), num2cell(n_vals(:,plot_num)), ...
                                   'UniformOutput', false), ...
			   'HorizontalAlignment', 'center', ...
			   'VerticalAlignment', 'bottom', ...
                           'FontSize', 8}, ...
			  {x_labels{plot_num}, y_labels{plot_num}}, ...
			  '', {}, 'text', props)};
     end

     plots{plot_num} = plot_superpose(plot_components, {}, '', ...
				      mergeStructs(props, struct('noLegends', 1)));
   end

   a_plot = class(a_plot, 'plot_bars', ...
		  plot_stack(plots, axis_limits, 'x', title_str, props));
end

% cellstr(strcat('n=', num2str(n_vals(:,plot_num))))