#pragma once
#include "a83/string-util.hpp"
#include "common.hpp"
#include "omp_mutex.hpp"
#include "data_t.hpp"
#include <fstream>
extern "C"
{
#include "zip/zip.h"
}
namespace _archive_ {
  omp::mutex mu_read = {}; ///< блокировка чтения в многопотоке
}
//! загрузка данных из zip архива
class Archive {
public:
  char* strm_buf = nullptr; ///< нужен для чтения файла из потока
  bool ready = false;
  Archive() = default;
  ~Archive() { close(); }
  Archive(CN(a83::Str) fname) { open(fname); }
  //!@param stream_mode для фикса чтения из рус-директорий
  void open(a83::Str fname, bool stream_mode = true) {
    if (ready)
      close();
    a83::log("Archive.open \"" + fname + "\"");
    if (stream_mode) {
      auto strm = std::fstream(fname, std::ios::in | std::ios::binary);
      // определяет размер файла в байтах:
      strm.seekp(0, std::ios::end);
      cauto strm_size = strm.tellp();
      strm.seekp(0, std::ios::beg);
      strm_buf = new char[strm_size];
      strm.read(strm_buf, strm_size);
      zip = zip_stream_open(strm_buf, strm_size, 0, 'r');
    } else
      zip = zip_open(fname.c_str(), 0, 'r');
    IFERROR( !zip, "Archive.read: zip_open ret 0");
    ready = true;
  } // open
  //! получение данных из архива
  data_t get_file(a83::Str fname) const {
    std::lock_guard lock(_archive_::mu_read);
    IFERROR( !ready, "Archive.get_file: !ready");
    a83::replace_all(fname, "\\", "/");
    a83::replace_all(fname, "//", "/");
    a83::delete_all(fname, "./");
    if (cauto ret = zip_entry_open(zip, fname.c_str()); ret < 0)
      a83::error(a83::Str("Archive.get_file.zip_entry_open: ")
        + zip_strerror(ret) + " Entry name \"" + fname + "\"");
    data_t out;
    if (out.size = zip_entry_size(zip); out.size < 1)
      a83::error("Archive.get_file: data size < 1");
    out.data = new uint8_t[out.size];
    if (cauto ret = zip_entry_noallocread(zip, out.data, out.size); ret < 0)
      a83::error(a83::Str("Archive.get_file.zip_entry_noallocread: ")
        + zip_strerror(ret));
    if (cauto ret = zip_entry_close(zip); ret < 0)
      a83::error(a83::Str("Archive.get_file.zip_entry_close: ")
        + zip_strerror(ret));
    return out;
  } // get_file
  void close() { 
    if (zip) {
      a83::log("Archive.close");
      zip_close(zip);
    }
    zip = 0;
    ready = false;
    if (strm_buf) {
      delete[] strm_buf;
      strm_buf = nullptr;
    }
  } // close
private:
  zip_t* zip = 0;
  nocopy(Archive);
}; // Archive