function query = DoEUpdate(obj)
% DOEUPDATE is the central algorithm for experimental design with the HiLoMoT algorithm.
%
%
% [query] = DoEUpdate(obj)
%
%
% INPUT
%
%   obj:    object   HILOMOTDOE object containing all relevant properties and methods.
%
%
% OUTPUT
%
%   query:  ? x p    New point(s) for measurement (query points).
%
%
% HiLoMoTDoE - Nonlinear System Identification Toolbox for Design of Experiments
% Benjamin Hartmann, 05-April-2012
% Institute of Mechanics & Automatic Control, University of Siegen, Germany
% Copyright (c) 2012 by Prof. Dr.-Ing. Oliver Nelles


% Get constants
obj.numberOfInputs = size(obj.inputMeasured,2);

% Logics
if isempty(obj.numberOfInitialPoints)
    % Set number of initial points, if no existing data available so far
    obj.numberOfInitialPoints = 2*(obj.numberOfInputs+1);
end
if ~obj.flagSpaceFilling
    % Check, if last measurement failed (NaN) or if #valid < #initial points
    numberOfValidPoints = sum(~isnan(obj.output));
    obj.flagSpaceFilling = isnan(obj.output(end)) || numberOfValidPoints < obj.numberOfInitialPoints;
end
if isempty(obj.output)
    obj.designProgram = 1; % Initialization phase
elseif ~obj.flagSpaceFilling
    obj.designProgram = 2; % Active learning phase
else
    obj.designProgram = 3; % Measurement failure
end

% Set parameters for normalization of input variables to interval [0,1]
MaxVal = max(obj.candidatePoints,[],1);
MinVal = min(obj.candidatePoints,[],1);

% Perform normalization
candidatePointsNorm = obj.DoEScaleData(obj.candidatePoints,MaxVal,MinVal,'f');
inputMeasuredNorm   = obj.DoEScaleData(obj.inputMeasured,MaxVal,MinVal,'f');
inputTargetNorm     = obj.DoEScaleData(obj.inputTarget,MaxVal,MinVal,'f');
% Note: The normalization also could be done outside this method for better
%       performance!

% Get number of samples
switch obj.designProgram
    
    case 1
        
        % Check for sufficient amount of initial points
        if obj.numberOfInitialPoints <= obj.numberOfInputs
            obj.numberOfInitialPoints = obj.numberOfInputs+1;
        end
        
        % Pick first point randomly from candidate set
        rand('seed',23);
        idxRand    = ceil(rand(1,1)*size(obj.candidatePoints,1));
        firstPoint = candidatePointsNorm(idxRand,:);
        
        % Generate initial points with space filling strategy
        queryNorm = obj.DoEPMCPoint(obj.numberOfInitialPoints, firstPoint, candidatePointsNorm);
        
    case 2
        
        % Generate empty net structure for Hilomot
        obj.LMNObject = hilomot;
        
        % Set options for Hilomot
        obj.LMNObject.numberOfPoints     = 2*(obj.numberOfInputs+1); % Doubled amount of samples in a LM as local parameters
        obj.LMNObject.smoothness         = obj.smoothnessSplit;
        obj.LMNObject.maxTrainTime       = 1; % Maximum training time for each data point in [min]
        obj.LMNObject.LOOCV              = true;
        obj.LMNObject.optLOOCV           = true;
        obj.LMNObject.xRegressorDegree   = 1;
        obj.LMNObject.scaleInput         = [];
        obj.LMNObject.lossFunctionGlobal = 'RMSE';
        obj.LMNObject.history.displayMode = false;
        
        % Store data in hilomot object and delete NaNs in data set for modeling
        idxNotNaN = ~isnan(obj.output); % Find all points that are not NaN's in the output
        warning('off','dataSet:setInput')
        obj.LMNObject.input  = inputMeasuredNorm(idxNotNaN,:);
        obj.LMNObject.output = obj.output(idxNotNaN,:);
        
        % Stop criterion for maximum number of LMs: N/2*(n+1)
        numberOfValidPoints = sum(idxNotNaN); % All points for modeling that are not NaN
        obj.LMNObject.maxNumberOfLM = ceil(numberOfValidPoints/(obj.numberOfInputs+1)/2);
        if ~isempty(obj.maxNumberOfLM) && obj.LMNObject.maxNumberOfLM > obj.maxNumberOfLM
            obj.LMNObject.maxNumberOfLM = obj.maxNumberOfLM; % Upper bound for max no. of local models
        end
        
        % Train LMN object with Hilomot
        obj.LMNObject = obj.LMNObject.train;
        
        % Select model with best complexity tradeoff
        LMN = obj.LMNObject;
        obj.LMNObject.leafModels = LMN.history.leafModelIter{LMN.suggestedNet};
               
        % Calculate query point with trained Hilomot object
        queryNorm = obj.DoEQuery;
       
    case 3
        
        % Set new query globally spacefilling
        queryNorm = obj.DoEPMCPoint(1, inputTargetNorm, candidatePointsNorm);
        
end

% Reverse normalization for query
query = obj.DoEScaleData(queryNorm,MaxVal,MinVal,'b');

% Find next incremental point for measurement, if variable increments are given explicitly
if ~isempty(obj.variableIncrements)
    query = obj.DoEFindNextIncrement(query, obj.variableIncrements);
    if obj.designProgram > 1
        count = 1;
        while sum(ismember(obj.inputTarget,query,'rows'))>0
            warning('hilomotDoE:DoEUpdate','Point was already measured!')
            queryNorm = obj.DoEPMCPoint(1, inputTargetNorm, candidatePointsNorm);
            if ~ismember(inputTargetNorm,queryNorm,'rows')
                inputTargetNorm = [inputTargetNorm; queryNorm];
                count = count+1;
                if count > 10
                    error('hilomotDoE:DoEUpdate','Not able to select any further candidate point! Maybe candidate points or variable increments are too sparse.')
                    break
                end
            else
                break
            end
            query = obj.DoEScaleData(queryNorm,MaxVal,MinVal,'b');
            query = obj.DoEFindNextIncrement(query, obj.variableIncrements);
        end
    end
end
end

