function [idx,History] = feval(h,ts)
%FEVAL
%
% Author(s): James G. Owen
% Revised:
% Copyright 1986-2001 The MathWorks, Inc.
% $Revision: 1.1.6.1 $ $Date: 2004/12/26 21:38:20 $

%% Overloaed feval to incorerate logical combinations of rules

s = size(ts.data);
recorder = tsguis.recorder;
History = {};

%% Define rule switches
outliersactive =  h.Outliersposition>0 && ~isempty(h.Outlierwindow) && ...
        h.Outlierwindow<s(1) && ...
        h.Outlierwindow>0 && h.Outlierconf>0;
flatlineactive = h.Flatlineposition>0 && ~isempty(h.Flatlinelength) && ...
        h.Flatlinelength>1 && ...
        size(ts.data,1)>2;
expressionactive = h.Expressionposition>0 && ...
    length(deblank(h.Mexpression))>1;
boundsactive = h.Boundsposition>0 && (~isempty(h.Yhigh) || ...
    ~isempty(h.Ylow) || ~isempty(h.Xhigh) || ~isempty(h.Xlow));

%% Initialize variables
idxBounds = false(s);
idxOutliers = false(s);
idxFlatlines = false(s);
idxExpression = false(s);
t = ts.Time;

%% If macro recording, initialize logical vars
if strcmp(recorder.Recording,'on') 
    History = {'%% Rule selection: Initialize logical variables'; ...
               ['idxBounds = false([' num2str(s) ']);'];...
               ['idxOutliers = false([' num2str(s) ']);']
               ['idxFlatlines = false([' num2str(s) ']);']
               ['idxExpression = false([' num2str(s) ']);']};
end    

