function vrep = convertHrepToVrep(hrep, zerotol, infinityBoxSize, useCddlib)
% convert the H-representation of a polytope into V-representation
%
% Syntax: vrep = convertHrepToVrep(hrep)
%   or    vrep = convertHrepToVrep(hrep, zerotol)
%   or    vrep = convertHrepToVrep(hrep, zerotol, infinityBoxSize)
% 
% The parameter zerotol applies for scaleHrep only (default is elkZerotol).
%
% Consider that cddlib applies an additional zerotol that is stored in the 
%   function zerotolCddlib during initialization of this library 
%   (buildCddlib).
%
% The parameter infinityBoxSize can be provided and is used when the given
%   H-representation is unbounded. In that case the V-representation of the
%   original object intersected with an infinity box is calculated. The
%   infinity box is a hypercube, centered at the origin and extended to 
%   [-1, 1] for each dimension. Default: 1e3.
%
% There are two underlying methods to perform this conversion. The direct
%   approach uses the double-description method (cddlib). An alternative 
%   approach is to utilize duality and use convertVrepToHrep with the 
%   underlying triangulation (convhulln). The default is choosen by speed 
%   and reliability. The alternative is used if this approach fails.
%
% convert(.., prefereCddlib) gives the possibility to change the default
%   standard algorithm to be used. Default: 0
%
% See also: convertPolytope, convertVrepToHrep, scaleHrep

% The elk-library: convex geometry applied to crystallization modeling.
%   Copyright (C) 2012 Alexander Reinhold
%
% This program is free software: you can redistribute it and/or modify it 
%   under the terms of the GNU General Public License as published by the 
%   Free Software Foundation, either version 3 of the License, or (at your 
%   option) any later version.
%
% This program is distributed in the hope that it will be useful, but 
%   WITHOUT ANY WARRANTY; without even the implied warranty of 
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
%   General Public License for more details.
%
% You should have received a copy of the GNU General Public License along 
%   with this program.  If not, see <http://www.gnu.org/licenses/>.

%% validate input
if ~exist('zerotol', 'var') || isempty(zerotol)
    zerotol = elkZerotol;
end
if ~exist('infinityBoxSize', 'var') || isempty(infinityBoxSize)
    infinityBoxSize = 1e3;
end
if ~exist('useCddlib', 'var') || isempty(useCddlib)
    useCddlib = 1;
end
if size(hrep.A, 1) ~= size(hrep.h, 1)
    error('elk:polytope:wrongType', ...
        'given H-representation is invalid, check if facet normals are a column vector');
end
nDim = size(hrep.A, 2);

%% Shortcut for simplices
% might be possible
% if size(hrep.A, 1) == (dim + 1) && rank(hrep.A - hrep.A(1, 2)
% end

%% reduce dimension & scaling
[hrepReduced reduceTrafo reduceOffset] = reduceHrepDimension(hrep, zerotol);
if isempty(hrepReduced)
    % empty polytope
    vrep = [];
    return
end
nDimReduced = size(hrepReduced.A, 2);
if nDimReduced == 0
    % 0-dimensional polytope (single point)
    vrep.V = reduceOffset';
    return
end
% The remaining polytope is at least one-dimensional.

%% scaling
[hrepScaled, scale, scaleOffset] = scaleHrep(hrepReduced, zerotol);
if scale == -inf
    % empty polytope
    vrep.V = zeros(0, nDim);
    return
end
if scale == 0
    % flat polytope
    error('elk:polytope:numericalError', ['Polytope appears to be flat ' ...
          'after reduceHrepDimension is aplied']);
end
if scale == inf
    % unbounded polytope >> apply infinityBox, retry scaling and continue
    boundingBoxHrep.A = [eye(nDimReduced); -1*eye(nDimReduced)];
    boundingBoxHrep.h = infinityBoxSize*ones(2*nDimReduced,1);
    hrepReduced.A = [hrepReduced.A; boundingBoxHrep.A];
    hrepReduced.h = [hrepReduced.h; boundingBoxHrep.h];
%     hrepReduced.h = [hrepReduced.h; boundingBoxHrep.h]
    [hrepScaled, scale, scaleOffset] = scaleHrep(hrepReduced, 0.5*zerotol);
    if scale == inf || scale == 0 || scale == -inf
        error('elk:polytopeOperation:numericalError', ['Oh, come on! ' ...
            'first assumed to be unbounded and now the polytope appears ' ...
            'empty flat or even still unbounded. I am giving up. Not your ' ...
            'fault, though. This should not happen.']);
    end
end
%! DEV: the code below was used beforehand to additionally identify
%       unbounded polytopes. This should not be necessary.
% [u singularValueVector v] = svd(hrep.A);
% if size(singularValueVector, 1) == 1 || size(singularValueVector, 2) == 1
%     singularValueVector = singularValueVector(1);
% else
%     singularValueVector = diag(singularValueVector);
% end
% if (scale == inf) || (~all(abs(singularValueVector) > zerotol))
%     % also unbounded case
% end
%/

%% normal processing
% outer conversion structure
useAlternative = 0;
while useCddlib > -1
try
    if useCddlib
        %% direct approach (cddlib)
        vrepScaled = convertHrepToVrepByCddlib(hrepScaled, 1);
    else
        %% dual approach (convhulln)
        % map to dual polytope in H-representationcomputeDualPolytope(hrepScaled)
        dualVrep = computeDualPolytope(hrepScaled);
        
        % convert to dual Vrep
        dualHrep = convertVrepToHrep(dualVrep, zerotol, inf, 0);
        
        % convert back to normal Hrep
        vrepScaled = computeDualPolytope(dualHrep);
    end
    % assign to exit loop:
    useCddlib = -1;
catch thisError
    if ~useAlternative
        % we do not rethrow the original error, as the cddlib error does
        %   not contain any hint.
        warning('elk:polytope:conversionMethod', ...
               ['Using alternative approach to convert polytope ' ...
                '(cddlib returned an error).']);
        useAlternative = 1;
        useCddlib = ~useCddlib;
        % try again
        continue
    end
        % safe input polytope
    if exist('log_convertHrepToVrep.mat', 'file')
        load log_convertHrepToVrep.mat
        logHrep{end+1} = hrep;
    else
        logHrep{1} = hrep;
    end
    save log_convertHrepToVrep.mat logHrep
    warning('elk:polytope:method', ...
        ['Given hrep cannot be converted to vrep (usually cddlib). ' ...
         'Polytope stored in: ' cd filesep 'log_convertHrepToVrep.mat']);
    rethrow(thisError);
end
end

%% Create result
% revert scaling
vrep = stretchVrep(vrepScaled, scale, zeros(nDimReduced, 1));
vrep = moveVrep(vrep, scaleOffset);
% revert dimension cutoff
if nDimReduced < nDim
    vrep.V = [vrep.V zeros(size(vrep.V, 1), nDim - nDimReduced)];
    vrep.V = vrep.V * reduceTrafo;
end
