function varargout = fread(obj, varargin)
%FREAD Read binary data from device.
% 
%    A=FREAD(OBJ,SIZE) reads at most the specified number of values, 
%    SIZE, from the device connected to serial port object, OBJ, and 
%    returns to A.  
%
%    FREAD blocks until one of the following occurs:
%        1. SIZE values have been received
%        2. A timeout occurs as specified by the Timeout property
%
%    The serial port object must be connected to the device with the 
%    FOPEN function before any data can be read from the device otherwise
%    an error is returned. A connected serial port object has a Status
%    property value of open.
%
%	 Available options for SIZE include:
%
%       N      read at most N values into a column vector.
%       [M,N]  read at most M * N values filling an M-by-N
%              matrix, in column order.
%
%    SIZE cannot be set to INF. If SIZE is greater than the OBJ's 
%    InputBufferSize property value an error will be returned. Note
%    that SIZE is specified in values while the InputBufferSize is
%    specified in bytes.
%
%    A=FREAD(OBJ,SIZE,'PRECISION') reads binary data with the specified 
%    precision, PRECISION. The precision argument controls the number 
%    of bits read for each value and the interpretation of those bits
%    as character, integer or floating point values. The supported
%    PRECISION strings are defined below. By default the 'uchar' 
%    PRECISION is used. By default, numeric values are returned in 
%    double precision arrays.
%   
%       MATLAB           Description
%       'uchar'          unsigned character,  8 bits.
%       'schar'          signed character,    8 bits.
%       'int8'           integer,             8 bits.
%       'int16'          integer,             16 bits.
%       'int32'          integer,             32 bits.
%       'uint8'          unsigned integer,    8 bits.
%       'uint16'         unsigned integer,    16 bits.
%       'uint32'         unsigned integer,    32 bits.
%       'single'         floating point,      32 bits.
%       'float32'        floating point,      32 bits.
%       'double'         floating point,      64 bits.
%       'float64'        floating point,      64 bits.
%       'char'           character,           8 bits (signed or unsigned).
%       'short'          integer,             16 bits.
%       'int'            integer,             32 bits.
%       'long'           integer,             32 or 64 bits.
%       'ushort'         unsigned integer,    16 bits.
%       'uint'           unsigned integer,    32 bits.
%       'ulong'          unsigned integer,    32 bits or 64 bits.
%       'float'          floating point,      32 bits.
%
%    [A,COUNT]=FREAD(OBJ,...) returns the number of values read to 
%    COUNT.
%
%    [A,COUNT,MSG]=FREAD(OBJ,...) returns a message, MSG, if FREAD 
%    did not complete successfully. If MSG is not specified a warning  
%    is displayed to the command line. 
%
%    The byte order of the device can be specified with OBJ's ByteOrder
%    property. 
%
%    OBJ's ValuesReceived property will be updated by the number of 
%    values read from the device.
% 
%    If OBJ's RecordStatus property is configured to on with the RECORD
%    function, the data received will be recorded in the file specified
%    by OBJ's RecordName property value.
%
%    See also SERIAL/FOPEN, SERIAL/FSCANF, SERIAL/FGETS, SERIAL/FGETL,
%    SERIAL/RECORD.
%

%    MP 7-13-99
%    Copyright 1999-2000 The MathWorks, Inc. 
%    $Revision: 1.4 $  $Date: 2000/08/29 23:47:52 $

% Error checking.
if nargout > 3
   error('Too many output arguments.');
end  

if ~isa(obj, 'instrument')
    error('OBJ must be an instrument object.');
end

if length(obj)>1
    error('OBJ must be a 1-by-1 instrument object.');
end

% Initialize variables.
warnMsg = 'The specified amount of data was not returned within the Timeout period.';

% Parse the input.
switch nargin
case 1
   error('SIZE must be specified.');
case 2
   size = varargin{1};
   precision = 'uchar';
case 3
   [size, precision] = deal(varargin{:});
otherwise
   error('Too many input arguments.');
end

% Error checking.
if ~isa(precision, 'char')
	error('PRECISION must be a string.');
elseif ~isa(size, 'double')
    error('SIZE must be a double.');
elseif size<=0,
    error('SIZE must be greater than 0.');
elseif (any(isinf(size)))
   error('SIZE cannot be set to INF.');
elseif (any(isnan(size)))
   error('SIZE cannot be set to NaN.');
end

% Define the type of data to be read.
switch (precision)
case {'uchar', 'char'}
    type = 5;
    signed = 0;
case {'schar'}
    type = 5;
    signed = 1;
case {'int8'}
    type = 0;
    signed = 1;
case {'int16', 'short'}
    type = 1;
    signed = 1;
case {'int32', 'int', 'long'}
    type = 2;
    signed = 1;
case {'uint8'}
    type = 0;
    signed = 0;
case {'uint16', 'ushort'}
    type = 1;
    signed = 0;
case {'uint32', 'uint', 'ulong'}
    type = 2;
    signed = 0;
case {'single', 'float32', 'float'}
    type = 3;
    signed = 1;
case {'double' ,'float64'}
    type = 4;
    signed = 1;
otherwise
    error('Invalid PRECISION specified. Type ''help serial/fread'' for more information.');
end

% Floor the size.  
% Note: The call to floor must be done after the error checking
% since floor on a string converts the string to its ascii value.
size = floor(size);

% Determine the total number of elements to read.
switch (length(size))
case 1
    totalSize = size;
    size = [size 1];
case 2
    totalSize = size(1)*size(2);
otherwise
    error('Invalid SIZE specified. Type ''help serial/fread'' for more information.');
end

% Call the fread java method.
try
   % Out contains the data and the number of data ready.
   out = fread(igetfield(obj, 'jobject'), totalSize, type, signed);
catch
   error(lasterr);
end   

% Parse the result from the java fread method.
out = cell(out);
[data, numRead, errmsg] = deal(out{:});

% Overwrite generic warning message with the
% error message if one exsits.
if ~isempty(errmsg),
    warnMsg = errmsg;
end

% Store the warning state.
warnState = warning;

% Change the warning state to on if not off.
if ~strcmp(warnState, 'off')
    warning('on');
end

% If the specified number of values were not available - return.
if (numRead ~= totalSize)
    varargout = {double(data(1:numRead)), numRead, warnMsg};
    if (nargout ~= 3)
        % Display warning.
        warning(warnMsg);
    end
    warning(warnState);
    return;
end

% Construct the output.
try	
  	switch precision
	case {'uint8', 'uchar', 'char'}
	    data = double(data);
        data = data + (data<0).*256;
    case {'uint16', 'ushort'}
        data = double(data);
        data = data + (data<0).*65536;
	case {'uint32', 'uint', 'ulong'}
        data = double(data);
        data = data + (data<0).*(2^32);
    case {'int8', 'schar'}
		data = double(data);
        data = data - (data>127)*256;
    otherwise
        data = double(data);
    end
    
    varargout{1} = reshape(data, size(1), size(2));
    
    % Data was successfully read and formatted.  Return the formatted
    % data, the number of values read and any errors that occurred
    % in the java code.
    varargout{2} = numRead;
    varargout{3} = errmsg;
catch
    % An error occurred while reshaping. Return the data as an array.
	varargout{1} = double(data);
    varargout{2} = numRead;
    varargout{3} = warnMsg;
end

% Warn if the MSG output variable is not specified.
if (nargout ~= 3) & ~isempty(varargout{3})
    warning(varargout{3});
end

% Restore the warning state.
warning(warnState);
