<cfscript>

	// The data directory has a mixture of Images (which are already persisted using a
	// compressed file-format) and large HTML files (which can be compressed).
	dataDirectory = expandPath( "./data" );

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //	

	// First, let's test the performance and outcome of the zip CLI when we use NO
	// COMPRESSION at all. This will store the files in an archive, but will not attempt
	// to safe any file-size.
	timer
		label = "No Compression (-0)"
		type = "outline"
		{

		archiveFilePath = expandPath( "./output/no-compression.zip" );

		zipOutput = executeZipFromDirectory(
			dataDirectory,
			[
				// Don't use any compression. This will be the fastest approach, but will
				// not result in any file-size advantage.
				"-0",
				// Recurse the input directory.
				"--recurse-paths",
				// Define the OUTPUT file path for the generated zip.
				archiveFilePath,
				// Define the INPUT file - NOTE that this path is RELATIVE TO THE WORKING
				// DIRECTORY! By using a relative directory, it allows us to generate a
				// ZIP file in which the relative paths become the entries in the
				// resultant archive.
				"./"
			]
		);

		echo( "File size: " & getFileSize( archiveFilePath ) );
		echo( "<pre>" & zipOutput & "</pre>" );

	}

	// Next, let's test the default behavior of the zip CLI. This uses a compression
	// setting of -6, which will attempt to compress all files.
	timer
		label = "Default Compression (-6)"
		type = "outline"
		{

		archiveFilePath = expandPath( "./output/default-compression.zip" );

		zipOutput = executeZipFromDirectory(
			dataDirectory,
			[
				// Recurse the input directory.
				"--recurse-paths",
				// Define the OUTPUT file path for the generated zip.
				archiveFilePath,
				// Define the INPUT file - NOTE that this path is RELATIVE TO THE WORKING
				// DIRECTORY! By using a relative directory, it allows us to generate a
				// ZIP file in which the relative paths become the entries in the
				// resultant archive.
				"./"
			]
		);

		echo( "File size: " & getFileSize( archiveFilePath ) );
		echo( "<pre>" & zipOutput & "</pre>" );

	}

	// And, finally, let's test the performance and outcome of the zip CLI when we use
	// the default compression, but tell the CLI to store any IMAGE FILES WITHOUT
	// COMPRESSION. This will include images in the archive, but will not attempt to
	// improve upon the already-compressed file-formats.
	timer
		label = "Mixed Compression (-6 + suffixes)"
		type = "outline"
		{

		archiveFilePath = expandPath( "./output/mixed-compression.zip" );

		// We are going to tell the zip CLI to skip compression for files with the given
		// set of file-extensions. This uses a colon-delimited list of extensions.
		// --
		// CAUTION: Unfortunately, these suffix values are CASE-SENSITIVE.
		suffixes = [ ".gif", ".jpeg", ".jpg", ".png" ].toList( ":" );

		zipOutput = executeZipFromDirectory(
			dataDirectory,
			[
				// Recurse the input directory.
				"--recurse-paths",
				// Define which files will be archived using the STORAGE method (no
				// compression) instead of DEFLATE.
				"--suffixes #suffixes#",
				// Define the OUTPUT file path for the generated zip.
				archiveFilePath,
				// Define the INPUT file - NOTE that this path is RELATIVE TO THE WORKING
				// DIRECTORY! By using a relative directory, it allows us to generate a
				// ZIP file in which the relative paths become the entries in the
				// resultant archive.
				"./"
			]
		);

		echo( "File size: " & getFileSize( archiveFilePath ) );
		echo( "<pre>" & zipOutput & "</pre>" );

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //
	
	/**
	* I execute the zip command-line utility from the given WORKING DIRECTORY using the
	* given arguments. If error-output is returned from the utility, an error with the
	* details is thrown.
	* 
	* @workingDirectory I am the working directory from which to execute the zip command.
	* @zipArguments I am the command-line arguments for zip.
	*/
	public string function executeZipFromDirectory(
		required string workingDirectory,
		required array zipArguments
		) {

		// The Shell Script that's going to proxy the ZIP command is expecting the
		// working directory to be the first argument. As such, let's create a normalized
		// set of arguments for our proxy that contains the working directory first,
		// followed by the rest of the commands.
		var normalizedArguments = [ workingDirectory ]
			.append( "zip" )
			.append( zipArguments, true )
		;

		execute
			name = expandPath( "./execute_from_directory.sh" )
			arguments = normalizedArguments.toList( " " )
			variable = "local.successOutput"
			errorVariable = "local.errorOutput"
			timeout = 30
			terminateOnTimeout = true
		;

		if ( len( errorOutput ?: "" ) ) {

			throw(
				type = "ZipFromDirectoryError",
				message = "The zip command-line proxy returned error output.",
				detail = "Error: #errorOutput#",
				extendedInfo = "Working directory: #workingDirectory#, Command-line arguments: #serializeJson( zipArguments )#"
			);

		}

		return( successOutput ?: "" );

	}


	/**
	* I return a string representing the byte-size of the given file.
	* 
	* @filepath I am the file to inspect.
	*/
	public string function getFileSize( required string filepath ) {

		return( numberFormat( fileInfo( filepath ).size ) );

	}

</cfscript>