function [obj] = estimateGivenModel(obj)
% ESTIMATEGIVENMODEL estimates a local neuro-fuzzy model to a given net
% sturcture. The function allows to refine an already trained LOLIMOT
% object. If the user realizes, that his net is not good enough, the
% algorithmn can continue processing without stating all over again.
%
% obj = estimateModel(obj)
%
% INPUT/OUTPUT:
%   obj:  object    LOLIMOT object containing all relevant net and data set
%                   information and variables.
%
%
% LoLiMoT - Nonlinear System Identification Toolbox
% Torsten Fischer, 08-December-2011
% Institute of Mechanics & Automatic Control, University of Siegen, Germany
% Copyright (c) 2012 by Prof. Dr.-Ing. Oliver Nelles


% Define some variables
obj.numberOfInputs = size(obj.input,2);
obj.numberOfOutputs = size(obj.output,2);

% Check if the properties of the local model objects are given correctly
if numel(obj.localModels) ~= numel(obj.leafModels)
    error('lolimot:estimateGivenModel','number of active models is not equal to the number of local model objects!')
end

for k = 1:numel(obj.localModels)
    
    % Check if corners are given correctly
    if (isempty(obj.localModels(k).lowerLeftCorner) || isempty(obj.localModels(k).upperRightCorner) || ...
            (numel(obj.localModels(k).upperRightCorner)~=size(obj.input,2)) || (numel(obj.localModels(k).lowerLeftCorner)~=size(obj.input,2)))
        error('lolimot:estimateGivenModel','no corner given for at least one hyper-rectangle! training aborted!')
    end
    
    % Check if the gaussians are given or have to be calculated
    if isempty(obj.localModels(k).center) || isempty(obj.localModels(k).standardDeviation)
        % Calculate the centers and standard deviation from corner positions and smoothness
        [obj.localModels(k).center,obj.localModels(k).standardDeviation] = ...
            obj.localModels.corner2Center(obj.localModels(k).lowerLeftCorner,obj.localModels(k).upperRightCorner,obj.smoothness);
    end
    
    % Check if the boudaries are known by the object
    if isempty(obj.localModels(k).zLowerBound) || isempty(obj.localModels(k).zUpperBound)
        % Add boundaries to each local model
        obj.localModels(k).zLowerBound = min(obj.zRegressor);
        obj.localModels(k).zUpperBound = max(obj.zRegressor);
    end
end

% Calculate Gaussians, if they are not given in the net object
if numel(obj.MSFValue) ~= numel(obj.leafModels)
    obj.MSFValue = arrayfun(@(loc) loc.calculateMSF(obj.zRegressor),obj.localModels,'UniformOutput',false);
end

% Initialize the allowed local model
obj.idxAllowedLM = obj.leafModels;

% Validities of all leaf local models
validityFunctionValue = obj.calculateVFV(obj.MSFValue(obj.leafModels));

% Indicies of the leaf models
idxLeafModels = find(obj.leafModels);

for k = 1:numel(idxLeafModels)
    
    % Check for sufficient amount of data samples
    if obj.pointsPerLMFactor * size(obj.xRegressor,2) - sum(validityFunctionValue{k}) > eps
        obj.idxAllowedLM(k) = false;
        warning('lolimot:estimateGivenModel',['local model No. ' num2str(idxLeafModels(k)) 'of the given model structure has not enough valid point to estimate the parameters of the local model properly.'])
    else
        obj.idxAllowedLM(k) = true;
    end
    
    % parameters of all leaf local models
    obj.localModels(idxLeafModels(k)).parameter = estimateParametersLocal(obj, obj.xRegressor, obj.output, validityFunctionValue{k}, [], obj.dataWeighting);
    
end

% Calculate model output
obj.outputModel = calculateModelOutput(obj);

% Calculate the local loss function values for all leaf models
localLossFunctionValue = calcLocalLossFunction(obj, obj.unscaledOutput, obj.outputModel , cell2mat(validityFunctionValue), obj.dataWeighting);
for k = 1:numel(idxLeafModels)
    % Allocate the right local loss function value to each local model
    obj.localModels(idxLeafModels(k)).localLossFunctionValue = localLossFunctionValue(k);
end

% Set number of iteration
if obj.history.iteration > 0
    obj.history.iteration = obj.history.itertion - 1;
end

end