import jDataview from 'jdataview';

interface TFileEntry {
    type: number;
    offset: number;
    realSize: number;
    compressedSize: number;
    lengthAligned: number;
    /** Raw filename bytes for re-decoding if needed */
    rawNameBytes?: Uint8Array;
}
/** Supported filename encodings */
type FilenameEncoding = 'utf-8' | 'euc-kr' | 'cp949' | 'latin1' | 'auto';
/** GRF loader options */
interface GrfOptions {
    /** Encoding for filenames (default: 'auto') */
    filenameEncoding?: FilenameEncoding;
    /** Threshold for auto-detection: if % of U+FFFD exceeds this, try Korean encodings (default: 0.01 = 1%) */
    autoDetectThreshold?: number;
    /** Maximum uncompressed size per file in bytes (default: 256MB) */
    maxFileUncompressedBytes?: number;
    /** Maximum total entries allowed (default: 500000) */
    maxEntries?: number;
}
/** Search/find options */
interface FindOptions {
    /** Filter by file extension (without dot, e.g., 'spr', 'act') */
    ext?: string;
    /** Filter by substring in path */
    contains?: string;
    /** Filter by path ending */
    endsWith?: string;
    /** Filter by regex pattern */
    regex?: RegExp;
    /** Maximum results to return (default: unlimited) */
    limit?: number;
}
/** Result of path resolution */
interface ResolveResult {
    status: 'found' | 'not_found' | 'ambiguous';
    /** The exact matched path (if found) */
    matchedPath?: string;
    /** All candidate paths (if ambiguous) */
    candidates?: string[];
}
/** GRF statistics */
interface GrfStats {
    /** Total file count */
    fileCount: number;
    /** Number of filenames with replacement character (U+FFFD) */
    badNameCount: number;
    /** Number of normalized key collisions */
    collisionCount: number;
    /** Extension statistics: ext -> count */
    extensionStats: Map<string, number>;
    /** Detected encoding used */
    detectedEncoding: FilenameEncoding;
}
declare const GRF_ERROR_CODES: {
    readonly INVALID_MAGIC: "GRF_INVALID_MAGIC";
    readonly UNSUPPORTED_VERSION: "GRF_UNSUPPORTED_VERSION";
    readonly NOT_LOADED: "GRF_NOT_LOADED";
    readonly FILE_NOT_FOUND: "GRF_FILE_NOT_FOUND";
    readonly AMBIGUOUS_PATH: "GRF_AMBIGUOUS_PATH";
    readonly DECOMPRESS_FAIL: "GRF_DECOMPRESS_FAIL";
    readonly CORRUPT_TABLE: "GRF_CORRUPT_TABLE";
    readonly LIMIT_EXCEEDED: "GRF_LIMIT_EXCEEDED";
    readonly INVALID_OFFSET: "GRF_INVALID_OFFSET";
    readonly DECRYPT_REQUIRED: "GRF_DECRYPT_REQUIRED";
};
declare class GrfError extends Error {
    code: keyof typeof GRF_ERROR_CODES;
    context?: Record<string, unknown> | undefined;
    constructor(code: keyof typeof GRF_ERROR_CODES, message: string, context?: Record<string, unknown> | undefined);
}
declare abstract class GrfBase<T> {
    private fd;
    version: number;
    fileCount: number;
    loaded: boolean;
    /** Map of exact filename -> entry */
    files: Map<string, TFileEntry>;
    /** Map of normalized path -> array of exact filenames (supports collisions) */
    private normalizedIndex;
    /** Map of extension -> array of exact filenames (for fast extension lookup) */
    private extensionIndex;
    private fileTableOffset;
    private cache;
    private cacheMaxSize;
    private cacheOrder;
    protected options: Required<GrfOptions>;
    private _stats;
    constructor(fd: T, options?: GrfOptions);
    abstract getStreamBuffer(fd: T, offset: number, length: number): Promise<Uint8Array>;
    getStreamReader(offset: number, length: number): Promise<jDataview>;
    load(): Promise<void>;
    private parseHeader;
    private parseFileList;
    private decodeEntry;
    private addToCache;
    private getFromCache;
    clearCache(): void;
    getFile(filename: string): Promise<{
        data: null | Uint8Array;
        error: null | string;
    }>;
    /**
     * Resolve a path to its exact filename in the GRF.
     * Tries exact match first, then normalized (case-insensitive, slash-agnostic).
     */
    resolvePath(query: string): ResolveResult;
    /**
     * Check if a file exists in the GRF.
     */
    hasFile(filename: string): boolean;
    /**
     * Get file entry metadata without extracting the file.
     */
    getEntry(filename: string): TFileEntry | null;
    /**
     * Find files matching the given criteria.
     */
    find(options?: FindOptions): string[];
    /**
     * Get all files with a specific extension.
     */
    getFilesByExtension(ext: string): string[];
    /**
     * List all unique extensions in the GRF.
     */
    listExtensions(): string[];
    /**
     * List all files in the GRF.
     */
    listFiles(): string[];
    /**
     * Get GRF statistics.
     */
    getStats(): GrfStats;
    /**
     * Get the detected/configured encoding used for filenames.
     */
    getDetectedEncoding(): FilenameEncoding;
    /**
     * Re-decode all filenames with a different encoding.
     * Useful if auto-detection chose wrong or you want to try a specific encoding.
     */
    reloadWithEncoding(encoding: FilenameEncoding): Promise<void>;
}

