function [success,theMessage]=xlswrite(file,data,sheet,range)
% XLSWRITE Write to Microsoft Excel spreadsheet file.
%
%   *** XLSWRITE is not recommended ***
%   See writetable, writematrix, or writecell.
%
%   ----------------------------------------------------
%   XLSWRITE(FILE,ARRAY) writes ARRAY to the first worksheet in the Excel file named
%   FILE, starting at cell A1.
%
%   XLSWRITE(FILE,ARRAY,SHEET) writes to the specified worksheet.
%
%   XLSWRITE(FILE,ARRAY,RANGE) writes to the rectangular region specified by RANGE in
%   the first worksheet of the file. Specify RANGE using the syntax 'C1:C2', where C1
%   and C2 are opposing corners of the region.
%
%   XLSWRITE(FILE,ARRAY,SHEET,RANGE) writes to the specified SHEET and RANGE.
%
%   STATUS = XLSWRITE(FILE,ARRAY,SHEET,RANGE) returns the completion status of the
%   write operation: TRUE (logical 1) for success, FALSE (logical 0) for failure.
%   Inputs SHEET and RANGE are optional.
%
%   [STATUS,MESSAGE] = XLSWRITE(FILE,ARRAY,SHEET,RANGE) returns any warning or error
%   messages generated by the write operation in structure MESSAGE. The structure
%   contains two fields: 'message' and 'identifier'. Inputs SHEET and RANGE are
%   optional.
%
%   Input Arguments:
%
%   FILE    Name of the file to write. If the file does not
%           exist, XLSWRITE creates a file, determining the format based on the
%           specified extension. To create a file compatible with Excel 97-2003
%           software, specify an extension of '.xls'. To create files in Excel 2007
%           or later formats, specify an extension of '.xlsx', '.xlsb', or '.xlsm'.
%           If you do not specify an extension, XLSWRITE applies '.xls'.
%
%   ARRAY   Two-dimensional numeric or character array or, if each cell contains a
%           single element, a cell array.
%
%   SHEET   Worksheet to write. One of the following:
%           * The worksheet name.
%           * Positive, integer-valued scalar indicating the worksheet
%             index.
%           If SHEET does not exist, XLSWRITE adds a new sheet at the end of the
%           worksheet collection. If SHEET is an index larger than the number of
%           worksheets, XLSWRITE appends new sheets until the number of worksheets in
%           the workbook equals SHEET.
%
%   RANGE   Character vector or scalar string that specifies a rectangular portion of
%           the worksheet to write. Not case sensitive. Use Excel A1 reference style.
%           * If you specify a SHEET, RANGE can either fit the size of
%             ARRAY or specify only the first cell (such as 'D2').
%           * If you do not specify a SHEET, RANGE must include both
%             corners and a colon character (:), even for a single cell (such as
%             'D2:D2').
%           * If RANGE is larger than the size of ARRAY, Excel fills the
%             remainder of the region with #N/A. If RANGE is smaller than the size of
%             ARRAY, XLSWRITE writes only the subset that fits into RANGE to the
%             file.
%
%   Notes:
%
%   * If your system does not have Excel for Windows, or if the COM server
%     (part of the typical installation of Excel) is unavailable, XLSWRITE:
%        * Writes ARRAY to a text file in comma-separated value (CSV) format.
%        * Ignores SHEET and RANGE arguments.
%        * Generates an error when ARRAY is a cell array.
%
%   * Excel converts Inf values to 65535. XLSWRITE converts NaN values to
%     empty cells.
%
%   ***********************************
%   *** XLSWRITE is not recommended ***
%   ***********************************
%
%   Compatibility Considerations:
%
%   For writing numeric data, replace the following:
%       XLSWRITE(FILE,ARRAY);
%
%   Instead Use:
%       writematrix(ARRAY,FILE);
%
%   For cell data, replace the following:
%       XLSWRITE(FILE,CELL);
%
%   Instead Use:
%       writecell(CELL,FILE);
% 
%   For specifying sheet and range, replace the following:
%       ... = XLSWRITE(FILE,DATA,SHEET,RANGE);
%   
%   Instead use:
%       ... = WRITE<type>(DATA,FILE,"Sheet",SHEET,"RANGE",RANGE);
%
% See also WRITEMATRIX, WRITECELL, WRITETABLE.
%

%   Copyright 1984-2021 The MathWorks, Inc.

Sheet1 = 1;

if nargin < 3
    sheet = Sheet1;
    range = '';
elseif nargin < 4
    range = '';
end

if nargout > 0
    success = true;
    theMessage = struct('message',{''},'identifier',{''});
end

[sheet,range] = convertStringsToChars(sheet,range);

