function qstyle(gs,h,varargin)
% qstyle  Apply styles to graphics object and its descandants.
%
% Syntax
% =======
%
%     qstyle(H,S,...)
%
% Input arguments
% ================
%
% * `H` [ numeric ] - Handle to a graphics object that will be styled along
% with its descandants (unless `'cascade='` is false).
%
% * `S` [ struct ] - Struct each field of which refers to an
% object-dot-property; the value of the field will be applied to the the
% respective property of the respective object; see below the list of
% graphics objects allowed.
%
% Options
% ========
%
% * `'cascade='` [ *`true`* | `false` ] - Cascade through all descendants of the
% object `H`; if false only the object `H` itself will be styled.
%
% * `'warning='` [ *`true`* | `false` ] - Display warnings produced by this
% function.
%
% Description
% ============
%
% The style structure, `S`, is constructed of any number of nested
% object-property fields:
%
%     S.object.property = value;
%
% The following is the list of standard Matlab grahics objects the
% first-level fields can refer to:
%
% * figure
% * axes
% * title
% * xlabel
% * ylabel
% * zlabel
% * line
% * bar
% * patch
% * text
%
% In addition, you can also refer to the following special instances of
% objects created by IRIS functions:
%
% * legend (an axes object)
% * plotpred (line objects with prediction data created by `plotpred`)
% * highlight (a patch object created by `highlight`)
% * highlightcaption (a text object created by `highlight`)
% * vline (a line object created by `vline`)
% * vlinecaption (a text object created by `vline`)
% * zeroline (a line object created by `zeroline`)
%
% The property used as the second-level field is simply any regular Matlab
% property of the respective object (see Matlab help on graphics).
%
% The value assigned to a particular property can be either of the
% following:
%
% * a single proper valid value (i.e. a value you would be able to assign using
% the standard Matlab `set` function);
% * a cell array of multiple different values that will be assigned to the
% objects of the same type in order of their creation;
% * a text string starting with a double exclamation point, `!!`, followed
% by Matlab commands. The commands are expected to eventually create a
% variable named `SET` whose value will then assigned to the respective
% property. The commands have access to variable `H`, a handle to the
% current object.
%
% Example
% ========
%

% -IRIS Toolbox.
% -Copyright (c) 2007-2012 Jaromir Benes.

% Parse options.
options = passvalopt('qreport.qstyle',varargin{:});

% Swap style struct and graphic handle if needed.
if all(ishghandle(gs))
    [gs,h] = deal(h,gs);
end

%**************************************************************************

if ischar(gs)
    % Called with a file name.
    % Remove extension.
    [fpath,ftitle] = fileparts(gs);
    gs = xxrungsf(fullfile(fpath,ftitle),@run);
