% This script reads the an Excel file containing the parameters for an
% Output module and writes some Fortran code containing appropriate
% parameters (which can be imported into the output module).
 
% Users will need to modify the XLS_file variable to point to the location
% of the OutListParameters.xlsx file.
% The FAST Simulation Toolbox must also be in the user's working directory
% (for the function GetOutListParameters)
%..........................................................................

clear all

% --- Parameters
% ModuleName = input('Enter the module for which the output module will be generated (ElastoDyn, ServoDyn, etc.): ','s');

ModuleName = 'AeroDyn'; % e.g. ElastoDyn, ServoDyn, AeroDyn, FAST_Farm

OFDir    = 'C:/Work/openfast/'; % Directory containing OpenFAST repository (to find Excel file, and output FORTRAN files)
addErrChk = false;
maxContLines = 254; % maximum number of continuation lines: must be less than 511 (Intel has 511 max; f95 standard is 255)

% --- Derived parameters
XLS_file = [OFDir 'docs/OtherSupporting/OutListParameters.xlsx'];
OutDir   = [OFDir 'modules/' lower(ModuleName) '/src/']; % assumed output directory (can be overwritten in FAST.Farm case, below)
OutListSheet = ModuleName;

if containString(ModuleName,'_Nodes')
    ModuleName = strrep(ModuleName,'_Nodes','');
    
    PrefixStr1= 'BldNdOuts_';
    PrefixStr2= 'BldNd_';
    NodalOutputs=true;
    StartIndx='1';
else
    PrefixStr1='';
    PrefixStr2='';
    NodalOutputs=false;
    StartIndx='0';
end


switch ModuleName
    case 'ElastoDyn'
        ModName = 'ED';
    case 'ServoDyn'
        ModName = 'SrvD';
        addErrChk = true;  %there is only one check
    case 'InflowWind'
        ModName = 'InflowWind';
    case 'AeroDyn'
        ModName = 'AD';
    case 'BeamDyn'
        ModName = 'BD';
    case 'HydroDyn'
        ModName = 'HydroDyn';
    case 'SubDyn'
        ModName = 'SD';
    case 'Morison'
        ModName = 'Morison';
    case 'SeaState'
        ModName = 'SeaSt';
    case {'FAST_Farm', 'FAST.Farm'}
        ModName = 'Farm';
        OutDir   = [OFDir 'glue-codes/fast-farm/src/'];
    otherwise
        error( 'Invalid module name.');        
end

      
out_file = [OutDir OutListSheet '_IO_Params2.f90'];
mod_file = [OutDir OutListSheet '_IO_Params1.f90'];


%..................


StrName  = 'ChanLen'; %ChanLen is defined in the NWTC Library
StrNameM = 'OutStrLenM1';

if strcmp(ModName,'SD')
    [Category, VarName, InvalidCriteria, ValidInputStr, ValidInputStr_VarName, ValidInputStr_Units, SubCategories ] = GetSubDynOutListParameters();
    CommentLine = 'These parameters are in the order stored in "GetSubDynOutListParameters.m"';
    UseSubCategories = true;
else
    fprintf('Reading: %s, sheet: %s\n', XLS_file, OutListSheet);
    [Category, VarName, InvalidCriteria, ValidInputStr, ValidInputStr_VarName, ValidInputStr_Units ] = GetOutListParameters( XLS_file, OutListSheet );
    CommentLine = 'These parameters are in the order stored in "OutListParameters.xlsx"';
    UseSubCategories = false;
end

%% The list of output names that are valid in the Module input file

[SORTedNames, IX] = sort( upper( ValidInputStr ) );      %just in case it's not upper case or sorted (but make sure you store the indices!) SORTedNames = SORTName(IX);
SORTedNames       = char( SORTedNames );                 %stored as a string array (with padding for FORTRAN)
Sorted_Units      = char( ValidInputStr_Units(IX) );     %stored as a string array (with padding for FORTRAN)

