// -*- mode: c++ -*-
#ifndef HOMCLOUD_BOUNDARY_MAP_SERIALIZER_EXT_H
#define HOMCLOUD_BOUNDARY_MAP_SERIALIZER_EXT_H

#include <msgpack.hpp>
#include <string>
#include <phat/helpers/misc.h>

namespace boundary_map_serializer_ext {

struct BMSerializer {
  PyObject_HEAD

  msgpack::sbuffer* boundary_map_buf_;
  msgpack::packer<msgpack::sbuffer>* bmap_packer_;
  int num_cols_;
  phat::index next_index_;
  
  void SetUp(int num_cols, std::string dstyle="none") {
    if (dstyle == "none") {
      boundary_map_buf_ = nullptr;
      bmap_packer_ = nullptr;
    } else {
      num_cols_ = num_cols;
      next_index_ = 0;
      boundary_map_buf_ = new msgpack::sbuffer();
      bmap_packer_ = new msgpack::packer<msgpack::sbuffer>(boundary_map_buf_);
      bmap_packer_->pack_map(3);
      bmap_packer_->pack(std::string("chunktype"));
      bmap_packer_->pack(std::string("boundary_map"));
      bmap_packer_->pack(std::string("type"));
      bmap_packer_->pack(dstyle);
      bmap_packer_->pack(std::string("map"));
      bmap_packer_->pack_array(num_cols);
    }
  }

  void TearDown() {
    delete bmap_packer_; bmap_packer_ = nullptr;
    delete boundary_map_buf_; boundary_map_buf_ = nullptr;
  }

  bool SetDimCol(phat::index idx, phat::dimension dim, const phat::column* col) {
    if (!bmap_packer_)
      return true;

    if (idx == next_index_) {
      next_index_++;
    } else {
      PyErr_SetString(PyExc_ValueError, "Cell id must be sequencial");
      return false;
    }

    if (idx >= num_cols_) {
      PyErr_SetString(PyExc_ValueError, "Too many cells are added");
      return false;
    }

    bmap_packer_->pack_array(2);
    bmap_packer_->pack<int>(dim);
    bmap_packer_->pack_array(col->size());
    for (phat::index element : *col)
      bmap_packer_->pack(element);

    return true;
  }
};

#ifdef HOMCLOUD_BOUNDARY_MAP_SERIALIZER_EXT_MODULE
static void** API;

static int import() {
  if (!PyImport_ImportModule("homcloud.boundary_map_serializer_ext")) 
    return -1;

  API = (void**)PyCapsule_Import("homcloud.boundary_map_serializer_ext._C_API", 0);
  return API ? 0 : -1;
}

static PyTypeObject* BMSerializerType() {
  return reinterpret_cast<PyTypeObject*>(API[0]);
}

static BMSerializer* BMSerializerNew(int num_cols, std::string dstyle) {
  BMSerializer* serializer = PyObject_New(BMSerializer, BMSerializerType());
  if (!serializer) return nullptr;
  serializer->SetUp(num_cols, dstyle);

  return serializer;
}
#endif

} // namespace boundary_map_serializer_ext

#endif // HOMCLOUD_BOUNDARY_MAP_SERIALIZER_EXT_H
