/*
 * Copyright 2026 CodingMarkus
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later OR Apache-2.0
 */

#pragma once

#include <stddef.h>
#include <stdint.h>

#ifndef SQUINCH_RESULT_DEFINED
#define SQUINCH_RESULT_DEFINED

/**
	Reports the result of a compression or decompression operation.
*/
enum SquinchResult {
	/** Operation completed successfully. */
	Success_SquinchResult,

	/** The input buffer was NULL with a nonzero input size. */
	InputBufferIsNULL_SquinchResult,

	/** The output buffer was NULL with a nonzero output size. */
	OutputBufferIsNULL_SquinchResult,

	/** The out-bytes-written pointer was NULL. */
	OutBytesWrittenIsNULL_SquinchResult,

	/** The input bytes consumed pointer was NULL. */
	InputBytesConsumedIsNULL_SquinchResult,

	/** The out output bytes written pointer was NULL. */
	OutputBytesWrittenIsNULL_SquinchResult,

	/** A header-value output pointer was NULL. */
	OutHeaderValueIsNULL_SquinchResult,

	/** The window size was outside its accepted range. */
	WindowSizeOutOfRange_SquinchResult,

	/** The match limit was outside its accepted range. */
	MatchLimitOutOfRange_SquinchResult,

	/** The bucket count was outside its accepted range. */
	BucketCountOutOfRange_SquinchResult,

	/** The compressor pointer was NULL. */
	CompressorIsNULL_SquinchResult,

	/** The decompressor pointer was NULL. */
	DecompressorIsNULL_SquinchResult,

	/** The maximum window size was outside its accepted range. */
	MaximumWindowSizeOutOfRange_SquinchResult,

	/** The compressed input was malformed. */
	InvalidInput_SquinchResult,

	/** Memory allocation failed or a size was not representable. */
	OutOfMemory_SquinchResult,

	/** The stream requires more history than the decoder reserved. */
	WindowTooSmall_SquinchResult,

	/** Input was supplied after the end of data. */
	InputAfterEndOfData_SquinchResult,

	/** Output was requested after the end of data was fully drained. */
	NoMoreOutputAvailable_SquinchResult,

	/** The complete stream header has not been parsed yet. */
	HeaderNotFullyParsed_SquinchResult,

	/** The look-ahead size was zero. */
	LookAheadSizeOutOfRange_SquinchResult,
};

#endif


/**
	The recommended compression match limit.
*/
static const size_t SQUINCH_DEFAULT_MATCH_LIMIT = 128;

/**
	The largest compression match limit accepted by the API.
*/
static const size_t SQUINCH_MAXIMUM_MATCH_LIMIT = 512;

/**
	The default number of hash matcher buckets.
*/
static const size_t SQUINCH_DEFAULT_BUCKET_COUNT = 65536;

/**
	The smallest nonzero hash matcher bucket count accepted by the API.
*/
static const size_t SQUINCH_MINIMUM_BUCKET_COUNT = 1024;

/**
	The default look-ahead ring size in bytes.
*/
static const size_t SQUINCH_DEFAULT_LOOK_AHEAD_SIZE = 1024;


/**
	Returns a sufficient output size for compressing inputSize bytes.

	inputSize must be in the range 0 through (SIZE_MAX - 16) / 2. The result is
	16 bytes of stream header plus at most two bytes for every input byte.
*/
#define SQUINCH_MAXIMUM_COMPRESSED_SIZE( inputSize ) \
	(16 + 2 * (inputSize))


struct SquinchCompressor;


