#include "DeflateEncoder.h"

#include "BitStream.h"
#include "ByteUtils.h"
#include "DeflateBlock.h"
#include "BufferBitStream.h"

#include <iostream>

DeflateEncoder::DeflateEncoder(BitStream* inputStream, BitStream* outputStream)
    : AbstractEncoder(inputStream, outputStream)
{

}

DeflateEncoder::~DeflateEncoder()
{

}

bool DeflateEncoder::encode()
{
    uint16_t count = 0;
    BufferBitStream stream;
    std::unique_ptr<DeflateBlock> working_block = std::make_unique<DeflateBlock>(&stream, mOutputStream);
    working_block->setCompressionMethod(mCompressionMethod);

    if (mChecksumCalculators.size() > 0)
    {
       //std::cout << "Setting checksum calculator " << std::endl;
       mOutputStream->setChecksumCalculator(mChecksumCalculators[0]);
    }

    while(true)
    {
        if (count == mMaxBlockSize)
        {
            //std::cout << working_block->getMetaData();
            working_block->write(count);

            working_block = std::make_unique<DeflateBlock>(&stream, mOutputStream);
            working_block->setCompressionMethod(mCompressionMethod);
            stream.reset();
        }

        if (auto byte = mInputStream->readNextByte())
        {
            //std::cout << "Adding byte " << ByteUtils::toString(*byte) << " to deflate block input" << std::endl;
            stream.writeByte(*byte);
        }
        else
        {
            if (const auto& remaining_bits = mInputStream->getRemainingBits(); remaining_bits.second > 0)
            {
                stream.writeNBits(remaining_bits.first, remaining_bits.second);
            }

            stream.resetOffsets();
            working_block->setIsFinalBlock(true);

            //std::cout << working_block->getMetaData();
            working_block->write(count);
            break;
        }
        count++;
    }
    mOutputStream->flushRemainingBits();
    mOutputStream->clearChecksumCalculator();
    return true;
}

bool DeflateEncoder::decode()
{
    auto working_block = std::make_unique<DeflateBlock>(mInputStream, mOutputStream);
    working_block->read();

    //std::cout << working_block->getMetaData() << std::endl;

    DeflateBlock* raw_block = working_block.get();

    while(!raw_block->isFinalBlock())
    {
        break;
    }

    return true;
}