[nr, CLen_Var] = size(SORTedNames);
[nr2,CLen_Unit] = size(Sorted_Units);

numPerR  = floor(100 / (3+max(CLen_Var,CLen_Unit))); %7;       %number of parameters per row of code

fprintf( 'Channels are %2.0f characters long.\n', max(CLen_Var+1,CLen_Unit) );
fprintf( 'There are %2.0f channels per line.\n', numPerR );

% .........................................................................
%% write the "VarName"s parameters to the module that defines them
% .........................................................................
[~,tmpFileName,tmpExt] = fileparts(XLS_file);
tmpFileName = [tmpFileName tmpExt];

fprintf('Writing: %s\n',mod_file);
fout = fopen( mod_file, 'wt' );
fprintf( fout, '%s\n',      '! ===================================================================================================' );
fprintf( fout, '%s\n',      '! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m"' );
fprintf( fout, '%s%s%s\n',  '!      using the parameters listed in the "',tmpFileName,'" Excel file. Any changes to these' );
fprintf( fout, '%s\n',      '!      lines should be modified in the Matlab script and/or Excel worksheet as necessary.');
fprintf( fout, '%s\n',      '! ===================================================================================================' );
fprintf( fout, '%s\n',     ['! This code was generated by "', mfilename, '.m".'] );
fprintf( fout, 'MODULE %s_IO_Params\n\n', ModuleName);
fprintf( fout, '   USE NWTC_Library\n');
fprintf( fout, '   USE %s_Types\n\n',  ModuleName);
fprintf( fout, '   IMPLICIT NONE\n\n');

% fprintf( fout, '\n\n%s\n\n',  '     ! Parameters related to output length (number of characters allowed in the output data headers):' );
tmpLen = max(length(StrNameM),length(StrName));
% numFmt =  ['   INTEGER(IntKi), PARAMETER      :: %' num2str(tmpLen) 's = '];                                                                
% fprintf( fout, [numFmt '%s\n\n'],   [StrNameM, repmat(' ',1,tmpLen-length(StrNameM))], [StrName ' - 1'] );        
fprintf( fout, '\n%s\n',      '     ! Indices for computing output channels:' );
fprintf( fout, '%s\n',        '     ! NOTES:');
fprintf( fout, '%s%s\n',      '     !    (1) ', CommentLine  );
fprintf( fout, '%s\n',        '     !    (2) Array AllOuts() must be dimensioned to the value of the largest output parameter' );
if strcmpi(ModuleName,'FAST')
fprintf( fout, '%s\n',        '     !    (3) If an index (MaxOutPts) ever becomes greater or equal to 1000, the logic to create ARRAY/1 in the FAST-to-ADAMS preprocessor will have to be changed.' );
end

CLen = max(CLen_Var+length(PrefixStr2),length('MaxOutPts'));
numFmtLen = num2str( floor(log10(length(VarName))+1) );
numFmt    =                  ['   INTEGER(IntKi), PARAMETER      :: %' num2str(CLen) 's = %' numFmtLen '.0f\n'];
numFmtN   =                  ['   INTEGER(IntKi), PARAMETER, PUBLIC  :: %' num2str(CLen) 's = %' numFmtLen '.0f\n'];
ParamNum  = 0;

if (~NodalOutputs)
    fprintf( fout, '\n%s\n\n',    '     !  Time:' );
    fprintf( fout, numFmt,       ['Time' repmat(' ',1,CLen-4)], ParamNum );  %Time is parameter 0; we left justify the character string
end

for i=1:length(VarName)
    if ischar( VarName{i} ) && ~isempty(VarName{i}) % print the parameter
        ParamNum = ParamNum + 1;
        fprintf( fout, numFmt,  [PrefixStr2 VarName{i}, repmat(' ',1,CLen-length(VarName{i})-length(PrefixStr2))], ParamNum );        
    else
        fprintf( fout, '\n\n%s%s:\n\n',  '     ! ', Category{i});  %make a comment describing the category
    end
