/*
 * ZXC - High-performance lossless compression
 *
 * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors.
 * SPDX-License-Identifier: BSD-3-Clause
 */

/**
 * @file zxc_sans_io.h
 * @brief Low-level, sans-I/O compression primitives.
 *
 * This header exposes the building blocks used by the higher-level buffer and
 * streaming APIs.  It is intended for callers who need to drive compression or
 * decompression themselves - for example, to integrate ZXC into a custom I/O
 * pipeline or an event loop.
 *
 * A typical usage pattern is:
 * -# Allocate and initialise a context with zxc_cctx_init().
 * -# Write the file header with zxc_write_file_header().
 * -# Compress or decompress individual blocks via the chunk wrappers.
 * -# Write the footer with zxc_write_file_footer().
 * -# Release the context with zxc_cctx_free().
 *
 * @see zxc_buffer.h  for the simple one-shot API.
 * @see zxc_stream.h  for the multi-threaded streaming API.
 */

#ifndef ZXC_SANS_IO_H
#define ZXC_SANS_IO_H

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

#include "zxc_export.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @defgroup sans_io Sans-IO API
 * @brief Low-level primitives for building custom compression drivers.
 * @{
 */

/**
 * @struct zxc_cctx_t
 * @brief Compression Context structure.
 *
 * This structure holds the state and buffers required for the compression
 * process. It is designed to be reused across multiple blocks or calls to avoid
 * the overhead of repeated memory allocations.
 *
 * **Key Fields:**
 * - `hash_table`: Stores epoch-tagged positions (`ZXC_LZ_HASH_SIZE` * 4 bytes).
 * - `hash_tags`:  Stores 8-bit tags for fast rejection (`ZXC_LZ_HASH_SIZE` * 1 byte).
 * - `chain_table`: Handles collisions by storing the *previous* occurrence of a
 *   hash. This forms a linked list for each hash bucket, allowing us to
 * traverse history.
 * - `epoch`: Used for "Lazy Hash Table Invalidation". Instead of
 * `ZXC_MEMSET`ing the entire hash table (which is slow) for every block, we
 * store `(epoch << 16) | offset`. If the stored epoch doesn't match the current
 * `ctx->epoch`, the entry is considered invalid/empty.
 *
 * hash_table Pointer to the hash table used for LZ77 match finding.
 * chain_table Pointer to the chain table for collision resolution.
 * memory_block Pointer to the single allocation block containing all buffers.
 * epoch Current epoch counter for lazy hash table invalidation.
 * buf_extras Pointer to the buffer for extra lengths (LL >= 15 or ML >= 15).
 * buf_offsets Pointer to the buffer for offsets.
 * buf_tokens Pointer to the buffer for token sequences.
 * literals Pointer to the buffer for raw literal bytes.
 * lit_buffer Pointer to a scratch buffer for literal processing (e.g.,
 * RLE decoding).
 * lit_buffer_cap Current capacity of the literal scratch buffer.
 * checksum_enabled Flag indicating if checksums should be computed.
 * compression_level The configured compression level.
 */
typedef struct {
    /* Hot zone: random access / high frequency.
     * Kept at the start to ensure they reside in the first cache line (64 bytes). */
    uint32_t* hash_table;  /**< Hash table for LZ77 match positions (epoch|pos). */
    uint8_t* hash_tags;    /**< Split tag table for fast match rejection (8-bit tags). */
    uint16_t* chain_table; /**< Chain table for collision resolution. */
    void* memory_block;    /**< Single allocation block owner. */
    uint32_t epoch;        /**< Current epoch for lazy hash table invalidation. */

    /* Warm zone: sequential access per sequence. */
    uint32_t* buf_sequences; /**< Buffer for sequence records (packed: LL(8)|ML(8)|Offset(16)). */
    uint8_t* buf_tokens;     /**< Buffer for token sequences. */
    uint16_t* buf_offsets;   /**< Buffer for offsets. */
    uint8_t* buf_extras;     /**< Buffer for extra lengths (vbytes for LL/ML). */
    uint8_t* literals;       /**< Buffer for literal bytes. */

    /* Cold zone: configuration / scratch / resizeable. */
    uint8_t* lit_buffer;   /**< Scratch buffer for literals (RLE). */
    size_t lit_buffer_cap; /**< Current capacity of the scratch buffer. */
    uint8_t* work_buf;     /**< Padded scratch buffer for buffer-API decompression. */
    size_t work_buf_cap;   /**< Capacity of the work buffer. */
    int checksum_enabled;  /**< 1 if checksum calculation/verification is enabled. */
    int compression_level; /**< Compression level. */

    /* Block-size derived parameters (computed once at init). */
    size_t chunk_size;    /**< Effective block size in bytes. */
    uint32_t offset_bits; /**< log2(chunk_size) - governs epoch_mark shift. */
    uint32_t offset_mask; /**< (1U << offset_bits) - 1 */
    uint32_t max_epoch;   /**< 1U << (32 - offset_bits) */
} zxc_cctx_t;

/**
 * @brief Initializes a ZXC compression context.
 *
 * Sets up the internal state required for compression operations, allocating
 * necessary buffers based on the chunk size and compression level.
 *
 * @param[out] ctx Pointer to the ZXC compression context structure to initialize.
 * @param[in] chunk_size The size of the data chunk to be compressed. This
 * determines the allocation size for various internal buffers.
 * @param[in] mode The operation mode (1 for compression, 0 for decompression).
 * @param[in] level The desired compression level to be stored in the context.
 * @param[in] checksum_enabled 1 to enable checksums, 0 to disable.
 * @return ZXC_OK on success, or a negative zxc_error_t code (e.g., ZXC_ERROR_MEMORY) if memory
 * allocation fails.
 */
ZXC_EXPORT int zxc_cctx_init(zxc_cctx_t* ctx, const size_t chunk_size, const int mode,
                             const int level, const int checksum_enabled);

/**
 * @brief Frees resources associated with a ZXC compression context.
 *
 * This function releases all internal buffers and tables associated with the
 * given ZXC compression context structure. It does not free the context pointer
 * itself, only its members.
 *
 * @param[in,out] ctx Pointer to the compression context to clean up.
 */
ZXC_EXPORT void zxc_cctx_free(zxc_cctx_t* ctx);

/**
 * @brief Writes the standard ZXC file header to a destination buffer.
 *
 * This function stores the magic word (little-endian) and the version number
 * into the provided buffer. It ensures the buffer has sufficient capacity
 * before writing.
 *
 * @param[out] dst The destination buffer where the header will be written.
 * @param[in] dst_capacity The total capacity of the destination buffer in bytes.
 * @param[in] chunk_size    The block size to encode in the header.
 * @param[in] has_checksum  Flag indicating whether the checksum bit should be set.
 * @return The number of bytes written (ZXC_FILE_HEADER_SIZE) on success,
 *         or ZXC_ERROR_DST_TOO_SMALL if the destination capacity is insufficient.
 */
ZXC_EXPORT int zxc_write_file_header(uint8_t* dst, const size_t dst_capacity,
                                     const size_t chunk_size, const int has_checksum);

/**
 * @brief Validates and reads the ZXC file header from a source buffer.
 *
 * This function checks if the provided source buffer is large enough to contain
 * a ZXC file header and verifies that the magic word and version number match
 * the expected ZXC format specifications.
 *
 * @param[in] src Pointer to the source buffer containing the file data.
 * @param[in] src_size Size of the source buffer in bytes.
 * @param[out] out_block_size    Optional pointer to receive the recommended block size.
 * @param[out] out_has_checksum  Optional pointer to receive the checksum flag.
 * @return ZXC_OK on success, or a negative error code (e.g., ZXC_ERROR_SRC_TOO_SMALL,
 * ZXC_ERROR_BAD_MAGIC, ZXC_ERROR_BAD_VERSION).
 */
ZXC_EXPORT int zxc_read_file_header(const uint8_t* src, const size_t src_size,
                                    size_t* out_block_size, int* out_has_checksum);

/**
 * @struct zxc_block_header_t
 * @brief Represents the on-disk header structure for a ZXC block (8 bytes).
 *
 * This structure contains metadata required to parse and decompress a block.
 * Note: raw_size is not stored in the header; decoders derive it from Section
 * Descriptors within the compressed payload.
 */
typedef struct {
    uint8_t block_type;  /**< Block type (see @ref zxc_block_type_t). */
    uint8_t block_flags; /**< Flags (e.g., checksum presence). */
    uint8_t reserved;    /**< Reserved for future protocol extensions. */
    uint8_t header_crc;  /**< Header integrity checksum (1 byte). */
    uint32_t comp_size;  /**< Compressed size excluding this header. */
} zxc_block_header_t;

/**
 * @brief Encodes a block header into the destination buffer.
 *
 * This function serializes the contents of a `zxc_block_header_t` structure
 * into a byte array in little-endian format. It ensures the destination buffer
 * has sufficient capacity before writing.
 *
 * @param[out] dst Pointer to the destination buffer where the header will be
 * written.
 * @param[in] dst_capacity The total size of the destination buffer in bytes.
 * @param[in] bh Pointer to the source block header structure containing the data to
 * write.
 *
 * @return The number of bytes written (ZXC_BLOCK_HEADER_SIZE) on success,
 *         or ZXC_ERROR_DST_TOO_SMALL if the destination buffer capacity is insufficient.
 */
ZXC_EXPORT int zxc_write_block_header(uint8_t* dst, const size_t dst_capacity,
                                      const zxc_block_header_t* bh);

/**
 * @brief Reads and parses a ZXC block header from a source buffer.
 *
 * This function extracts the block type, flags, reserved fields, compressed
 * size, and raw size from the first `ZXC_BLOCK_HEADER_SIZE` bytes of the source
 * buffer. It handles endianness conversion for multi-byte fields (Little
 * Endian).
 *
 * @param[in] src       Pointer to the source buffer containing the block data.
 * @param[in] src_size  The size of the source buffer in bytes.
 * @param[out] bh        Pointer to a `zxc_block_header_t` structure where the parsed
 *                  header information will be stored.
 *
 * @return ZXC_OK on success, or ZXC_ERROR_SRC_TOO_SMALL if the source buffer is smaller
 *         than the required block header size.
 */
ZXC_EXPORT int zxc_read_block_header(const uint8_t* src, const size_t src_size,
                                     zxc_block_header_t* bh);

/**
 * @brief Writes the ZXC file footer.
 *
 * The footer stores the original uncompressed size and an optional global
 * checksum.  It is always @c ZXC_FILE_FOOTER_SIZE (12) bytes.
 *
 * @param[out] dst             Destination buffer.
 * @param[in] dst_capacity     Capacity of destination buffer.
 * @param[in] src_size         Original uncompressed size of the data.
 * @param[in] global_hash      Global checksum hash (if enabled).
 * @param[in] checksum_enabled Flag indicating if checksum is enabled.
 * @return Number of bytes written (12) on success, or ZXC_ERROR_DST_TOO_SMALL on failure.
 */
ZXC_EXPORT int zxc_write_file_footer(uint8_t* dst, const size_t dst_capacity,
                                     const uint64_t src_size, const uint32_t global_hash,
                                     const int checksum_enabled);

/** @} */ /* end of sans_io */

#ifdef __cplusplus
}
#endif

#endif  // ZXC_SANS_IO_H