function cdef = obtainCrystal(crystalName, genericCode, flagEnforce)
% obtain crystal definition from database
%
% Syntax: cdef = obtainCrystal(name)
%
% Return a crystal definition that is stored in the local database. All
%   crystals used during the elk-project can be found here.
%
% obtainCrystal('init') is used to initialize the database. You should use
%   this whenever you added an example to this library.
%
% obtainCrystal without any argument returns the possible options, 
%   including the information, whether it is a generic example. Information
%   is returned as a struct array: cdef(i).crystalName cdef(i).isGeneric.
%   If no output variable is specified, the possible names are printed on
%   the screen.
% 
% obtainCrystal(..., par) is used for generic crystal definitions for which, 
%   based on a required set of facets that fixes the crystal position, the 
%   additional set is specified. Par may be a logic index vector, of a 
%   integer number, which decodes this vector. The correct length can be 
%   determined by the common informations, obtained by getCrystal(name).
%
% For templates to generate a new crystal definition, use 'database/cuboid'
%   or 'database/generic_potash_alum' as templates.
%
% See also: viewCrystal 

% 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/>.

%% input handling
if ~exist('crystalName', 'var')
    crystalName = '';
end
if ~exist('genericCode', 'var')
    genericCode = [];
end
if ~exist('flagEnforce', 'var')
    flagEnforce = false;
end

%% handle coding
if islogical(genericCode)
    % cast [1 0 1 1] to [1 3 4] - 1 = [0 2 3]
    genericCode = uint32(find(genericCode) - 1);
end
if numel(genericCode) > 1 && ~isa(genericCode, 'double')
    % cast [0 2 3] to [1 4 8] (binary encoding) and sum up
    genericCode = sum(2.^genericCode);
end
% create file name (even though, we not yet know if the case is generic)
if isempty(genericCode)
    fileName = [crystalName '.mat'];
else
    fileName = [crystalName '_' num2str(genericCode) '.mat'];
end

%% database handling
% try to load crystal from previous calculations (speedup)
if ~flagEnforce && ~isempty(crystalName) && ~strcmpi(crystalName, 'init')
    try
        cdef = obtainData(fileName(1:end-4), ...
            'database', ['.' filesep 'data-crystal'], 'type', 'mat');
        % if the above is not an error, we already have what we wanted
        return
    catch
        flagEnforce = true;
    end
else
    % if init command, enforce second call for database
    flagEnforce = true;
end
% use database definition files
if flagEnforce
    cdef = obtainData(crystalName, 'prompt', nargout == 0);
end

%% evaluate output
if ~exist('crystalName', 'var') || isempty(crystalName) || ...
        strcmpi(crystalName, 'init')
    % case - no cdef requested
    if nargout == 0
        clear cdef
    end
    return
elseif cdef.isGeneric && ~isempty(genericCode)
    
    for iMiller = 1:size(cdef.millerGeneric, 1)
        if bitand(genericCode, 2^(iMiller - 1) ) ~= 0
            cdef.millerDefined(end+1,:) = cdef.millerGeneric(iMiller, :);
        end
    end
end
cdef = extendCdef(cdef);

if ~isempty(genericCode) && ~cdef.isGeneric
    warning('elk:crystal:notGeneric', ['Non-generic crystal definition ' ...
        'called with generic code. To obtain speedup in loading cystal ' ...
        'data, call obtainCrystal(..) or obtainCrystal(.., []) for ' ...
        'non-generic cases.']);
    % enforce proper file name
    fileName = [crystalName '.mat'];
    cdef.genericCode = [];
else
    cdef.genericCode = genericCode;
end

%% save data
save([fileparts(which('obtainCrystal')) filesep 'data' filesep fileName], ...
    'cdef')
obtainData('init', 'database', ['.' filesep 'data'], ...
    'type', 'mat', 'prompt', -1);

end