text: // Chemfiles, a modern library for chemistry file reading and writing
  // Copyright (C) Guillaume Fraux and contributors -- BSD license
  
  #include <fstream>
  
  #include "catch.hpp"
  #include "helpers.hpp"
  #include "chemfiles/File.hpp"
  #include "chemfiles/files/Bz2File.hpp"
  #include "chemfiles/Error.hpp"
  using namespace chemfiles;
  
  static void check_file(TextFile& file) {
      CHECK(file.readline() == "297");
      CHECK(file.readline() == " generated by VMD");
      CHECK(file.readline() == "  O          0.417219        8.303366       11.737172");
  
      file.rewind();
      CHECK(file.readline() == "297");
      CHECK(file.readline() == " generated by VMD");
  
      // Count lines
      file.rewind();
      size_t lines = 0;
      while (!file.eof()) {
          file.readline();
          lines++;
      }
  
      CHECK(lines == 29901);
      CHECK(file.tellpos() == 1606000);
      CHECK(file.eof());
  
      file.seekpos(23804);
      CHECK(file.readline() == "  H          8.479585        0.521128       11.514298");
  }
  
  TEST_CASE("Read a text file") {
      SECTION("Read at different compressions levels") {
          auto file_6 = TextFile("data/xyz/water.6.xyz.bz2", File::READ, File::BZIP2);
          check_file(file_6);
  
          auto file_9 = TextFile("data/xyz/water.9.xyz.bz2", File::READ, File::BZIP2);
          check_file(file_9);
      }
  
      SECTION("Constructor errors") {
          CHECK_THROWS_WITH(
              Bz2File("not existing", File::READ),
              "could not open the file at 'not existing'"
          );
  
          CHECK_THROWS_WITH(
              Bz2File("data/xyz/water.9.xyz.bz2", File::APPEND),
              "appending (open mode 'a') is not supported with bzip2 files"
          );
      }
  
      SECTION("Lines offsets") {
          // Compare offset with uncompressed file
          auto file = TextFile("data/xyz/water.xyz", File::READ, File::DEFAULT);
          auto positions = std::vector<uint64_t>();
          while (!file.eof()) {
              positions.push_back(file.tellpos());
              file.readline();
          }
  
          auto bz2_file = TextFile("data/xyz/water.6.xyz.bz2", File::READ, File::BZIP2);
          for (size_t i=0; i<positions.size(); i++) {
              CHECK(positions[i] == bz2_file.tellpos());
              bz2_file.readline();
          }
          CHECK(bz2_file.eof());
      }
  }
  
  TEST_CASE("Write a bzip2 file") {
      auto filename = NamedTempPath(".bz2");
  
      {
          TextFile file(filename, File::WRITE, File::BZIP2);
          file.print("Test\n");
          file.print("{}\n", 5467);
          CHECK(file.tellpos() == 10);
      }
  
      std::ifstream checking(filename, std::ios::binary);
      REQUIRE(checking.is_open());
      checking.seekg(0, std::ios::end);
      auto size = static_cast<size_t>(checking.tellg());
      checking.seekg(0, std::ios::beg);
  
      auto content = std::vector<uint8_t>(size);
      checking.read(reinterpret_cast<char*>(content.data()), static_cast<std::streamsize>(size));
  
      auto expected = std::vector<uint8_t> {
          'B', 'Z', 'h', 0x36, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0xde, 0x45, 0xac,
          0xea, 0x00, 0x00, 0x03, 0x4b, 0x80, 0x00, 0x10, 0x07, 0x80, 0x04, 0x00,
          0x02, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x31, 0x06, 0x4c, 0x41, 0x01, 0x93,
          0x0c, 0x8e, 0x07, 0x7d, 0x38, 0x2e, 0xe4, 0x8a, 0x70, 0xa1, 0x21, 0xbc,
          0x8b, 0x59, 0xd4
      };
      CHECK(content == expected);
  
      // Decompress and compare
      TextFile file(filename, File::READ, File::BZIP2);
      CHECK(file.readline() == "Test");
      CHECK(file.readline() == "5467");
  }
  
  TEST_CASE("In-memory decompression") {
      auto content = std::vector<uint8_t> {
          'B', 'Z', 'h', 0x36, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0xde, 0x45, 0xac,
          0xea, 0x00, 0x00, 0x03, 0x4b, 0x80, 0x00, 0x10, 0x07, 0x80, 0x04, 0x00,
          0x02, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x31, 0x06, 0x4c, 0x41, 0x01, 0x93,
          0x0c, 0x8e, 0x07, 0x7d, 0x38, 0x2e, 0xe4, 0x8a, 0x70, 0xa1, 0x21, 0xbc,
          0x8b, 0x59, 0xd4
      };
  
      auto decompressed = bz2inflate_in_place(reinterpret_cast<const char*>(content.data()), content.size());
      CHECK(std::string(decompressed.begin(), decompressed.end()) == "Test\n5467\n");
  
      content[23] = 0x00;
      CHECK_THROWS_WITH(
          bz2inflate_in_place(reinterpret_cast<const char*>(content.data()), content.size()),
          "bzip2: corrupted file (code: -4)"
      );
  
      content[0] = 0x00;
      CHECK_THROWS_WITH(
          bz2inflate_in_place(reinterpret_cast<const char*>(content.data()), content.size()),
          "bzip2: this file do not seems to be a bz2 file (code: -5)"
      );
  }
  