class IgniteWalConverterArguments {

    public static IgniteWalConverterArguments parse(final PrintStream out, String args[]) {
        AtomicReference<CLIArgumentParser> parserRef = new AtomicReference<>();

        CLIArgumentParser parser = new CLIArgumentParser(asList(
            optionalArg(WAL_DIR.arg(), "Path to dir with wal files.", String.class, () -> {
                if (parserRef.get().get(Args.WAL_ARCHIVE_DIR.arg()) == null)
                    throw new IllegalArgumentException("One of the arguments --wal-dir or --wal-archive-dir must be specified.");
                else
                    return null;
            }),
            optionalArg(Args.WAL_ARCHIVE_DIR.arg(), "Path to dir with wal files.", String.class),
            optionalArg(PAGE_SIZE.arg(), "Size of pages, which was selected for file store (1024, 2048, 4096, etc).", Integer.class, () -> 4096),
            optionalArg(BINARY_METADATA_DIR.arg(), "Path to binary meta.", String.class),
            optionalArg(MARSHALLER_MAPPING_DIR.arg(), "Path to marshaller dir.", String.class),
            optionalArg(UNWRAP_BINARY.arg(), "Unwrap binary non-primitive objects.", Boolean.class),
            optionalArg(RECORD_TYPES.arg(), "Comma-separated WAL record types (TX_RECORD, DATA_RECORD, etc). By default, all types will be printed.", String[].class),
            optionalArg(WAL_TIME_FROM_MILLIS.arg(), "The start time interval for the record time in milliseconds.", Long.class),
            optionalArg(WAL_TIME_TO_MILLIS.arg(), "The end time interval for the record time in milliseconds.", Long.class),
            optionalArg(HAS_TEXT.arg(), "Filter by substring in the WAL record.", String.class),
            optionalArg(INCLUDE_SENSITIVE.arg(), "Strategy for the processing of sensitive data (SHOW, HIDE, HASH, MD5). Default MD5.", String.class, ProcessSensitiveData.MD5::toString),
            optionalArg(PRINT_STAT.arg(), "Write summary statistics for WAL.", Boolean.class),
            optionalArg(SKIP_CRC.arg(), "Skip CRC calculation/check flag", Boolean.class)
        ));

        if (args == null || args.length < 1) {
            out.println("Print WAL log data in human-readable form.");
            out.println(parser.usage());
            out.println("For example:");
            out.println("    " + WAL_DIR.arg() + " /work/db/wal");
            out.println("    " + WAL_ARCHIVE_DIR.arg() + " /work/db/wal_archive");
            out.println("    " + PAGE_SIZE.arg() + " 4096");
            out.println("    " + BINARY_METADATA_DIR.arg() + " /work/db/nodeId-consistentId");
            out.println("    " + MARSHALLER_MAPPING_DIR.arg() + " /work/db/marshaller");
            out.println("    " + UNWRAP_BINARY.arg());
            out.println("    " + RECORD_TYPES.arg() + " DataRecord,TxRecord");
            out.println("    " + WAL_TIME_FROM_MILLIS.arg() + " 1575158400000");
            out.println("    " + WAL_TIME_TO_MILLIS.arg() + " 1577836740999");
            out.println("    " + HAS_TEXT.arg() + " search_string");
            out.println("    " + INCLUDE_SENSITIVE.arg() + " SHOW");
            out.println("    " + SKIP_CRC.arg());

            return null;
        }

        parserRef.set(parser);

        parser.parse(asList(args).iterator());

        File walDir = file(parser.get(WAL_DIR.arg()), true, false);
        File walArchiveDir = file(parser.get(Args.WAL_ARCHIVE_DIR.arg()), true, false);
        int pageSize = parser.get(PAGE_SIZE.arg());
        File binaryMetadataDir = file(parser.get(BINARY_METADATA_DIR.arg()), true, true);
        File marshallerMappingDir = file(parser.get(MARSHALLER_MAPPING_DIR.arg()), true, true);
        boolean unwrapBinary = parser.get(UNWRAP_BINARY.arg());
        final Set<WALRecord.RecordType> recordTypes = checkRecordTypes(parser.get(RECORD_TYPES.arg()));
        Long fromTime = parser.get(Args.WAL_TIME_FROM_MILLIS.arg());
        Long toTime = parser.get(Args.WAL_TIME_TO_MILLIS.arg());
        String hasText = parser.get(Args.HAS_TEXT.arg());
        boolean printStat = parser.get(Args.PRINT_STAT.arg());
        boolean skipCrc = parser.get(Args.SKIP_CRC.arg());

        String processSensitiveDataStr = parser.get(Args.INCLUDE_SENSITIVE.arg());

        ProcessSensitiveData includeSensitive;

        try {
            includeSensitive = ProcessSensitiveData.valueOf(processSensitiveDataStr);
        }
        catch (Exception e) {
            throw new IllegalArgumentException("Unknown --include-sensitive: " + processSensitiveDataStr +
                ". Supported: " + Arrays.toString(ProcessSensitiveData.values()));
        }

        out.println("Program arguments:");

        if (walDir != null)
            out.printf("\t%s = %s\n", WAL_DIR, walDir.getAbsolutePath());

        if (walArchiveDir != null)
            out.printf("\t%s = %s\n", WAL_ARCHIVE_DIR, walArchiveDir.getAbsolutePath());

        out.printf("\t%s = %d\n", PAGE_SIZE, pageSize);

        if (binaryMetadataDir != null)
            out.printf("\t%s = %s\n", BINARY_METADATA_DIR, binaryMetadataDir);

        if (marshallerMappingDir != null)
            out.printf("\t%s = %s\n", MARSHALLER_MAPPING_DIR, marshallerMappingDir);

        out.printf("\t%s = %s\n", UNWRAP_BINARY, unwrapBinary);

        if (!F.isEmpty(recordTypes))
            out.printf("\t%s = %s\n", RECORD_TYPES, recordTypes);

        if (fromTime != null)
            out.printf("\t%s = %s\n", WAL_TIME_FROM_MILLIS, new Date(fromTime));

        if (toTime != null)
            out.printf("\t%s = %s\n", WAL_TIME_TO_MILLIS, new Date(toTime));

        if (hasText != null)
            out.printf("\t%s = %s\n", HAS_TEXT, hasText);

        out.printf("\t%s = %s\n", INCLUDE_SENSITIVE, includeSensitive);

        out.printf("\t%s = %b\n", PRINT_STAT, printStat);

        out.printf("\t%s = %b\n", SKIP_CRC, skipCrc);

        return new IgniteWalConverterArguments(walDir, walArchiveDir, pageSize,
            binaryMetadataDir, marshallerMappingDir,
            unwrapBinary, recordTypes, fromTime, toTime, hasText, includeSensitive, printStat, skipCrc);
    }

    public  IgniteWalConverterArguments(
        File walDir,
        File walArchiveDir,
        int pageSize,
        File binaryMetadataDir,
        File marshallerMappingDir,
        boolean unwrapBinary,
        Set<WALRecord.RecordType> recordTypes,
        Long fromTime,
        Long toTime,
        String hasText,
        ProcessSensitiveData includeSensitive,
        boolean printStat,
        boolean skipCrc
    );

}