end

fprintf( fout, '\n\n%s%s\n',  '     ! ', 'The maximum number of output channels which can be output by the code.');  
if NodalOutputs
    fprintf( fout, numFmtN, [PrefixStr2 'MaxOutPts', repmat(' ',1,CLen-length('MaxOutPts'))], ParamNum );
else
    fprintf( fout, numFmt,  [PrefixStr2 'MaxOutPts', repmat(' ',1,CLen-length('MaxOutPts'))], ParamNum );   
end
% fprintf( fout, '\n\n%s\n\n',  '     ! Regular Variables:' );
% fprintf( fout, ['%s%' numFmtLen '.0f%s\n'],  '   REAL(ReKi)                     :: AllOuts  (0:', ...
%                    ParamNum, ')                               ! An array holding the value of all of the calculated (not selected) output channels.');
% fprintf( fout, ['%s'               '%s\n'],  '   TYPE(OutParmType), ALLOCATABLE :: OutParam (:) ', ...
%                              '                                 ! An array holding names, units, and indices of all of the selected output channels.');
%

if UseSubCategories
    fprintf( fout, '\n\n' );
    for i=1:length(SubCategories)

        nPerRow2 = SubCategories{i}.Sizes(1) * 2;
        if nPerRow2 <= 6; nPerRow2 = nPerRow2 * 2; end

        nlines = ceil(length(SubCategories{i}.VarName)/nPerRow2); % number of lines
        nGroup = 1; %max(1,ceil(nlines / maxContLines)); % number of groups
        groupSize = length(SubCategories{i}.VarName); % maxContLines*nPerRow2; %ceil(nr/nGroup); % channels per group

        for iGroup = 1:nGroup
            iStart = 1;
            iEnd = length(SubCategories{i}.VarName);
                    
            fprintf( fout, '%s\n', ...
             strcat('   INTEGER(IntKi), PARAMETER :: ', SubCategories{i}.Name, '(', num2str(SubCategories{i}.Sizes(1)), ',', ...
                                           num2str(SubCategories{i}.Sizes(2)), ',', num2str(SubCategories{i}.Sizes(3)), ') = reshape((/ & ') );                                 

            indent = '                ';
            for iRow = iStart:nPerRow2:iEnd
                fprintf( fout, '%s', indent );  %the indent for each line
                lastRow = min(iRow+nPerRow2-1,iEnd);
                ContLine = true;
                for iNum = iRow:lastRow
                    fprintf( fout, ['%' num2str(CLen_Var) 's'], num2str(SubCategories{i}.VarName{iNum}) );
                    if iNum < iEnd
                        fprintf( fout, '%s', ',' );
                    else
                        fprintf( fout, '%s\n', strcat('/),(/', num2str(SubCategories{i}.Sizes(1)), ',', num2str(SubCategories{i}.Sizes(2)), ',', num2str(SubCategories{i}.Sizes(3)), '/))')); %end of array
                        ContLine = false;
                    end
                end
        
                if ContLine
                    fprintf( fout, '%s\n', ' &');
                end
        
            end %iRow
        end %iGroup
        
        fprintf(fout,'\n\n');
        

    end
end


fprintf( fout, '\n%s%s\n', '!End of code generated by Matlab script ', mfilename);
fprintf( fout, '%s\n',      '! ===================================================================================================' );
fclose(fout);
%%


