// Copyright 2022, FOSS-VG Developers and Contributers
//
// Author(s):
//  Jocadbz
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html

#include "../lib/zlibutil.hpp" 

#include <bitset>
#include <iomanip>
#include <iostream>
#include <vector>
#include <tinyutf8/tinyutf8.h>
#include <zlib/zlib.h>
#include <cstring>

#include "../lib/cli.hpp"
#include "../lib/file.hpp"

#define EXIT_SUCCESS 0
#define EXIT_RUNTIME 1
#define EXIT_USAGE   2

#include <iostream>
#include <fstream>
#include <vector>
#include <zlib.h>

#define CHUNK_SIZE 16384 // Chunk size


std::vector<char> compressData(const char* data, int size) {
    z_stream zs;
    memset(&zs, 0, sizeof(zs));

    if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK)
        throw(std::runtime_error("deflateInit failed while compressing."));

    zs.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data));
    zs.avail_in = size;

    int ret;
    char outbuffer[CHUNK_SIZE];
    std::vector<char> compressedData;

    do {
        zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
        zs.avail_out = CHUNK_SIZE;

        ret = deflate(&zs, Z_FINISH);

        if (compressedData.size() < zs.total_out)
            compressedData.insert(compressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out);
    } while (ret == Z_OK);

    deflateEnd(&zs);

    if (ret != Z_STREAM_END)
        throw(std::runtime_error("Error while compressing: " + std::to_string(ret)));

    return compressedData;
}


std::vector<char> decompressData(const char* data, int size) {
    z_stream zs;
    memset(&zs, 0, sizeof(zs));

    if (inflateInit(&zs) != Z_OK)
        throw(std::runtime_error("inflateInit failed while decompressing."));

    zs.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data));
    zs.avail_in = size;

    int ret;
    char outbuffer[CHUNK_SIZE];
    std::vector<char> decompressedData;

    do {
        zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
        zs.avail_out = CHUNK_SIZE;

        ret = inflate(&zs, 0);

        if (decompressedData.size() < zs.total_out)
            decompressedData.insert(decompressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out);
    } while (ret == Z_OK);

    inflateEnd(&zs);

    if (ret != Z_STREAM_END)
        throw(std::runtime_error("Error while decompressing: " + std::to_string(ret)));

    return decompressedData;
}