elseif iscellstr(gs) && length(gs) == 1
    % Called directly with commands.
    gs = strtrim(gs{1});
    gs = strrep(gs,'"','''');
    if ~isempty(gs)
        if gs(end) ~= ';'
            gs(end+1) = ';';
        end
        gs = xxrungsf(gs,@eval);
    end
end

for i = h(:).'
    if ~ishghandle(i)
        continue
    end
    switch get(i,'type')
        case 'figure'
            xxfigure(i,gs,options);
        case 'axes'
            xxaxes(i,gs,options);
        case 'line'
            line_(i,gs,options);
        case 'patch'
            patch_(i,gs,options);
    end
end
    
end

% Subfunctions.

%**************************************************************************
function d = xxrungsf(gsf,func)
    % Run graphic style file and create graphic style database.
    axes = [];
    figure = [];
    label = [];
    line = [];
    title = [];
    func(gsf);
    d = struct();
    d.axes = axes;
    d.figure = figure;
    d.label = label;
    d.line = line;
    d.title = title;
end
% xxrungsf().

%**************************************************************************
function xxapplyto(h,d,field,options)
    
    h = findobj(h,'flat','-not','userData','excludeFromStyle');
    if isempty(h)
        return
    end
    
    % Make fieldnames in the qstyle struct case-insensitive.
    list = fieldnames(d);
    index = strcmpi(field,list);
    if ~any(index)
        return
    end
    d = d.(list{index});
    
    nh = length(h);
    list = fieldnames(d);
    for i = 1 : length(list)
        x = d.(list{i});
        if ~iscell(x)
            x = {x};
        end
        nx = numel(x);
        name = regexprep(list{i},'_*$','');
        for j = 1 : nh
            value = x{1+rem(j-1+options.offset,nx)};
            try
                if ischar(value) && strncmp(strtrim(value),'!!',2)
                    % Execture style processor.
                    value = qreport.styleprocessor(h(j),value);
                end
                set(h(j),name,value);
            catch Error
                flag = xxexceptions(h(j),name,value);
                if ~flag && options.warning
                    warning('iris:qreport',...
                        ['Error setting %s property ''%s''.\n', ...
                        'Matlab says: %s'],...
                        field,name,Error.message);
                end
            end
        end
    end
    
end
% xapplyto().

%**************************************************************************
function xxfigure(h,d,options)
    if isempty(h)
        return
    end
    h = h(:)';
    xxapplyto(h,d,'figure',options);
    if options.cascade
        for i = h
            %{
            % Find all children with titles, and style the titles.
            obj = findobj(i,'-property','title');
            xxtitle(obj(:).',d,options);
            %}
            % Find all axes.
            obj = findobj(i,'type','axes');
            xxaxes(obj(:).',d,options);
        end
    end
end
% xxfigure().

%**************************************************************************
function xxtitle(h,d,options)
    for ih = h(:).'
        tt = get(ih,'title');
        if ~isempty(tt) && ~isempty(get(tt,'string'))
            xxapplyto(tt,d,'title',options);
        end
    end
end
% xxtitle().

%**************************************************************************
function xxaxes(H,D,OPT,CHKPEER)
    if isempty(H)
        return
    end
    try
        CHKPEER; %#ok<VUNUS>
    catch %#ok<CTCH>
        CHKPEER = true;
    end
    % Find all legend axes, and apply the legend style to them. Do not
    % cascade through the legend axes.
    lg = findobj(H,'flat','Tag','legend');
    xxapplyto(lg,D,'legend',OPT);
    % Find the remaining regular axes. Cascade through them if requested by
    % the user.
    H = findobj(H,'flat','-not','Tag','legend');
    H = H(:).';
    xxapplyto(H(end:-1:1),D,'axes',OPT);
        
    % First, objects that can only have one instance within each parent
    % axes object. These are considered part of the axes and are styled
    % even if cascade is false.
    for i = H
        % Check if this axes has a plotyy peer.
        peer = [];
        if CHKPEER
            peer = getappdata(i,'graphicsPlotyyPeer');
        end
        % The axes obj itself.
        axesobj = i;
        % Handles to axis labels.
        xlabelobj = get(i,'xlabel');
        ylabelobj = get(i,'ylabel');
        zlabelobj = get(i,'zlabel');
        % Handles to titles.
        titleobj = get(i,'title');
        if ~isempty(peer)
            axesobj(end+1) = peer; %#ok<AGROW>
            xlabelobj(end+1) = get(peer,'xlabel'); %#ok<AGROW>
            ylabelobj(end+1) = get(peer,'ylabel'); %#ok<AGROW>
            zlabelobj(end+1) = get(peer,'zlabel'); %#ok<AGROW>
            titleobj(end+1) = get(peer,'title'); %#ok<AGROW>
        end
        xxapplyto(axesobj,D,'axes',OPT);
        xxapplyto(xlabelobj,D,'xlabel',OPT);
        xxapplyto(ylabelobj,D,'ylabel',OPT);
        xxapplyto(zlabelobj,D,'zlabel',OPT);
        xxapplyto(titleobj,D,'title',OPT);
    end

    % Then, objects that can have multiple instance within a parent
    % axes object.
    if OPT.cascade
        
        for i = H
            
            % Check if this axes has a plotyy peer.
            peer = [];
            if CHKPEER
                peer = getappdata(i,'graphicsPlotyyPeer');
            end
            
            for j = [i,peer]
                
                % Find handles to all line objects except those crated by
                % `zeroline`, `vline`, and the prediction data plotted by
                % `plotpred`.
                lineobj = findobj(j,'type','line', ...
                    '-and','-not','tag','zeroline', ...
                    '-and','-not','tag','vline', ...
                    '-and','-not','tag','plotpred');
                xxapplyto(lineobj(end:-1:1).',D,'line',OPT);
                
                % Find handles to prediction data lines created by `plotpred`.
                plotpredobj = findobj(j,'type','line','tag','plotpred');
                xxapplyto(plotpredobj.',D,'plotpred',OPT);
                
                % Find handles to zerolines. Do not revert the order of
                % handles.
                zerolineobj = findobj(j,'type','line','tag','zeroline');
                xxapplyto(zerolineobj.',D,'zeroline',OPT);
                
                % Find handles to vlines. Do not revert the order of handles.
                vlineobj = findobj(j,'type','line','tag','vline');
                xxapplyto(vlineobj.',D,'vline',OPT);
                
                % Bar graphs.
                barobj = findobj(j,'-property','barWidth');
                xxapplyto(barobj(end:-1:1).',D,'bar',OPT);
                
                % Find handles to all patches except highlights and fancharts.
                patchobj = findobj(j,'type','patch', ...
                    '-and','-not','tag','highlight', ...
                    '-and','-not','tag','fanchart');
                xxapplyto(patchobj(end:-1:1).',D,'patch',OPT);
                
                % Find handles to highlights. Do not revert the order of
                % handles.
                highlightobj = findobj(j,'type','patch','tag','highlight');
                xxapplyto(highlightobj.',D,'highlight',OPT);
                
                % Find handles to fancharts. Do not revert the order of
                % handles.
                fanchartobj = findobj(j,'type','patch','tag','fanchart');
                xxapplyto(fanchartobj.',D,'fanchart',OPT);
                
                % Find handles to all text objects except zeroline captions and
                % highlight captions.
                textobj = findobj(j,'type','text', ...
                    '-and','-not','tag','zeroline-caption', ...
                    '-and','-not','tag','vline-caption');
                xxapplyto(textobj(end:-1:1).',D,'text',OPT);
                
                % Find handles to vline-captions and highlight-captions.
                vlinecaptionobj = findobj(j,'tag','vline-caption');
                xxapplyto(vlinecaptionobj(end:-1:1).',D,'vlinecaption',OPT);
                highlightcaptionobj = findobj(j,'tag','highlight-caption');
                xxapplyto(highlightcaptionobj(end:-1:1).',D, ...
                    'highlightcaption',OPT);
                
            end
            
        end
    end
    
end
% xaxes().

%**************************************************************************
function flag = xxexceptions(h,name,value)
    flag = true;
    switch get(h,'type')
        case 'axes'
            switch lower(name)
                case 'yaxislocation'
                    if strcmpi(value,'either')
                        grfun.axisoneitherside(h,'y');
                    end
                case 'xaxislocation'
                    if strcmpi(value,'either')
                        grfun.axisoneitherside(h,'x');
                    end
                case 'yticklabelformat'
                    yTick = get(h,'yTick');
                    yTickLabel = cell(size(yTick));
                    for i = 1 : length(yTick)
                        yTickLabel{i} = sprintf(value,yTick(i));
                    end
                    set(h,'yTickLabel',yTickLabel, ...
                        'yTickMode','manual', ...
                        'yTickLabelMode','manual');
                case 'tight'
                    if isequal(value,true) || isequal(lower(value),'on')
                        grfun.yaxistight(h);
                    end
                case 'clicktocopy'
                    if isequal(value,true)
                        grfun.clicktocopy(h);
                    end
                otherwise
                    flag = false;
            end
        case 'patch'
            switch lower(name)
                case 'basecolor'
                    if strcmpi(get(h,'tag'),'fanchart')
                        whi = get(h,'userData');
                        facecol = get(h,'faceColor');
                        if ischar(facecol) && strcmpi(facecol,'none')
                            grfun.excludefromlegend(h);
                        else
                            facecol = whi*[1,1,1] + (1-whi)*value;
                        end;
                        set(h,'faceColor',facecol);
                    end
                otherwise
                    flag = false;
            end
    end
end
% xxexception().