// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "puffin/src/sample_generator.h"

#include <malloc.h>
#include <zlib.h>

#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>

#include "puffin/src/bit_reader.h"
#include "puffin/src/puff_writer.h"
#include "puffin/src/set_errors.h"

namespace puffin {
namespace sample_generator {

using std::cerr;
using std::cout;
using std::endl;
using std::string;

bool CompressToDeflate(const Buffer& uncomp,
                      Buffer* comp,
                      int compression,
                      int strategy) {
  z_stream stream;
  stream.next_in = (z_const Bytef*)uncomp.data();
  stream.avail_in = static_cast<unsigned int>(uncomp.size());
  stream.next_out = comp->data();
  stream.avail_out = comp->size();

  stream.zalloc = nullptr;
  stream.zfree = nullptr;
  stream.opaque = nullptr;

  TEST_AND_RETURN_FALSE(
      Z_OK == deflateInit2(&stream, compression, Z_DEFLATED, -15, 8, strategy));
  // If there was not enough output available return error.
  TEST_AND_RETURN_FALSE(Z_STREAM_END == deflate(&stream, Z_FINISH));
  TEST_AND_RETURN_FALSE(Z_OK == deflateEnd(&stream));
  comp->resize(stream.total_out);
  return true;
}

// Print an array into hex-format to the output. This can be used to create
// static arrays for unit testing of the puffer/huffer.
void PrintArray(const string& name, const Buffer& array) {
  cout << "const Buffer " << name << " = {" << endl << " ";
  for (size_t idx = 0; idx < array.size(); idx++) {
    cout << " 0x" << std::hex << std::uppercase << std::setfill('0')
         << std::setw(2) << uint(array[idx]);
    if (idx == array.size() - 1) {
      cout << std::dec << "};" << endl;
      return;
    }
    cout << ",";
    if ((idx + 1) % 12 == 0) {
      cout << endl << " ";
    }
  }
}

bool PrintSample(Puffer* puffer,
                 int compression,
                 int strategy,
                 const Buffer& original) {
  PrintArray("original", original);

  Buffer comp(original.size() * 4 + 10);
  TEST_AND_RETURN_FALSE(
      CompressToDeflate(original, &comp, compression, strategy));
  PrintArray("compressed", comp);

  Buffer puff(original.size() * 3 + 10);
  puffin::Error error;

  BufferBitReader bit_reader(comp.data(), comp.size());
  BufferPuffWriter puff_writer(puff.data(), puff.size());
  TEST_AND_RETURN_FALSE(
      puffer->PuffDeflate(&bit_reader, &puff_writer, nullptr, &error));
  TEST_AND_RETURN_FALSE(comp.size() == bit_reader.Offset());

  puff.resize(puff_writer.Size());
  PrintArray("puffed", puff);
  return true;
}

}  // namespace sample_generator
}  // namespace puffin