class DataBridgeApp {

    @SuppressWarnings("PMD.SystemPrintln")
    @SuppressFBWarnings(value = "VA_FORMAT_STRING_USES_NEWLINE", justification = "We will use the standard carriage return character.")
    protected ReturnValue parseCommandLineArguments(String[] args, ApplicationContext applicationContext)
    {
        // Get the application argument parser.
        ArgumentParser argParser = getArgumentParser();

        try
        {
            s3AccessKeyOpt = argParser.addArgument("a", "s3AccessKey", true, "S3 access key.", false);
            s3SecretKeyOpt = argParser.addArgument("p", "s3SecretKey", true, "S3 secret key.", false);
            s3EndpointOpt = argParser.addArgument("e", "s3Endpoint", true, "S3 endpoint.", false);
            localPathOpt = argParser.addArgument("l", "localPath", true, "The path to files on your local file system.", true);
            manifestPathOpt = argParser.addArgument("m", "manifestPath", true, "Local path to the manifest file.", true);
            regServerHostOpt = argParser.addArgument("H", "regServerHost", true, "Registration Service hostname.", false);
            regServerPortOpt = argParser.addArgument("P", "regServerPort", true, "Registration Service port.", false);
            dmRegServerHostOpt = argParser.addArgument("Y", "dmRegServerHost", true, "Registration Service hostname (deprecated - use regServerHost).", false);
            dmRegServerPortOpt = argParser.addArgument("Z", "dmRegServerPort", true, "Registration Service port (deprecated - use regServerPort.", false);
            sslOpt = argParser.addArgument("s", "ssl", true, "Enable or disable SSL (HTTPS).", false);
            usernameOpt = argParser.addArgument("u", "username", true, "The username for HTTPS client authentication.", false);
            passwordOpt = argParser.addArgument("w", "password", true, "The password used for HTTPS client authentication.", false);
            enableEnvVariablesOpt = argParser.addArgument("E", "enableEnvVariables", true,
                "The enableEnvVariables used for HTTPS client authentication through environment provided variable.", false);
            trustSelfSignedCertificateOpt =
                argParser.addArgument("C", "trustSelfSignedCertificate", true, "If set to true, makes HTTPS client trust self-signed certificate.", false);
            disableHostnameVerificationOpt =
                argParser.addArgument("d", "disableHostnameVerification", true, "If set to true, turns off hostname verification.", false);
            helpOpt = argParser.addArgument("h", "help", false, "Display usage information and exit.", false);
            versionOpt = argParser.addArgument("v", "version", false, "Display version information and exit.", false);
            httpProxyHostOpt = argParser.addArgument("n", "httpProxyHost", true, "HTTP proxy host.", false);
            httpProxyPortOpt = argParser.addArgument("o", "httpProxyPort", true, "HTTP proxy port.", false);
            maxThreadsOpt = argParser.addArgument("t", "maxThreads", true, "Maximum number of threads.", false);
            socketTimeoutOpt =
                argParser.addArgument("c", "socketTimeout", true, "The socket timeout in milliseconds. 0 indicates no timeout. Default 50000.", false);

            // Parse command line arguments without failing on any missing required arguments by passing "false" as the second argument.
            argParser.parseArguments(args, false);

            // If help option was specified, then display usage information and return success.
            if (argParser.getBooleanValue(helpOpt))
            {
                System.out.println(argParser.getUsageInformation());
                return ReturnValue.SUCCESS;
            }

            // If version option was specified, then display version information and return success.
            if (argParser.getBooleanValue(versionOpt))
            {
                BuildInformation buildInformation = applicationContext.getBean(BuildInformation.class);
                System.out.println(String
                    .format(BUILD_INFO_STRING_FORMAT, buildInformation.getBuildDate(), buildInformation.getBuildNumber(), buildInformation.getBuildOs(),
                        buildInformation.getBuildUser()));
                return ReturnValue.SUCCESS;
            }

            // Parse command line arguments for the second time, enforcing the required arguments by passing "true" as the second argument.
            argParser.parseArguments(args, true);

            // Extract boolean option values passing "false" as a default value.
            useSsl = argParser.getStringValueAsBoolean(sslOpt, false);
            trustSelfSignedCertificate = argParser.getStringValueAsBoolean(trustSelfSignedCertificateOpt, false);
            disableHostnameVerification = argParser.getStringValueAsBoolean(disableHostnameVerificationOpt, false);

            if (useSsl)
            {
                // Username is required when useSsl is enabled.
                if (StringUtils.isBlank(argParser.getStringValue(usernameOpt)))
                {
                    throw new ParseException("Username is required when SSL is enabled.");
                }
                // Password or enableEnvVariables with corresponding env var is required when useSsl is enabled.
                ToolsArgumentHelper.validateCliEnvArgument(argParser, passwordOpt, enableEnvVariablesOpt);
            }

            // Ensure that both the S3 secret and access keys were specified or both not specified.
            if (StringUtils.isNotBlank(argParser.getStringValue(s3SecretKeyOpt)) && StringUtils.isBlank(argParser.getStringValue(s3AccessKeyOpt)))
            {
                throw new ParseException("S3 access key must be specified when S3 secret key is present.");
            }
            if (StringUtils.isNotBlank(argParser.getStringValue(s3AccessKeyOpt)) && StringUtils.isBlank(argParser.getStringValue(s3SecretKeyOpt)))
            {
                throw new ParseException("S3 secret key must be specified when S3 access key is present.");
            }

            // Get the registration server host
            regServerHost = argParser.getStringValue(regServerHostOpt);
            if (StringUtils.isBlank(regServerHost))
            {
                regServerHost = argParser.getStringValue(dmRegServerHostOpt);
                if (StringUtils.isBlank(regServerHost))
                {
                    throw new ParseException("A registration host must be specified.");
                }
            }
            else
            {
                if (StringUtils.isNotBlank(argParser.getStringValue(dmRegServerHostOpt)))
                {
                    throw new ParseException("The regServerHost and the dmRegServerHost options can't both be specified.");
                }
            }

            // Get the registration server port
            regServerPort = argParser.getIntegerValue(regServerPortOpt);
            if (regServerPort == null)
            {
                regServerPort = argParser.getIntegerValue(dmRegServerPortOpt);
                if (regServerPort == null)
                {
                    throw new ParseException("A registration port must be specified.");
                }
            }
            else
            {
                if (argParser.getIntegerValue(dmRegServerPortOpt) != null)
                {
                    throw new ParseException("The regServerPort and the dmRegServerPort options can't both be specified.");
                }
            }

            // Extract all Integer option values here to catch any NumberFormatException exceptions.
            httpProxyPort = argParser.getIntegerValue(httpProxyPortOpt);
            maxThreads = argParser.getIntegerValue(maxThreadsOpt, ToolsCommonConstants.DEFAULT_THREADS);
        }
        catch (ParseException ex)
        {
            // Log a friendly error and return a failure which will cause the application to exit.
            LOGGER.error("Error parsing command line arguments: " + ex.getMessage() + "\n" + argParser.getUsageInformation());
            return ReturnValue.FAILURE;
        }

        // The command line arguments were all parsed successfully so return null to continue processing.
        return null;
    }

}