% Handle input.
try
    % handle requested Excel workbook filename.
    if ~isempty(file)
        file = convertStringsToChars(file);
        if ~ischar(file)
            error(message('MATLAB:xlswrite:InputClassFilename'));
        end
        % check for wildcards in filename
        if any(strfind(file, '*'))
            error(message('MATLAB:xlswrite:FileName'));
        end
        [Directory,file,ext]=fileparts(file);
        if isempty(ext) % add default Excel extension;
            ext = '.xls';
        end
        file = abspath(fullfile(Directory,[file ext]));
        [a1, a2] = fileattrib(file);
        if a1 && ~(a2.UserWrite == 1)
            error(message('MATLAB:xlswrite:FileReadOnly'));
        end
    else % get workbook filename.
        error(message('MATLAB:xlswrite:EmptyFileName'));
    end

    % Check for empty input data
    if isempty(data)
        error(message('MATLAB:xlswrite:EmptyInput'));
    end
    data = convertStringsToChars(data);

    % Check for N-D array input data
    if ndims(data)>2 %#ok<ISMAT>
        error(message('MATLAB:xlswrite:InputDimension'));
    end

    isSupportedType = iscell(data) || isnumeric(data) || ischar(data) || islogical(data);
    isSparseType = issparse(data);
    % Check class of input data
    if ~isSupportedType || isSparseType
        error(message('MATLAB:xlswrite:InputClass'));
    end


    % convert input to cell array of data.
     if iscell(data)
        A=data;
     else
         A=num2cell(data);
     end

    if nargin > 2
        % Verify class of sheet parameter.
        if ~(ischar(sheet) || (isnumeric(sheet) && sheet > 0))
            error(message('MATLAB:xlswrite:InputClassSheetArg'));
        end
        if isempty(sheet)
            sheet = Sheet1;
        end
        % parse REGION into sheet and range.
        % Parse sheet and range strings.
        if ischar(sheet) && ~isempty(strfind(sheet,':'))
            range = sheet; % only range was specified.
            sheet = Sheet1;% Use default sheet.
        elseif ~ischar(range)
            error(message('MATLAB:xlswrite:InputClassRangeArg'));
        end
    end

catch exception
    narginchk(2,4);
    success = false;
    theMessage = exceptionHandler(nargout, exception);
    return;
end
%------------------------------------------------------------------------------
% Attempt to start Excel as ActiveX server.
try
    Excel = matlab.io.internal.getExcelInstance;

catch exception %#ok<NASGU>
    warning(message('MATLAB:xlswrite:NoCOMServer'));
    if nargout > 0
        [theMessage.message,theMessage.identifier] = lastwarn;
    end
    % write data as CSV file, that is, comma delimited.
    file = regexprep(file,'(\.xls[^.]*+)$','.csv'); 
    try
        dlmwrite(file,data,','); % write data.
    catch exception
        exceptionNew = MException('MATLAB:xlswrite:dlmwrite','%s', getString(message('MATLAB:xlswrite:dlmwrite')));
        exceptionNew = exceptionNew.addCause(exception);
        if nargout == 0
            % Throw error.
            throw(exceptionNew);
        else
            success = false;
            theMessage.message = exceptionNew.getReport;
            theMessage.identifier = exceptionNew.identifier;
        end
    end
    return;
end
%------------------------------------------------------------------------------
try
    % Construct range string
    if isempty(strfind(range,':'))
        % Range was partly specified or not at all. Calculate range.
        [m,n] = size(A);
        range = calcrange(range,m,n);
    end
catch exception
    success = false;
    theMessage = exceptionHandler(nargout, exception);
    return;
end

%------------------------------------------------------------------------------
try
    bCreated = ~exist(file,'file');
    ExecuteWrite;
catch exception
    if (bCreated && exist(file, 'file') == 2)
        delete(file);
    end
    success = false;
    theMessage = exceptionHandler(nargout, exception);
end        
    function ExecuteWrite
        if bCreated
            % Create new workbook.  
            %This is in place because in the presence of a Google Desktop
            %Search installation, calling Add, and then SaveAs after adding data,
            %to create a new Excel file, will leave an Excel process hanging.  
            %This workaround prevents it from happening, by creating a blank file,
            %and saving it.  It can then be opened with Open.
            ExcelWorkbook = Excel.workbooks.Add;
            switch ext
                case '.xls' %xlExcel8 or xlWorkbookNormal
                   xlFormat = -4143;
                case '.xlsb' %xlExcel12
                   xlFormat = 50;
                case '.xlsx' %xlOpenXMLWorkbook
                   xlFormat = 51;
                case '.xlsm' %xlOpenXMLWorkbookMacroEnabled 
                   xlFormat = 52;
                otherwise
                   xlFormat = -4143;
            end
            ExcelWorkbook.SaveAs(file, xlFormat);
            ExcelWorkbook.Close(false);
        end

        %Open file
        readOnly = false;
        [~, ExcelWorkbook,workbookState] = openExcelWorkbook(Excel, file, readOnly);
        c = onCleanup(@()xlsCleanup(Excel,file,workbookState));

        if ExcelWorkbook.ReadOnly ~= 0
            %This means the file is probably open in another process.
            error(message('MATLAB:xlswrite:LockedFile', file));
        end
        
        try
            % select region.
            % Activate indicated worksheet.
            [theMessage, TargetSheet, visibility] = activate_sheet(Excel,sheet);
        catch
           error(message('MATLAB:xlswrite:InvalidSheetName', sheet)); 
        end
        
        try
            % Select range in worksheet.
            Select(Range(Excel,sprintf('%s',range)));
        catch
            error(message('MATLAB:xlswrite:SelectDataRange'));
        end

        % Export data to selected region.
        set(Excel.selection,'Value',A);
        
        resetVisibility(TargetSheet, visibility);
        
        ExcelWorkbook.Save      
    end
