function writestruct(S, filename, varargin)
%WRITESTRUCT Write a struct to a file.
%
%   WRITESTRUCT(S, FILENAME) writes the struct S to the file FILENAME.
%
%   S must be a scalar struct containing fields with scalar or vector data.
%   The data in each field must be one of the following datatypes: string,
%   datetime, duration, categorical, logical, struct, or any numeric datatype.
%
%   FILENAME can be one of these:
%
%       - For local files, FILENAME can contain an absolute file name with a
%         file extension. FILENAME can also be a relative path to the current
%         folder, or to a folder on the MATLAB path.
%         For example, to export to a file in the current folder:
%
%            writestruct(S, "microscopy.xml");
%
%       - For remote files, FILENAME must be a full path specified as a
%         uniform resource locator (URL). For example, to export a remote
%         file to Amazon S3, specify the full URL for the file:
%
%            writestruct(S, "s3://bucketname/path_to_file/my_setup.xml");
%
%         For more information on accessing remote data, see "Work with
%         Remote Data" in the documentation.
%
%   WRITESTRUCT(S, FILENAME, "FileType", FILETYPE) specifies the file type.
%
%       The default value for FILETYPE is "auto", which makes WRITESTRUCT
%       detect the output file type from the extension of the supplied file name.
%
%       FILETYPE should be manually specified when the file extension and the
%       type of the file can differ.
%       For example, specify FILETYPE to write an XML file with a ".kml"
%       extension:
%
%           writestruct(S, "data.kml", "FileType", "xml");
%
%   Name-Value Pairs:
%   --------------------------------------------------------
%
%   "FileType"        - Specifies the file type. Supported values include:
%                       "auto" - detect the file type to write based on the
%                                extension of the file name
%                       "xml"  - export as an XML file
%
%   "StructNodeName"  - This will be the name of the root node of the XML
%                       file written. For instance, consider the following
%                       struct, S:
%
%                          S.Product(1) = struct("Name", "Widget1", "Price", 2);
%                          S.Product(2) = struct("Name", "Widget2", "Price", 5);
%                          writestruct(S, "products.xml", "StructNodeName", "Products");
%
%                       If "StructNodeName" is specified as "Product", the XML
%                       written will have the following contents:
%
%                          <?xml version="1.0" encoding="UTF-8"?>
%                          <Products>
%                              <Product>
%                                  <Name>Widget1</Name>
%                                  <Price>2</Price>
%                              </Product>
%                              <Product>
%                                  <Name>Widget2</Name>
%                                  <Price>5</Price>
%                              </Product>
%                          </Products>
%
%                       If not specified, "StructNodeName" defaults to "struct".
%
%   "AttributeSuffix" - Suffix indicating fields to write out as XML
%                       attributes. All fields with the specified suffix
%                       will be written out as XML attributes, excluding
%                       the suffix in the resulting attribute name. This is
%                       particularly helpful for round-trip I/O workflows.
%                       For instance, if a struct contains a field named
%                       "PriceAttribute", via AttributeSuffix, this field
%                       can be written out to the XML file as "Price" by
%                       specifying the value of AttributeSuffix as
%                       "Attribute". The default value of "AttributeSuffix"
%                       is "Attribute".
%
%   See also READSTRUCT, STRUCT, WRITETABLE, WRITEMATRIX, WRITECELL

% Copyright 2019-2020 The MathWorks, Inc.

    import matlab.io.xml.internal.write.*;

    try
        % 1. Input parsing and validity checking.
        nvStruct = parseWriteStructNVPairs(varargin{:});

        % validate order of required arguments
        validateRequiredArgumentOrder(S, filename, "struct");

        S = validateStruct(S, nvStruct.AttributeSuffix);
        filename = validateFilename(filename);

        % Error out if we can't detect the FileType from the file extension (when "auto").
        fileType = detectFileType(filename, nvStruct.FileType);

        % 2. Call into C++ with the input struct, filename, and options.
        if fileType == "xml"
            import matlab.io.xml.internal.write.builtin.writestruct;
            writestruct(S, filename, nvStruct);
        end
    catch ME
        throwAsCaller(ME)
    end
end
