/*
 * Copyright (C) 2017 Matthieu Gautier <mgautier@kymeria.fr>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
 * NON-INFRINGEMENT.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 */

#ifndef ZIM_WRITER_CLUSTER_H_
#define ZIM_WRITER_CLUSTER_H_

#include <zim/zim.h>
#include <zim/blob.h>
#include <iostream>
#include <vector>
#include <pthread.h>
#include <functional>

#include <zim/writer/article.h>
#include "../zim_types.h"

namespace zim {

namespace writer {

enum class DataType { plain, file };
struct Data {
  Data(zim::writer::DataType type, const std::string& value) :
    type(type), value(value) {}
  Data(zim::writer::DataType type, const char* data, zim::size_type size) :
    type(type), value(data, size) {}
  DataType type;
  std::string value;
};

using writer_t = std::function<void(const Blob& data)>;

class Cluster {
  typedef std::vector<offset_t> Offsets;
  typedef std::vector<Data> ClusterData;


  public:
    Cluster(CompressionType compression);
    virtual ~Cluster();

    void setCompression(CompressionType c) { compression = c; }
    CompressionType getCompression() const { return compression; }

    void addArticle(const zim::writer::Article* article);
    void addData(const char* data, zsize_t size);

    blob_index_t count() const  { return blob_index_t(blobOffsets.size() - 1); }
    zsize_t size() const;
    offset_t getOffset() const { return offset; }
    void setOffset(offset_t o) { offset = o; }
    bool is_extended() const { return isExtended; }
    void clear_data();
    void close();
    bool isClosed() const;

    void setClusterIndex(cluster_index_t idx) { index = idx; }
    cluster_index_t getClusterIndex() const { return index; }

    zsize_t getBlobSize(blob_index_t n) const
    { return zsize_t(blobOffsets[blob_index_type(n)+1].v - blobOffsets[blob_index_type(n)].v); }

    void write(int out_fd) const;

  protected:
    CompressionType compression;
    cluster_index_t index;
    bool isExtended;
    Offsets blobOffsets;
    offset_t offset;
    zsize_t _size;
    ClusterData _data;
    mutable Blob compressed_data;
    std::string tmp_filename;
    mutable pthread_mutex_t m_closedMutex;
    bool closed = false;

  private:
    void write_content(writer_t writer) const;
    template<typename OFFSET_TYPE>
    void write_offsets(writer_t writer) const;
    void write_data(writer_t writer) const;
    void compress();
    template<typename COMP_INFO>
    void _compress();
    void clear_raw_data();
    void clear_compressed_data();
};

};

};


#endif //ZIM_WRITER_CLUSTER_H_