/**
 * Using this Browser, we work from a File or Blob object.
 * We are use the FileReader API to read only some part of the file to avoid
 * loading 2 gigas into memory
 */
declare class GrfBrowser extends GrfBase<File | Blob> {
    constructor(file: File | Blob, options?: GrfOptions);
    getStreamBuffer(buffer: File | Blob, offset: number, length: number): Promise<Uint8Array>;
}

/** Options for GrfNode */
interface GrfNodeOptions extends GrfOptions {
    /** Use buffer pool for better performance (default: true) */
    useBufferPool?: boolean;
}
declare class GrfNode extends GrfBase<number> {
    private useBufferPool;
    constructor(fd: number, options?: GrfNodeOptions);
    getStreamBuffer(fd: number, offset: number, length: number): Promise<Uint8Array>;
}

/**
 * Simple buffer pool for reducing GC pressure
 * Pools buffers of common sizes for reuse
 */
declare class BufferPool {
    private pools;
    private maxPoolSize;
    private readonly poolSizes;
    constructor();
    /**
     * Get appropriate pool size for requested length
     */
    private getPoolSize;
    /**
     * Acquire a buffer from the pool or create new one
     */
    acquire(length: number): Buffer;
    /**
     * Release a buffer back to the pool
     */
    release(buffer: Buffer): void;
    /**
     * Clear all pools
     */
    clear(): void;
    /**
     * Get pool statistics
     */
    stats(): {
        size: number;
        total: number;
        inUse: number;
    }[];
}
declare const bufferPool: BufferPool;

/**
 * Korean encoding decoder module
 *
 * Uses iconv-lite in Node.js for proper CP949 support.
 * Falls back to TextDecoder in browser (with limitations for CP949 extended chars).
 */
/**
 * Check if we're in a Node.js environment with iconv-lite available
 */
declare function hasIconvLite(): boolean;
/**
 * Count C1 control characters (U+0080-U+009F) in a string.
 * These usually indicate incorrectly decoded Korean bytes.
 * When EUC-KR decoder encounters CP949-extended bytes (0x80-0x9F range),
 * they get decoded as C1 control characters instead of Korean characters.
 */
declare function countC1ControlChars(str: string): number;
/**
 * Count replacement characters (U+FFFD) in a string
 */
declare function countReplacementChars(str: string): number;
/**
 * Count total "bad" characters (replacement + C1 control)
 */
declare function countBadChars(str: string): number;
/**
 * Check if a string looks like mojibake (CP949 bytes misread as Windows-1252).
 *
 * Mojibake occurs when:
 * 1. Korean text is encoded as CP949 bytes
 * 2. Those bytes are incorrectly decoded as Windows-1252/Latin-1
 *
 * Example: "유저인터페이스" → "À¯ÀúÀÎÅÍÆäÀÌ½º"
 *
 * @param str - The string to check
 * @returns true if the string appears to be mojibake
 */
declare function isMojibake(str: string): boolean;
/**
 * Fix mojibake by re-encoding as Windows-1252 and decoding as CP949.
 *
 * This reverses the common encoding error where CP949 bytes were
 * incorrectly interpreted as Windows-1252.
 *
 * Example: "À¯ÀúÀÎÅÍÆäÀÌ½º" → "유저인터페이스"
 *
 * @param garbled - The mojibake string to fix
 * @returns The corrected Korean string, or the original if unfixable
 */
declare function fixMojibake(garbled: string): string;
/**
 * Convert Korean text to mojibake (for testing purposes).
 *
 * This simulates the encoding error where Korean text is encoded as CP949
 * but decoded as Windows-1252.
 *
 * Example: "유저인터페이스" → "À¯ÀúÀÎÅÍÆäÀÌ½º"
 *
 * @param korean - The Korean string to garble
 * @returns The mojibake string
 */
declare function toMojibake(korean: string): string;
/**
 * Normalize a filename by detecting and fixing encoding issues.
 *
 * This function:
 * 1. Checks if the filename is mojibake and fixes it
 * 2. Returns the normalized filename
 *
 * @param filename - The filename to normalize
 * @returns The normalized filename
 */
declare function normalizeFilename(filename: string): string;
/**
 * Normalize a path by fixing mojibake in each segment.
 *
 * @param filepath - The full path to normalize
 * @returns The normalized path
 */
declare function normalizePath(filepath: string): string;

export { type FilenameEncoding, type FindOptions, GRF_ERROR_CODES, GrfBrowser, GrfError, GrfNode, type GrfNodeOptions, type GrfOptions, type GrfStats, type ResolveResult, type TFileEntry, bufferPool, countBadChars, countC1ControlChars, countReplacementChars, fixMojibake, hasIconvLite, isMojibake, normalizePath as normalizeEncodingPath, normalizeFilename, toMojibake };
