#ifndef ZINFLATE_H
#define ZINFLATE_H

#include "forkjoin.h"
#include "misc.h"
#include "queue.h"

class Inflator : public Fork
{
public:
    class Err : public CryptlibException {public: Err(const char *message) : CryptlibException(message) {}};
    class UnexpectedEndErr : public Err {public: UnexpectedEndErr() : Err("Inflator: unexpected end of compressed block") {}};

    Inflator(BufferedTransformation *output = NULL,
             BufferedTransformation *bypassed = NULL);

    void Put(byte b)
        {Inflator::Put(&b, 1);}

    void Put(const byte *inString, unsigned int length);
    void InputFinished();

private:
    struct huft {
      byte e;                /* number of extra bits or operation */
      byte b;                /* number of bits in this code or subcode */
      union {
        word16 n;              /* literal, length base, or distance base */
        struct huft *t;     /* pointer to next level of table */
      } v;
    };

    int huft_build (unsigned *, unsigned, unsigned, const word16 *, const word16 *,
                       huft **, int *);
    int huft_free (huft *);
    int inflate_codes (huft *, huft *, int, int);
    int inflate_stored (void);
    int inflate_fixed (void);
    int inflate_dynamic (void);
    int inflate_block (bool &);
    void flush_output(unsigned int w);

    static const word16 border[19];
    static const word16 cplens[31];
    static const word16 cplext[31];
    static const word16 cpdist[31];
    static const word16 cpdext[31];

    static const word16 mask_bits[18];

    ByteQueue inQueue;
    byte NEXTBYTE();

    SecByteBlock slide;
    unsigned int wp;
    word32 bb;                         /* bit buffer */
    unsigned bk;                    /* bits in bit buffer */

    bool afterEnd;
};

#endif