% .........................................................................
%% Open the file for subroutine that checks if the input OutList contains
% valid entries
% .........................................................................
fprintf('Writing: %s\n',out_file);
fout = fopen( out_file, 'wt' );
fprintf( fout, '%s\n', ...
'!**********************************************************************************************************************************', ... 
'! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m"', ...
'!      using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these ' , ...
'!      lines should be modified in the Matlab script and/or Excel worksheet as necessary. ', ...
'!----------------------------------------------------------------------------------------------------------------------------------',...
'!> This routine checks to see if any requested output channel names (stored in the OutList(:)) are invalid. It returns a ',...
'!! warning if any of the channels are not available outputs from the module.',...
'!!  It assigns the settings for OutParam(:) (i.e, the index, name, and units of the output channels, WriteOutput(:)).',...
'!!  the sign is set to 0 if the channel is invalid.',...
'!! It sets assumes the value p%NumOuts has been set before this routine has been called, and it sets the values of p%OutParam here.',...
'!! ',...
['!! This routine was generated by Write_ChckOutLst.m using the parameters listed in OutListParameters.xlsx.'] );
if strcmp(ModName,'AD')
    fprintf( fout, '%s\n', ...
    ['SUBROUTINE ' PrefixStr1, 'SetOutParam(' PrefixStr2 'OutList, p, p_' ModName ', ErrStat, ErrMsg )'] );
else
    fprintf( fout, '%s\n', ...
    ['SUBROUTINE ' PrefixStr1, 'SetOutParam(' PrefixStr2 'OutList, p, ErrStat, ErrMsg )'] );
end
fprintf( fout, '%s\n', ...
'!..................................................................................................................................',...
'   '   ,...
'   IMPLICIT                        NONE',...
'   ',...
'      ! Passed variables',...
'   '      ,...
['   CHARACTER(' StrName '),        INTENT(IN)     :: ' PrefixStr2 'OutList(:)                        !< The list of user-requested outputs'] );
if strcmp(ModName,'AD')
    fprintf( fout, '%s\n', ...
    '   TYPE(RotParameterType),    INTENT(INOUT)  :: p                                 !< The module parameters',...
    ['   TYPE(' ModName '_ParameterType),    INTENT(INOUT)  :: p_' ModName '                              !< The module parameters'] );
else
    fprintf( fout, '%s\n', ...
    ['   TYPE(' ModName '_ParameterType),    INTENT(INOUT)  :: p                                 !< The module parameters'] );
end
fprintf( fout, '%s\n', ...
'   INTEGER(IntKi),            INTENT(OUT)    :: ErrStat                           !< The error status code',...
'   CHARACTER(*),              INTENT(OUT)    :: ErrMsg                            !< The error message, if an error occurred',...
'   '    ,...
'      ! Local variables',...
'   ',...
'   INTEGER                      :: ErrStat2                                        ! temporary (local) error status',...
'   INTEGER                      :: I                                               ! Generic loop-counting index',...
'   INTEGER                      :: J                                               ! Generic loop-counting index',...
'   INTEGER                      :: INDX                                            ! Index for valid arrays');
% '   '  );
% if (~NodalOutputs)
%     fprintf( fout, '%s\n', ...
% '   LOGICAL                      :: CheckOutListAgain                               ! Flag used to determine if output parameter starting with "M" is valid (or the negative of another parameter)');
% end
fprintf( fout, '%s\n', ...
['   LOGICAL                      :: InvalidOutput(' StartIndx ':' PrefixStr2 'MaxOutPts)                      ! This array determines if the output channel is valid for this configuration'],...
['   CHARACTER(*), PARAMETER      :: RoutineName = "' PrefixStr1 'SetOutParam"'],...
'   '   );


nlines = ceil(nr/numPerR); % number of lines
nGroup = max(1,ceil(nlines / maxContLines)); % number of groups
groupSize = ceil(nr/nGroup); % channels per group


