classdef (Hidden = true) FileWriter < rtw.connectivity.Writer
%FILEWRITER This is an output writer which prints all its output to a file.
%
%   Internally, this class uses the FOPEN, FWRITE and FCLOSE functions.
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2011-2021 The MathWorks, Inc.
    
    
    properties(Access = private)
        FileId = -1;
        FileName;
        CallCBeautifier;
        ObfuscateCode;
        IsBuffered = false;
        FileBuffer = '';
    end
    
    
    methods % Constructor
        
        function this = FileWriter(fileName, append, callCBeautifier, obfuscateCode, varargin)
        %FILEWRITER Creates a new instance of the file writer. This class
        %uses `fopen`, `fwrite`, and `fclose` in its implementation.
        %
        %   fileName - the name of the file to write to.
        %
        %   append - a logical flag that indicates whether to append to the
        %   file or to truncate it.
            
            narginchk(4,6);
            
            encoding = '';
            machineFormat = 'n';
            
            if nargin > 4
                machineFormat = varargin{1};
            end
            if nargin>5
                encoding = varargin{2};
            end
            % The filename must be a character string.
            assert(ischar(fileName), 'The file name is not a valid string.');
            
            % Also check that the append and callCBeautifier flags are logical
            assert(islogical(append), 'The "append" parameter must be a logical value.');
            assert(islogical(callCBeautifier), 'The "callCBeautifier" parameter must be a logical value.');
            assert(islogical(obfuscateCode), 'The "obfuscateCode" parameter must be a logical value.');
            
            % store the filename and callCBeautifier
            this.FileName = fileName;
            this.CallCBeautifier = callCBeautifier;
            this.ObfuscateCode = obfuscateCode;
            
            if coder.internal.connectivity.featureOn('BufferedFileWriter')
                this.IsBuffered = true;
            end
            
            % Now finally open the file.
            if append
                this.FileId = fopen(fileName, 'a', machineFormat, encoding);
            else
                this.FileId = fopen(fileName, 'w', machineFormat, encoding);
            end
            
            assert(isnumeric(this.FileId) && this.FileId >= 0, ...
                   'Could not open the file "%s" for writing.', ...
                   fileName);
        end
        
    end
    
    
    methods % The Writer interface
        
        function write(this, str)
            %fprintf ensures native encoding is honored when writing out
            %the code.
            if this.IsBuffered
                this.FileBuffer = sprintf('%s%s', this.FileBuffer, str);
            else
                fprintf(this.FileId, '%s', str);
            end
        end
        
        function writeBinary(this, data)
            %write binary data to the file.
            this.flush();
            fwrite(this.FileId, data);
        end        
        
        function close(this)
            if this.FileId >= 0
                this.flush();
                fclose(this.FileId);
                this.FileId = -1;
                
                % beautify the code if requested
                if this.CallCBeautifier && ~this.ObfuscateCode
                    c_beautifier(this.FileName);
                end
                
                % obfuscate the code if requested
                if this.ObfuscateCode
                    % we must be in the same directory as the source file
                    % for the doObfuscation API to work
                    srcDir = fileparts(this.FileName);
                    origDir = cd(srcDir);
                    restoreDir = onCleanup(@()cd(origDir));
                    modelName = '';
                    rtwprivate('doObfuscation', modelName, 1, true);
                    % delete the constant header file to inform doObfuscation
                    % to continue to obfuscate the next set of files in this 
                    % directory
                    if exist(fullfile(srcDir, '__cf_.h'),'file')
                        delete(fullfile(srcDir, '__cf_.h'));
                    end
                    restoreDir.delete;
                end
            end
        end
        
        
        function delete(this)
            this.close();
        end
        
    end
    methods (Access='private')
        
        function flush(this)
            if this.IsBuffered
                fprintf(this.FileId, '%s', this.FileBuffer);
                this.FileBuffer = '';
            end
        end
    end
      
end
