%DIP_MEASUREMENT   Constructor method.
%   DIP_MEASUREMENT(B) converts the array or structure B to a
%   dip_measurement object.
%
%   If B is a struct, it must have this form:
%      B(ii).id       = Label (numeric) for object ii.
%      B(ii).msrname1 = Result of measurement 1 on object ii.
%      B(ii).msrname2 = Result of measurement 2 on object ii.
%      B(ii).msrname3 = ...
%          ...
%   If 'id' is missing, 1:N is assumed.
%
%   DIP_MEASUREMENT(ID,'MSRNAME1',MSR1,'MSRNAME2',MSR2,...)
%   creates a dip_measurement structure containing the id's in
%   ID, and the measurements 'MSRNAME1', 'MSRNAME2', etc. with
%   associated data MSR1, MSR2, etc. If the measurement names
%   are missing, 'data1', 'data2', etc. are assumed. If ID is
%   missing, 1:N is assumed, but only if a measurement name
%   is the first parameter (else there is no way of knowing
%   what the first parameter is). Finally, ID, MSR1, MSR2, etc.
%   must have the same number of columns; a column is a vector
%   representing the measurement result on one object.

% (C) Copyright 1999-2002               Pattern Recognition Group
%     All rights reserved               Faculty of Applied Physics
%                                       Delft University of Technology
%                                       Lorentzweg 1
%                                       2628 CJ Delft
%                                       The Netherlands
%
% Cris Luengo, November 2000.
% 26 March 2001:    Added multiple-parameter input modes.
% 21 April 2001:    Fixed inconsistency: 'data' and 'names' now always
%                   are row cell arrays.
% 5 February 2002:  Measurement names are not case-sensitive any more.
% 13 February 2002: Improved creation of dip_measurement objects by
%                   the dip_measure function.


% Undocumented syntax (used by dip_measure):
%    DIP_MEASUREMENT('TRUST_ME',B) converts the struct B to a
%    dip_measurement object. No conversion is done, but some
%    checking is. B must be in the form of a dip_meas object:
%
% dip_measurement objects contain these elements:             size:
% 'id'        Array with the object IDs.                      (1,L)
% 'data'      Cell array with the data arrays.                (1,N)with(M,L)
% 'names'     Cell array with the names for the measurements. (1,N)
% we need to add:
% 'axes'      Cell array with cell arrays with strings        (1,N)with(M,1)
% 'units'     Cell array with cell arrays with strings        (1,N)with(M,1)
%
% L: number of objects
% N: number of measurements
% M: number of values for a specific measurement
%
%    ID     names{1}     names{1}     names{2}
%           axes{1}{1}   axes{1}{2}   axes{2}{1}    <- new & useful
%           units{1}{1}  units{1}{2}  units{2}{1}   <- new & useful
% --------------------------------------------------
%    id(1)  data{1}(1,1) data{1}(2,1) data{2}(1,1)
%    id{2}  data{1}(1,2) data{1}(2,2) data{2}(1,2)
%    id{3}  data{1}(1,3) data{1}(2,3) data{2}(1,3)
%
% How would we obtain these new elements from outside the object?
% For the moment, this data is only for display.


function out = dip_measurement(varargin)
data = struct('id',[],'data',[],'names',[]);
data.data = cell(0); data.names = cell(0);
if nargin == 1
   in = varargin{1};
   if isa(in,'dip_measurement')
      out = in;
      return
   elseif isa(in,'struct')
      % Backwards-compatability: measurement structure
      % (resulting from the STRUCT function)
      in = in(:);
      if length(in)<1
         error('Illegal input.')
      end
      data.names = fieldnames(in)';
      N = length(data.names);
      I = strcmpi(data.names,'ID');
      if ~any(I)
         data.id = 1:length(in);
      else
         I = find(I); I = I(1);
         data.id = eval(['[in.',data.names{I},']']);
         if length(data.id) ~= length(in) | length(unique(data.id)) ~= length(data.id)
            error('Object ID array in input structure is not OK.')
         end
         N = N-1;
         data.names(I) = [];
      end
      if N<1
         error('Illegal input.')
      end
      data.data = cell(1,N);
      for ii=1:N
         data.data{ii} = full(double(eval(['[in.',data.names{ii},']'])));
      end
   elseif isnumeric(in)
      s = size(in);
      if isempty(s) | ~isnumeric(in) | length(s)~=2
         error('Illegal input.')
      else
         data.data = {full(double(in))};
         data.id = 1:s(2);
         data.names = {'data'};
      end
   else
      error(['Conversion to dip_measurement from ',class(in),' is not possible.'])
   end
elseif nargin > 1
   if nargin == 2 & ischar(varargin{1}) & strcmpi(varargin{1},'trust_me')
      % Undocumented feature: used by dip_measure
      data = varargin{2};
      if ~isstruct(data) | length(data)~=1
         error('Creating dip_measurement object: Expected structure as input.')
      end
      if ~isequal(fieldnames(data),{'id';'data';'names'})
         error('Creating dip_measurement object: Received illegal structure as input (1).')
      end
      N = prod(size(data.data));
      if prod(size(data.names)) ~= N
         error('Creating dip_measurement object: Received illegal structure as input (2).')
      end
      L = prod(size(data.id));
      for ii=1:N
         if size(data.data{ii},2) ~= L
            error('Creating dip_measurement object: Received illegal structure as input (3).')
         end
      end
   else
      % Advanced syntax!
      if ~ischar(varargin{1})
         n = 2;
         if ~isnumeric(varargin{1})
            error('Expected numeric data in argument 1.');
         end
         N = prod(size(varargin{1}));
         data.id = reshape(varargin{1},1,N);
      else
         n = 1;
         data.id = [];
         N = 0;
      end
      q = 1;
      while n<=nargin
         if ischar(varargin{n})
            data.names{q} = varargin{n};
            n = n+1;
         else
            data.names{q} = ['data',num2str(q)];
         end
         if any(strcmpi(data.names{q},data.names(1:q-1)))
            error('Repeated names for measurements.');
         end
         if n>nargin
            error('Unexpected end of parameter list.');
         end
         if ~isnumeric(varargin{n})
            error(['Expected numeric data in argument ',num2str(n),'.']);
         else
            data.data{q} =  full(double(varargin{n}));
            if N == 0
               N = size(data.data{q},2);
               data.id = 1:N;
            else
               if N ~= size(data.data{q},2)
                  error(['Wrong number of data points in in argument ',num2str(n),'.']);
               end
            end
         end
         q = q+1;
         n = n+1;
      end
   end
else
   % Create empty object.
end
out = class(data,'dip_measurement');