for iGroup = 1:nGroup
    iStart = (iGroup-1)*groupSize+1;
    iEnd = min(nr, iGroup*groupSize);

    nGroupSize = iEnd - iStart + 1;

    if nGroup > 1
        iGroupStr = num2str(iGroup);
    else
        iGroupStr = "";
    end
    
    fprintf( fout, '%s%s%s%s%s%s%s\n', ...
        '   CHARACTER(',StrNameM, '), PARAMETER  :: ValidParamAry', iGroupStr, '(', num2str(nGroupSize), ') =  (/  &   ! This lists the names of the allowed parameters, which must be sorted alphabetically' );                                 

    for iRow = iStart:numPerR:iEnd
        fprintf( fout, '%s', '                               ' );  %the indent for each line
        lastRow = min(iRow+numPerR-1,iEnd);
        ContLine = true;
        for iNum = iRow:lastRow
            fprintf( fout, ['"%' num2str(CLen_Var) 's"'], SORTedNames(iNum,:) );
            if iNum < iEnd
                fprintf( fout, '%s', ',' );
            else
                fprintf( fout, '%s\n', '/)'); %end of array
                ContLine = false;
            end
        end

        if ContLine
            fprintf( fout, '%s\n', ' &');
        end

    end %iRow
end %iGroup

if nGroup > 1
    fprintf( fout, '%s%s%s%s%s\n', ...
            '   CHARACTER(',StrNameM, '), PARAMETER  :: ValidParamAry(', num2str(nr), ') =  (/  &   ! This lists the names of the allowed parameters, which must be sorted alphabetically' );                                 
    fprintf( fout, '%s', '                ValidParamAry1' );
    for iGroup = 2:nGroup
        fprintf(fout, ', ValidParamAry%s', num2str(iGroup));
    end %iGroup
    fprintf( fout, '%s\n\n', '/)' );
end

%% The list of parameter names corresponding to the entries in ValidParamAry
Sorted_OutInd = ValidInputStr_VarName(IX);

for iGroup = 1:nGroup
    iStart = (iGroup-1)*groupSize+1;
    iEnd = min(nr, iGroup*groupSize);

    nGroupSize = iEnd - iStart + 1;
    if nGroup > 1
        iGroupStr = num2str(iGroup);
    else
        iGroupStr = "";
    end

    fprintf( fout, '%s%s%s%s%s\n', ...
     '   INTEGER(IntKi), PARAMETER :: ParamIndxAry', iGroupStr, '(', num2str(nGroupSize), ') =  (/ &                            ! This lists the index into AllOuts(:) of the allowed parameters ValidParamAry(:)' );                                 

    for iRow = iStart:numPerR:iEnd
        fprintf( fout, '%s', '                               ' );  %the indent for each line
        lastRow = min(iRow+numPerR-1,iEnd);
        ContLine = true;
        for iNum = iRow:lastRow
            fprintf( fout, [' %' num2str(CLen_Var+length(PrefixStr2)) 's '], [PrefixStr2 Sorted_OutInd{iNum}] );
            if iNum < iEnd
                fprintf( fout, '%s', ',' );
            else
                fprintf( fout, '%s\n', '/)'); %end of array
                ContLine = false;
            end
        end

        if ContLine
            fprintf( fout, '%s\n', ' &');
        end

    end %iRow
end %iGroup

if nGroup > 1
    fprintf( fout, '%s%s%s\n', ...
            '   INTEGER(IntKi), PARAMETER :: ParamIndxAry(', num2str(nr), ') =  (/  &   ! This lists the index into AllOuts(:) of the allowed parameters ValidParamAry(:)' );                                 
    fprintf( fout, '%s', '                ParamIndxAry1' );
    for iGroup = 2:nGroup
        fprintf(fout, ', ParamIndxAry%s', num2str(iGroup));
    end %iGroup
    fprintf( fout, '%s\n\n', '/)' );
end


%% The units corresponding to the entries in ValidParamAry

% note: these units actually correspond to the unique entries in AllOuts(:),
% but i think it will be easier to implement using this array corresponding
% to ValidParamAry(:) entries

