function [tc, results] = ibis_ami_mingw
% ibis_ami_mingw returns the toolchain for "minGW".
% Since MinGW is based on Linux/gcc, you can use GNU/GCC and modify
% relevant items.

%   Copyright 2012-2019 The MathWorks, Inc.

[tc, results] = ibis_ami_gnu_gcc; %SerDes Toolbox

if ~results.error
    % Change the platform, name and version of the toolchain
    tc.Platform = 'win64';
    tc.SupportedVersion = '6.x';
    tc.Name = 'MinGW64 | gmake (64-bit Windows)';
    tc.Alias = {'GNU-x'};
    
    results.tcFile = ['ibis_ami_gnu_gcc_gmake_', tc.Platform, '.mat']; %SerDes Toolbox
    
    % Setup MINGW_ROOT to where MinGW is installed.
    setupFile = 'setup_mingw.bat';
    tc.MATLABSetup{1} = sprintf('mingwSetupFcn(''%s'');', setupFile);
    tc.ShellSetup{1} = ['call "' setupFile '"'];
    
    % ------------------------------
    % Macros
    % ------------------------------
    
    tc.addMacro('MW_EXTERNLIB_DIR',      '$(MATLAB_ROOT)/extern/lib/win64/mingw64');
    tc.addIntrinsicMacros('MINGW_ROOT');
    tc.addIntrinsicMacros('MINGW_C_STANDARD_OPTS');
    % Make sure Windows shell commands are available
    % http://www.gnu.org/software/make/manual/make.html#Choosing-the-Shell
    tc.addMacro('SHELL', '%SystemRoot%/system32/cmd.exe');
    
    tc.InlinedCommands = 'MINGW_C_STANDARD_OPTS = $(C_STANDARD_OPTS)';
    
    % ------------------------------
    % Set up build tools:
    % ------------------------------
    % - Use "gcc" for C and "g++" for C++ (defaults from gnu_gcc)
    % - Change object extension to .obj
    % - Change shared lib extension to .dll
    % - Change static lib extension to .lib
    % - Add -lws2_32 to linker libs to permit building SIL/PIL
    %   (no harm if ws2_32 is not used all the time)
    
    compilerTool = tc.getBuildTool('C Compiler');
    compilerTool.setPath('$(MINGW_ROOT)');
    compilerTool.setFileExtension('Object','.obj');
    
    linkerTool = tc.getBuildTool('Linker');
    linkerTool.setPath('$(MINGW_ROOT)');
    if isempty(linkerTool.Libraries)
        linkerTool.Libraries = {'-lws2_32'};
    elseif iscellstr(linkerTool.Libraries)
        linkerTool.Libraries{end+1} = '-lws2_32';
    end
    linkerTool.setFileExtension('Shared Library','.dll');
    linkerTool.setFileExtension('Executable', '.dll'); %SerDes Toolbox
    
    cppCompilerTool = tc.getBuildTool('C++ Compiler');
    cppCompilerTool.setPath('$(MINGW_ROOT)');
    cppCompilerTool.setFileExtension('Object','.obj');
    
    cppLinkerTool = tc.getBuildTool('C++ Linker');
    cppLinkerTool.setPath('$(MINGW_ROOT)');
    % "-lws2_32" is picked up from the C Linker settings already.
    cppLinkerTool.setFileExtension('Shared Library','.dll');
    cppLinkerTool.setFileExtension('Executable', '.dll'); %SerDes Toolbox
    
    archiverTool = tc.getBuildTool('Archiver');
    archiverTool.setPath('$(MINGW_ROOT)');
    archiverTool.setFileExtension('Static Library','.lib');

    % ------------------------------
    % set up builder:
    % ------------------------------
    % Change shell commands for Windows
    tool = tc.BuilderApplication;
    tool.setDirective( 'DeleteCommand', '@del');
    tool.setDirective( 'MoveCommand', '@move');
    tool.setPath('%MATLAB%/bin/win64');
    
    % ------------------------------
    % Set up build configurations:
    % ------------------------------
    
    % Filter out "-fPIC" from glnxa64 gcc compiler options for minGW for
    % Faster Runs, Faster Builds and Debug.
    compilerOptsToRemove = {' -fPIC'};
    % Add "-m64" flag for GM (g1525481). The MinGW version we supported  
    % generates code for 64-bit environment by default. However, GM uses  
    % MinGW 4.5.1 32-bit compiler, which requires "-m64" to generate 64-bit 
    % code. MEX also passes -m64 flag in MinGW option files.    
    compilerOptsToAdd = {'-m64'};
    
    % Add linker flag "-static" for MinGW 5.3. (The static linking behavior
    % is default for MinGW 4.9) g1554433
    linkerOptsToAdd = {}; %SerDes Toolbox
    
    % Windows needs all symbols referenced in a shared library to be resolved 
    % at link time during linking of an executable.
    % Create an import library with DLL.
    specialMinGWOpts = '-Wl,--out-implib,$(basename $(PRODUCT)).lib';

    % Faster Runs:
    cfg = tc.getBuildConfiguration('Faster Runs');
    cfg.getOption('C++ Linker').setValue({(strrep(cfg.getOption('C++ Linker').getValue{1}, '-shared', '-shared -static'))}); %SerDes Toolbox
    modifyOptions(cfg, 'C Compiler', compilerOptsToRemove, compilerOptsToAdd);
    modifyOptions(cfg, 'C++ Compiler', compilerOptsToRemove, compilerOptsToAdd);
    modifyOptions(cfg, 'Linker', {}, {specialMinGWOpts}); %SerDes Toolbox
    modifyOptions(cfg, 'C++ Linker', {}, {specialMinGWOpts}); %SerDes Toolbox
    % Add shared library linker options
    i_addSharedLinkerOptions(cfg, specialMinGWOpts);

    % Faster Builds:
    cfg = tc.getBuildConfiguration('Faster Builds');
    cfg.getOption('C++ Linker').setValue({(strrep(cfg.getOption('C++ Linker').getValue{1}, '-shared', '-shared -static'))}); %SerDes Toolbox
    modifyOptions(cfg, 'C Compiler', compilerOptsToRemove, compilerOptsToAdd);
    modifyOptions(cfg, 'C++ Compiler', compilerOptsToRemove, compilerOptsToAdd);
    modifyOptions(cfg, 'Linker', {}, {specialMinGWOpts}); %SerDes Toolbox
    modifyOptions(cfg, 'C++ Linker', {}, {specialMinGWOpts}); %SerDes Toolbox
    % Add shared library linker options
    i_addSharedLinkerOptions(cfg, specialMinGWOpts);

    % Debug:
    cfg = tc.getBuildConfiguration('Debug');
    cfg.getOption('C++ Linker').setValue({(strrep(cfg.getOption('C++ Linker').getValue{1}, '-shared', '-shared -static'))}); %SerDes Toolbox
    modifyOptions(cfg, 'C Compiler', compilerOptsToRemove, compilerOptsToAdd);
    modifyOptions(cfg, 'C++ Compiler', compilerOptsToRemove, compilerOptsToAdd);
    modifyOptions(cfg, 'Linker', {}, {specialMinGWOpts}); %SerDes Toolbox
    modifyOptions(cfg, 'C++ Linker', {}, {specialMinGWOpts}); %SerDes Toolbox
    % Add shared library linker options
    i_addSharedLinkerOptions(cfg, specialMinGWOpts);
    
    % ------------------------------
    % OpenMP
    % ------------------------------
    % remove OpenMP support from gnu_gcc.m
    tc.removeCustomBuildConfiguration('OpenMP'); 
    % add OpenMP support for MinGW
    openMPCFlags = '-fopenmp';
    openMPLFlags = '-fopenmp';
    tc.addCustomBuildConfiguration('OpenMP', ...
        'C Compiler',   openMPCFlags, 'Linker',     openMPLFlags, ...
        'C++ Compiler', openMPCFlags, 'C++ Linker', openMPLFlags); 
    
    % ------------------------------
    % Avx2
    % ------------------------------
    avxFlag = '-mavx2 -mfma';
    tc.addCustomBuildConfiguration('AVX2', ...
        'C Compiler',   avxFlag, ...
        'C++ Compiler', avxFlag); 
    
    % In Windows, it is better to use a command file to call the Makefile.
    tc.addAttribute('RequiresCommandFile');
    tc.addAttribute('RequiresBatchFile');
    tc.addAttribute('UseLibPrefixForSystemLibrariesWithLibExtension');
    tc.addAttribute('AlwaysToolchainInfoCompliant');

    % The Windows command line length is limited and with
    % relative paths the limit applies to the relative path
    % prepended with path to the current working folder. As
    % mitigation, use the absolute path.
    tc.addAttribute('PreferAbsolutePaths');
    
    results.tc = tc;
    
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Local Functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function modifyOptions(buildCfg, buildTool, optionsToRemove, optionsToAdd)
% This function removes the compiler/linker options in optionsToRemove, 
% adds the options in optionsToAdd, and also changes the macro from
% $(C_STANDARD_OPTS) to $(MINGW_C_STANDARD_OPTS) if "$(C_STANDARD_OPTS)"
% exists in the options.
%Inputs:
% optionsToRemove: a cell array containing strings.
% optionsToAdd: a cell array containing strings.

    value = buildCfg.getOption(buildTool).getValue;
    
    %remove the options in optionsToRemove
    for n=1:numel(optionsToRemove)
        value = regexprep(value, optionsToRemove{n}, '');
    end
    
    %add the options in optionsToAdd
    for n=1:numel(optionsToAdd)
        value{1} = [value{1}, ' ', optionsToAdd{n}];
    end

    %change the macro from $(C_STANDARD_OPTS) to $(MINGW_C_STANDARD_OPTS) g1470655
    value = strrep(value, '$(C_STANDARD_OPTS)', '$(MINGW_C_STANDARD_OPTS)');
    
    buildCfg.setOption(buildTool, value); 

end

function i_addSharedLinkerOptions(cfg, specialMinGWOpts)
    sharedLinkerOpts = cfg.getOption('Shared Library Linker');
    cfg.setOption('Shared Library Linker', horzcat(sharedLinkerOpts.getValue, specialMinGWOpts));
    CppSharedLinkerOpts = cfg.getOption('C++ Shared Library Linker');
    cfg.setOption('C++ Shared Library Linker', horzcat(CppSharedLinkerOpts.getValue, specialMinGWOpts));
end