%% Process time/data bounds
if boundsactive
    X = ts.Data;
    % Bounds Error checking
    if ~isempty(h.Ylow) && (length(h.Ylow)~=1 && length(h.Ylow)~=s(2))
        error('@exclusion:feval:ylowbounds', ...
            'The Y lower bound must either be a scalar or have length equal to the number of data columns');
    end
    if ~isempty(h.Yhigh) && (length(h.Yhigh)~=1 && length(h.Yhigh)~=s(2))
        error('@exclusion:feval:yhighbounds', ...
            'The Y upper bound must either be a scalar or have length equal to the number of data columns');
    end
    if (~isempty(h.Xlow) && ~isscalar(h.Xlow)) && (~isempty(h.Xhigh) && ~isscalar(h.Xhigh))
        error('@exclusion:feval:xbounds', ...
            'The time upper and lower bounds must be scalar');
    end  
    % Bounds comparison
    unitconv = tsunitconv(h.Xunits,ts.TimeInfo.Units);
    if ~isempty(h.Xlow) && isfinite(h.Xlow) 
        if strcmp(h.Xlowstrict,'on')
            idxBounds(find(t*unitconv>=h.Xlow),:) = true;
            symb = '>=';
        else
            idxBounds(find(t*unitconv>h.Xlow),:) = true;
            symb = '>';
        end
        % Update logical bound var in macro history
        if strcmp(recorder.Recording,'on')               
            History = [History;...
                    {'%% Rule selection: Lower time bound'};...
                    {['idxBounds(find(' ts.Name '.Time ' symb sprintf('%f',h.Xlow/unitconv) '),:) = true;']}];
        end  
    end
    if ~isempty(h.Xhigh) && isfinite(h.Xhigh)
        if strcmp(h.Xhighstrict,'on')
            idxBounds(find(t*unitconv<=h.Xhigh),:) = true;
            symb = '<=';
        else
            idxBounds(find(t*unitconv<h.Xhigh),:) = true;
            symb = '<';
        end   
        % Update logical bound var in macro history
        if strcmp(recorder.Recording,'on')               
            History = [History;...
                    {'%% Rule selection: Lower time bound'};...
                    {['idxBounds(find(' ts.Name '.Time ' symb sprintf('%f',h.Xhigh/unitconv) '),:) = true;']}];
        end 
    end
    if length(h.Ylow)>1 && any(isfinite(h.Ylow))
        if strcmp(h.Ylowstrict,'on')
            idxBounds(any((X>=ones(size(X,1),1)*(h.Ylow(:)'))')') = true;
            symb = '>=';
        else
            idxBounds(any((X>ones(size(X,1),1)*(h.Ylow(:)'))')') = true;
            symb = '>';
        end
        % Update logical bound var in macro history
        if strcmp(recorder.Recording,'on')               
            History = [History;...
                    {'%% Rule selection: Upper data bound'};...
                    {['idxBounds(any((' ts.Name '.Data ' symb  'ones(size(' ts.Name 
                    '.TimeInfo.Length,1),1)*['  num2str(h.Ylow(:)') '])'' )'');']}];
        end        
    elseif length(h.Ylow)==1 && isfinite(h.Ylow)
        if strcmp(h.Ylowstrict,'on')
            idxBounds(X>=h.Ylow) = true;
            symb = '>=';
        else
            idxBounds(X>h.Ylow) = true;
            symb = '>';
        end
        % Update logical bound var in macro history
        if strcmp(recorder.Recording,'on')               
            History = [History;...
                    {'%% Rule selection: Upper data bound'};...
                    {['idxBounds(' ts.Name '.Data ' symb sprintf('%f',h.Ylow) ') = true;']}];
        end 
    end
    if length(h.Yhigh)>1 && any(isfinite(h.Yhigh))
        if strcmp(h.Yhighstrict,'on')
            idxBounds(any((X<=ones(size(X,1),1)*(h.Yhigh(:)'))')') = true;
            symb = '<=';
        else
            idxBounds(any((X<ones(size(X,1),1)*(h.Yhigh(:)'))')') = true;
            symb = '<';
        end
        % Update logical bound var in macro history
        if strcmp(recorder.Recording,'on')               
            History = [History;...
                    {'%% Rule selection: Lower data bound'};...
                    {['idxBounds(any((' ts.Name '.Data ' symb  'ones(size(' ts.Name 
                    '.TimeInfo.Length,1),1)*['  num2str(h.Yhigh(:)') '])'' )'');']}];
        end        
    elseif length(h.Yhigh)==1 && isfinite(h.Yhigh)
        if strcmp(h.Yhighstrict,'on')
            idxBounds(X<=h.Yhigh) = true;
            symb = '<=';
        else
            idxBounds(X<h.Yhigh) = true;
            symb = '<';
        end
        % Update logical bound var in macro history
        if strcmp(recorder.Recording,'on')               
            History = [History;...
                    {'%% Rule selection: Lower data bound'};...
                    {['idxBounds(' ts.Name '.Data ' symb sprintf('%f',h.Yhigh) ') = true;']}];
        end 
    end    
end

%% Process outliers and flatlines bounds      
if outliersactive
    idxOutliers = ts.select('outliers',h.Outlierwindow,h.Outlierconf);
    if strcmp(recorder.Recording,'on')               
          History = [History;...
                    {'%% Rule selection: Outlier selection'};...
                    {['idxOutliers = ' ts.Name '.select(''outliers'',' ...
                    sprintf('%f',h.Outlierwindow) ',' sprintf('%f',h.Outlierconf) ');']}];           
    end 
end
if flatlineactive 
    idxFlatlines = ts.select('flatlines',h.Flatlinelength);  
    if strcmp(recorder.Recording,'on') && any(any(idxFlatlines))            
          History = [History;...
                    {'%% Rule selection: Flatline selection'};...
                    {['idxFlatlines = ' ts.Name '.select(''flatlines'',' ...
                    sprintf('%f',h.Flatlinelength) ');']}];           
    end 
end 
if expressionactive
    try 
        x = ts.Data;
        idxExpression = eval(h.Mexpression);
        if strcmp(recorder.Recording,'on') && any(any(idxExpression))
           History = [History;...
                    {'%% Rule selection: MATLAB expression selection'};...
                    {['x = ' ts.Name '.Data;']};...
                    {['idxExpression = ' h.Mexpression ';']}];
        end
    catch
        msgbox('Invalid MATLAB expression. Note the label x must be used to represent the data',...
              'Time Series Tool')
    end
end

%% Combine the selected points using the logical rules
order = [h.Boundsposition h.Outliersposition h.Flatlineposition ...
    h.Expressionposition];
logic = {h.BoundsLogicalOp,h.OutliersLogicalOp,h.FlatlineLogicalOp,...
    h.ExpressionLogicalOp};
selection = {~idxBounds,idxOutliers,idxFlatlines,idxExpression};
selectionName = {'~idxBounds','idxOutliers','idxFlatlines','idxExpression'};
[order,I] = sort(order);
logic = logic(I);
selection = selection(I);
selectionName = selectionName(I);

if any(order>0)
    ind = min(find(order>0)); % First selected rule
    idx = selection{ind};
    if strcmp(recorder.Recording,'on')    
         History = [History;...
           {'%% Rule based selection: Combining selection rules'};...  
           {['idx = ' selectionName{ind} ';']}];
    end
    for k=ind+1:4 % Loop through remaining selected rules
        if strcmp(logic{k-1},'and')
            idx = idx & selection{k};
            if strcmp(recorder.Recording,'on') 
                History = [History;...
                    {['idx = idx & ' selectionName{k} ';']}];
            end
        else
            idx = idx | selection{k};
            if strcmp(recorder.Recording,'on')
                History = [History;...
                    {['idx = idx  | ' selectionName{k} ';']}];
            end
        end
    end
else
    idx = false(s); % Nothing selected
    if strcmp(recorder.Recording,'on') 
        History = [History;...
           {['idx = false(' num2str(s) ');']}];
    end
end