for iGroup = 1:nGroup
    iStart = (iGroup-1)*groupSize+1;
    iEnd = min(nr, iGroup*groupSize);
    
    nGroupSize = iEnd - iStart + 1;
    if nGroup > 1
        iGroupStr = num2str(iGroup);
    else
        iGroupStr = "";
    end

    fprintf( fout, '%s%s%s%s%s%s%s\n', ...
     '   CHARACTER(',StrName, '), PARAMETER :: ParamUnitsAry', iGroupStr, '(', num2str(nGroupSize), ') =  (/ character(ChanLen) :: &  ! This lists the units corresponding to the allowed parameters' );                                 

    for iRow = iStart:numPerR:iEnd
        fprintf( fout, '%s', '                               ' );  %the indent for each line
        lastRow = min(iRow+numPerR-1,iEnd);
        ContLine = true;
        for iNum = iRow:lastRow
            fprintf( fout, ['"%' num2str(CLen_Unit) 's"'], Sorted_Units(iNum,:) );
            if iNum < iEnd
                fprintf( fout, '%s', ',' );
            else
                fprintf( fout, '%s\n', '/)'); %end of array
                ContLine = false;
            end
        end

        if ContLine
            fprintf( fout, '%s\n', ' &');
        end

    end %iRow
end %iGroup

if nGroup > 1
    fprintf( fout, '%s%s%s%s%s\n', ...
            '   CHARACTER(',StrName, '), PARAMETER  :: ParamUnitsAry(', num2str(nr), ') =  (/  &   ! This lists the units corresponding to the allowed parameters' );                                 
    fprintf( fout, '%s', '                ParamUnitsAry1' );
    for iGroup = 2:nGroup
        fprintf(fout, ', ParamUnitsAry%s', num2str(iGroup));
    end %iGroup
    fprintf( fout, '%s\n\n', '/)' );
end

%% add the subroutine initializations
fprintf( fout, '%s\n', ...
'',...
'',...
'      ! Initialize values',...
'   ErrStat = ErrID_None',...
'   ErrMsg = ""',...
'   InvalidOutput = .FALSE.',...
'' );

%% Determine if the entry is valid
if ( addErrChk )
        % remove the blank lines (where new categories start)
    nu = length(VarName);

    fprintf( fout, '%s\n', ...
        '', ...
        '      ! Determine which inputs are not valid',...
        '' ); 
    
    for iRow = 1:nu
        if ischar(VarName{iRow}) && ischar(InvalidCriteria{iRow}) && ~isempty(InvalidCriteria{iRow})
            fprintf( fout, ['%s%' num2str(CLen_Var) 's%s%s%s\n'], ...
               '   InvalidOutput(', VarName{iRow}, ') = ( ', InvalidCriteria{iRow}, ' )' );
        end
    end

    fprintf( fout, '%s\n', '' );
    
else
    fprintf( fout, '%s\n', ...
        '', ...
        '!   ..... Developer must add checking for invalid inputs here: .....',...
        '', ...
        '!   ................. End of validity checking .................',...
        '' );    
end 

%% add the last part of the subroutine
fprintf( fout, '%s\n', ...
'',...
'   !-------------------------------------------------------------------------------------------------',...
'   ! Allocate and set index, name, and units for the output channels',...
'   ! If a selected output channel is not available in this module, set error flag.',...
'   !-------------------------------------------------------------------------------------------------',...
''  ,...
['   ALLOCATE ( p%' PrefixStr2 'OutParam(' StartIndx ':p%' PrefixStr2 'NumOuts) , STAT=ErrStat2 )'],...
'   IF ( ErrStat2 /= 0_IntKi )  THEN',...
['      CALL SetErrStat( ErrID_Fatal,"Error allocating memory for the ' ModuleName ' ' PrefixStr2 'OutParam array.", ErrStat, ErrMsg, RoutineName )'],...
'      RETURN',...
'   ENDIF',...
''   );

