/*
 * Copyright 2017 Google LLC.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "deepvariant/python/clif_converters.h"

#include <memory>
#include <mutex>

// This has to go before numpy
#include <Python.h>

#include "deepvariant/pileup_image_native.h"
#include "numpy/arrayobject.h"
#include "numpy/ndarrayobject.h"
#include "clif/python/postconv.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/types.h"

namespace learning {
namespace genomics {
namespace deepvariant {

using ::tensorflow::string;

// We need to initialize the numpy C ARRAY API via a call to import_array():
// https://docs.scipy.org/doc/numpy-1.13.0/reference/c-api.array.html#importing-the-api
// Usually this is put in the module initialization function, but we cannot
// easily a call there as that function is generated by CLIF. Instead we use
// std::call_once to initialize this C++ upon the first call to our converter.
std::once_flag import_array_flag;

void call_import_array() {
  import_array();
}

PyObject* Clif_PyObjFrom(std::unique_ptr<ImageRow> img_row,
                         const clif::py::PostConv& pc) {
  // Initialize numpy C array API if needed.
  std::call_once(import_array_flag, call_import_array);
  if (!img_row) { Py_RETURN_NONE; }

  npy_intp dims[] { 1, img_row->Width(), img_row->num_channels };
  PyArrayObject* res = reinterpret_cast<PyArrayObject*>(
      PyArray_SimpleNew(3, dims, PyArray_UBYTE));
  CHECK(res != nullptr);
  unsigned char* data = reinterpret_cast<unsigned char*> PyArray_DATA(res);
  unsigned char* cur = data;
  for (int i = 0; i < img_row->Width(); i++) {
    *cur++ = img_row->base[i];
    *cur++ = img_row->base_quality[i];
    *cur++ = img_row->mapping_quality[i];
    *cur++ = img_row->on_positive_strand[i];
    *cur++ = img_row->supports_alt[i];
    *cur++ = img_row->matches_ref[i];
    if (img_row->sequencing_type_image) {
      *cur++ = img_row->sequencing_type[i];
    }
    // Current default num_channels is 6. If custom_pileup_image is set to true,
    // we add an additional channel, which encodes oplens for indels.
    if (img_row->custom_pileup_image) {
      *cur++ = img_row->op_length[i];
    }
  }
  return PyArray_Return(res);
}

}  // namespace deepvariant
}  // namespace genomics
}  // namespace learning
