#pragma once

#include "AbstractEncoder.h"
#include "HuffmanEncoder.h"
#include "CircleBuffer.h"

#include <string>
#include <vector>
#include <memory>
#include <tuple>

class PrefixCodeGenerator;

class Lz77Encoder : public AbstractEncoder
{
public:
    using Hit = std::tuple<unsigned, unsigned, unsigned char>;

    Lz77Encoder(BitStream* inputStream, BitStream* outputStream);

    bool encode() override;

    bool decode() override;

    const std::vector<Hit>& getHitBuffer() const;

    void setSearchBufferSize(unsigned size);

    void setLookAheadBufferSize(unsigned size);

    void setPrefixCodeGenerator(std::unique_ptr<PrefixCodeGenerator> generator);

    bool hitBufferFull() const;

private:

    bool lookAheadSourceEmpty() const;
    unsigned char getSearchBufferItem(unsigned index) const;

    unsigned lookAheadForMatchingChars(unsigned searchIndex);

    void lookForMatches(unsigned char searchChar, unsigned& hitLength, unsigned& hitOffset);

    void populateLookaheadBuffer(unsigned size, bool firstPass = false);

    void populateSearchBuffer(const Hit& hit);

    unsigned mMaxHitBufferSize{32000};
    std::vector<Hit> mHitBuffer;

    unsigned mSearchBufferSize{32000};
    CircleBuffer<unsigned char> mSearchBuffer;

    unsigned mLookAheadBufferSize{256};
    int mMaxLookAheadBufferIndex{0};
    unsigned mMinLengthMatchSize{1};

    CircleBuffer<unsigned char>  mLookaheadBuffer;
    std::unique_ptr<PrefixCodeGenerator> mCodeGenerator;
};