if (~NodalOutputs) %and strcmp(StartIndx,'0')
fprintf( fout, '%s\n', ...
'      ! Set index, name, and units for the time output channel:',...
''   ,...
'   p%OutParam(0)%Indx  = Time',...
'   p%OutParam(0)%Name  = "Time"    ! OutParam(0) is the time channel by default.',...
'   p%OutParam(0)%Units = "(s)"',...
'   p%OutParam(0)%SignM = 1',...
''   );
end

fprintf( fout, '%s\n', ...
''   ,...
'      ! Set index, name, and units for all of the output channels.',...
'      ! If a selected output channel is not available by this module set ErrStat = ErrID_Warn.',...
''   ,...
['   DO I = 1,p%' PrefixStr2 'NumOuts'],...
''   ,...
['      p%' PrefixStr2 'OutParam(I)%Name  = ' PrefixStr2 'OutList(I)'] );

fprintf( fout, '%s\n', ...
''   ,...
['      Indx = FindValidChannelIndx(' PrefixStr2 'OutList(I), ValidParamAry, p%' PrefixStr2 'OutParam(I)%SignM)'], ...
''      ); 

if (NodalOutputs) %and strcmp(StartIndx,'0')
fprintf( fout, '%s\n', ...
['      if (p%' PrefixStr2 'OutParam(I)%SignM /= 1) then   ! this won''t be used' ],...
'         CALL SetErrStat(ErrID_Severe, "Negative channels not allowed for nodal outputs. Resetting channel name.", ErrStat, ErrMsg, RoutineName)',...
['         p%' PrefixStr2 'OutParam(I)%SignM = 1'],...
['         p%' PrefixStr2 'OutParam(I)%Name  = p%' PrefixStr2 'OutParam(I)%Name(2:) ! remove the first character that makes this a negative value'],...
'      end if', ...
''      ); 
end

fprintf( fout, '%s\n', ...
'      IF ( Indx > 0 ) THEN ! we found the channel name',...
'         IF ( InvalidOutput( ParamIndxAry(Indx) ) ) THEN  ! but, it isn''t valid for these settings',...
['            p%' PrefixStr2 'OutParam(I)%Indx  = 0                 ! pick any valid channel (I just picked "Time=0" here because it''s universal)'],...
['            p%' PrefixStr2 'OutParam(I)%Units = "INVALID"'],...
['            p%' PrefixStr2 'OutParam(I)%SignM = 0'],...
'         ELSE',...
['            p%' PrefixStr2 'OutParam(I)%Indx  = ParamIndxAry(Indx)'],...
['            p%' PrefixStr2 'OutParam(I)%Units = ParamUnitsAry(Indx) ! it''s a valid output'],...
'         END IF',...
'      ELSE ! this channel isn''t valid',...
['         p%' PrefixStr2 'OutParam(I)%Indx  = 0                    ! pick any valid channel (I just picked "Time=0" here because it''s universal)'],...
['         p%' PrefixStr2 'OutParam(I)%Units = "INVALID"'            ],...
['         p%' PrefixStr2 'OutParam(I)%SignM = 0                    ! multiply all results by zero'],...
'   '         ,...
['         CALL SetErrStat(ErrID_Fatal, TRIM(p%' PrefixStr2 'OutParam(I)%Name)//" is not an available output channel.",ErrStat,ErrMsg,RoutineName)'],...
'      END IF',...
'   '      ,...
'   END DO',...
'   '   ,...
'   RETURN',...
['END SUBROUTINE ' PrefixStr1 'SetOutParam'],...
'!----------------------------------------------------------------------------------------------------------------------------------',...
'!End of code generated by Matlab script', ...
'!**********************************************************************************************************************************' );
fprintf( fout, 'END MODULE %s_IO_Params\n',ModuleName)
%% Close the file
fclose(fout);

%% warn when too many outputs have been created!
fprintf( '%s%s%s\n', 'There are ', num2str(ParamNum), ' output parameters.');
if strcmpi(ModuleName,'FAST') && ParamNum >= 1000
    error('Too many output parameters! The maximum for FAST2ADAMS datasets is 1000.')
end    
                                