end
%--------------------------------------------------------------------------
function [theMessage, TargetSheet, visibility] = activate_sheet(Excel,Sheet)
% Activate specified worksheet in workbook.

% Initialize worksheet object
WorkSheets = Excel.sheets;
theMessage = struct('message',{''},'identifier',{''});

% Get name of specified worksheet from workbook
try
    TargetSheet = get(WorkSheets,'item',Sheet);
catch exception  %#ok<NASGU>
    % Worksheet does not exist. Add worksheet.
    TargetSheet = addsheet(WorkSheets,Sheet);
    warning(message('MATLAB:xlswrite:AddSheet'));
    if nargout > 0
        [theMessage.message,theMessage.identifier] = lastwarn;
    end
end

% We temporarily make xlSheetHidden and xlSheetVeryHidden spreadsheets
% xlsheetVisible so we can write to them. Store the visibility for later.
visibility = get(TargetSheet, 'Visible');

% Activate silently fails if the sheet is hidden
set(TargetSheet, 'Visible', 'xlSheetVisible');

% activate worksheet
Activate(TargetSheet);
end
%--------------------------------------------------------------------------
function resetVisibility(TargetSheet, visibility)
% Rehide the sheet after writing if it was hidden at the start
if ismember(visibility, {'xlSheetHidden', 'xlSheetVeryHidden'})  
    set(TargetSheet, 'Visible', visibility);
end
end
%------------------------------------------------------------------------------
function newsheet = addsheet(WorkSheets,Sheet)
% Add new worksheet, Sheet into worksheet collection, WorkSheets.

if isnumeric(Sheet)
    % iteratively add worksheet by index until number of sheets == Sheet.
    while WorkSheets.Count < Sheet
        % find last sheet in worksheet collection
        lastsheet = WorkSheets.Item(WorkSheets.Count);
        newsheet = WorkSheets.Add([],lastsheet);
    end
else
    % add worksheet by name.
    % find last sheet in worksheet collection
    lastsheet = WorkSheets.Item(WorkSheets.Count);
    newsheet = WorkSheets.Add([],lastsheet);
end
% If Sheet is a string, rename new sheet to this string.
if ischar(Sheet)
    set(newsheet,'Name',Sheet);
end
end
%------------------------------------------------------------------------------
function [absolutepath]=abspath(partialpath)

% parse partial path into path parts
[pathname, filename, ext] = fileparts(partialpath);
% no path qualification is present in partial path; assume parent is pwd, except
% when path string starts with '~' or is identical to '~'.
if isempty(pathname) && partialpath(1) ~= '~'
    Directory = pwd;
elseif isempty(regexp(partialpath,'^(.:|\\\\|/|~)','once'));
    % path did not start with any of drive name, UNC path or '~'.
    Directory = [pwd,filesep,pathname];
else
    % path content present in partial path; assume relative to current directory,
    % or absolute.
    Directory = pathname;
end

% construct absolute filename
absolutepath = fullfile(Directory,[filename,ext]);
end
%------------------------------------------------------------------------------
function range = calcrange(range,m,n)
% Calculate full target range, in Excel A1 notation, to include array of size
% m x n

import matlab.io.spreadsheet.internal.columnLetter;
import matlab.io.spreadsheet.internal.columnNumber;

range = upper(range);
cols = isletter(range);
rows = ~cols;
% Construct first row.
if ~any(rows)
    firstrow = 1; % Default row.
else
    firstrow = str2double(range(rows)); % from range input.
end
% Construct first column.
if ~any(cols)
    firstcol = 'A'; % Default column.
else
    firstcol = range(cols); % from range input.
end
try
    lastrow = num2str(firstrow+m-1);   % Construct last row as a string.
    firstrow = num2str(firstrow);      % Convert first row to string image.
    lastcol = columnLetter(columnNumber(firstcol)+n-1); % Construct last column.

    range = [firstcol firstrow ':' lastcol lastrow]; % Final range string.
catch exception
    error(message('MATLAB:xlswrite:CalculateRange', range));
end
end
%-------------------------------------------------------------------------------
function messageStruct = exceptionHandler(nArgs, exception)
    if nArgs == 0
        throwAsCaller(exception);  	   
    else
        messageStruct.message = exception.message;       
        messageStruct.identifier = exception.identifier;
    end
end