/**
	Compresses one complete input buffer into an output buffer.

	@param[in] input The bytes to compress. This may be NULL only when
		inputSize is zero. The input and output buffers must not overlap.
	@param[in] inputSize The number of input bytes in the range 0 through
		SIZE_MAX.
	@param[out] output The destination buffer. This may be NULL only when
		outputSize is zero.
	@param[in] outputSize The destination capacity in bytes in the range 0
		through SIZE_MAX. SQUINCH_MAXIMUM_COMPRESSED_SIZE(inputSize) is always
		sufficient when its inputSize requirement is met.
	@param[out] outBytesWritten Receives the number of bytes written, including
		when the output buffer is full. This pointer must not be NULL.
	@param[in] windowSize The history window size in bytes in the range 16
		through 65536.
	@param[in] matchLimit The maximum candidates examined per hash chain in
		the range 1 through SQUINCH_MAXIMUM_MATCH_LIMIT. Zero selects
		SQUINCH_DEFAULT_MATCH_LIMIT. Larger values can improve compression at
		the cost of speed.
	@param[in] bucketCount The number of hash matcher buckets. Zero selects
		SQUINCH_DEFAULT_BUCKET_COUNT. Nonzero values must be at least
		SQUINCH_MINIMUM_BUCKET_COUNT; there is no maximum. Fewer buckets use
		less memory but can reduce compression speed and ratio.

	@returns
		- Success_SquinchResult: On success.
		- A parameter-specific invalid-argument result.
		- OutOfMemory_SquinchResult: When allocation fails.
*/
enum SquinchResult SquinchCompress(
	const void * input,
	size_t inputSize,
	void * output,
	size_t outputSize,
	size_t * outBytesWritten,
	size_t windowSize,
	size_t matchLimit,
	size_t bucketCount
);


/**
	Creates a streaming compressor for the supplied settings.

	The compressor retains a history window, look-ahead ring, and matcher
	tables. Pass the returned object to the streaming input and destroy
	functions.

	@param[out] outCompressor Receives the new compressor on success. This
		pointer must not be NULL and is not modified on failure.
	@param[in] uncompressedSize The total uncompressed size to store in the
		header. Zero means unknown. Values above 2^40 - 1 are stored as zero.
	@param[in] windowSize The history window size in bytes in the range 16
		through 65536.
	@param[in] lookAheadSize The look-ahead ring size in bytes in the range 1
		through SIZE_MAX. Zero selects min(windowSize, 1024).
	@param[in] bucketCount The number of hash matcher buckets. Zero selects
		SQUINCH_DEFAULT_BUCKET_COUNT. Nonzero values must be at least
		SQUINCH_MINIMUM_BUCKET_COUNT; there is no maximum.
	@param[in] matchLimit The maximum candidates examined per hash chain in
		the range 1 through SQUINCH_MAXIMUM_MATCH_LIMIT. Zero selects
		SQUINCH_DEFAULT_MATCH_LIMIT.

	@returns
		- Success_SquinchResult: On success.
		- A parameter-specific invalid-argument result.
		- OutOfMemory_SquinchResult: When allocation fails.
*/
enum SquinchResult SquinchCompressorCreate(
	struct SquinchCompressor ** outCompressor,
	uint_least64_t uncompressedSize,
	size_t windowSize,
	size_t lookAheadSize,
	size_t bucketCount,
	size_t matchLimit
);


/**
	Consumes input bytes and writes compressed output in one call.

	Input is copied into the compressor's look-ahead ring before it is
	compressed. A zero inputSize finishes the compressor. After finishing,
	call this function with zero input until it writes no more output. The
	compressor cannot accept more input after it has been finished.

	@param[in,out] compressor The compressor to update. It must not be NULL.
	@param[in] input The bytes to consume. This may be NULL only when
		inputSize is zero.
	@param[in] inputSize The number of input bytes in the range 0 through
		SIZE_MAX. Zero finishes the stream.
	@param[out] outInputBytesConsumed Receives the number of input bytes
		consumed in the range 0 through inputSize.
	@param[out] output The destination buffer. This may be NULL only when
		outputSize is zero.
	@param[in] outputSize The destination capacity in bytes.
	@param[out] outOutputBytesWritten Receives the number of output bytes
		written in the range 0 through outputSize.

	@returns
		- Success_SquinchResult: On success. Input may be partially consumed
		  when compressed output remains pending.
		- A parameter-specific invalid-argument result.
		- InputAfterEndOfData_SquinchResult: For input after end of data.
		- NoMoreOutputAvailable_SquinchResult: For output after it was drained.
*/
enum SquinchResult SquinchCompressorStep(
	struct SquinchCompressor * compressor,
	const void * input,
	size_t inputSize,
	size_t * outInputBytesConsumed,
	void * output,
	size_t outputSize,
	size_t * outOutputBytesWritten
);


/**
	Destroys a streaming compressor and releases its memory.

	@param compressor The compressor to destroy, or NULL. A non-NULL pointer
		is invalid after this call.
*/
void SquinchCompressorDestroy( struct SquinchCompressor * compressor );
