#include "wrap_cl.hpp"




namespace pyopencl {
#if PYOPENCL_CL_VERSION >= 0x1020
  py::object image_desc_dummy_getter(cl_image_desc &desc)
  {
    return py::none();
  }

  void image_desc_set_shape(cl_image_desc &desc, py::object py_shape)
  {
    COPY_PY_REGION_TRIPLE(shape);
    desc.image_width = shape[0];
    desc.image_height = shape[1];
    desc.image_depth = shape[2];
    desc.image_array_size = shape[2];
  }

  void image_desc_set_pitches(cl_image_desc &desc, py::object py_pitches)
  {
    COPY_PY_PITCH_TUPLE(pitches);
    desc.image_row_pitch = pitches[0];
    desc.image_slice_pitch = pitches[1];
  }

  void image_desc_set_buffer(cl_image_desc &desc, memory_object *mobj)
  {
    if (mobj)
      desc.buffer = mobj->data();
    else
      desc.buffer = 0;
  }

#endif
}




using namespace pyopencl;




void pyopencl_expose_part_2(py::module &m)
{
  // {{{ image

#if PYOPENCL_CL_VERSION >= 0x1020
  {
    typedef cl_image_desc cls;
    py::class_<cls>(m, "ImageDescriptor")
      .def(py::init<>())
      .def_readwrite("image_type", &cls::image_type)
      .def_property("shape", &image_desc_dummy_getter, image_desc_set_shape)
      .def_readwrite("array_size", &cls::image_array_size)
      .def_property("pitches", &image_desc_dummy_getter, image_desc_set_pitches)
      .def_readwrite("num_mip_levels", &cls::num_mip_levels)
      .def_readwrite("num_samples", &cls::num_samples)
      .def_property("buffer", &image_desc_dummy_getter, image_desc_set_buffer)
      ;
  }
#endif

  {
    typedef image cls;
    py::class_<cls, memory_object>(m, "Image", py::dynamic_attr())
      .def(
          py::init(
            [](
              context const &ctx,
              cl_mem_flags flags,
              cl_image_format const &fmt,
              py::sequence shape,
              py::sequence pitches,
              py::object buffer)
            {
              return create_image(ctx, flags, fmt, shape, pitches, buffer);
            }),
          py::arg("context"),
          py::arg("flags"),
          py::arg("format"),
          py::arg("shape")=py::none(),
          py::arg("pitches")=py::none(),
          py::arg("hostbuf")=py::none()
          )
#if PYOPENCL_CL_VERSION >= 0x1020
      .def(
          py::init(
            [](
              context const &ctx,
              cl_mem_flags flags,
              cl_image_format const &fmt,
              cl_image_desc &desc,
              py::object buffer)
            {
              return create_image_from_desc(ctx, flags, fmt, desc, buffer);
            }),
          py::arg("context"),
          py::arg("flags"),
          py::arg("format"),
          py::arg("desc"),
          py::arg("hostbuf")=py::none()
          )
#endif
      .DEF_SIMPLE_METHOD(get_image_info)
      ;
  }

  {
    typedef cl_image_format cls;
    py::class_<cls>(m, "ImageFormat")
      .def(
          py::init(
            [](cl_channel_order ord, cl_channel_type tp)
            {
              return make_image_format(ord, tp);
            }))
      .def_readwrite("channel_order", &cls::image_channel_order)
      .def_readwrite("channel_data_type", &cls::image_channel_data_type)
      .def_property_readonly("channel_count", &get_image_format_channel_count)
      .def_property_readonly("dtype_size", &get_image_format_channel_dtype_size)
      .def_property_readonly("itemsize", &get_image_format_item_size)
      ;
  }

  DEF_SIMPLE_FUNCTION(get_supported_image_formats);

  m.def("_enqueue_read_image", enqueue_read_image,
      py::arg("queue"),
      py::arg("mem"),
      py::arg("origin"),
      py::arg("region"),
      py::arg("hostbuf"),
      py::arg("row_pitch")=0,
      py::arg("slice_pitch")=0,
      py::arg("wait_for")=py::none(),
      py::arg("is_blocking")=true
      );
  m.def("_enqueue_write_image", enqueue_write_image,
      py::arg("queue"),
      py::arg("mem"),
      py::arg("origin"),
      py::arg("region"),
      py::arg("hostbuf"),
      py::arg("row_pitch")=0,
      py::arg("slice_pitch")=0,
      py::arg("wait_for")=py::none(),
      py::arg("is_blocking")=true
      );

  m.def("_enqueue_copy_image", enqueue_copy_image,
      py::arg("queue"),
      py::arg("src"),
      py::arg("dest"),
      py::arg("src_origin"),
      py::arg("dest_origin"),
      py::arg("region"),
      py::arg("wait_for")=py::none()
      );
  m.def("_enqueue_copy_image_to_buffer", enqueue_copy_image_to_buffer,
      py::arg("queue"),
      py::arg("src"),
      py::arg("dest"),
      py::arg("origin"),
      py::arg("region"),
      py::arg("offset"),
      py::arg("wait_for")=py::none()
      );
  m.def("_enqueue_copy_buffer_to_image", enqueue_copy_buffer_to_image,
      py::arg("queue"),
      py::arg("src"),
      py::arg("dest"),
      py::arg("offset"),
      py::arg("origin"),
      py::arg("region"),
      py::arg("wait_for")=py::none()
      );

#if PYOPENCL_CL_VERSION >= 0x1020
  m.def("enqueue_fill_image", enqueue_fill_image,
      py::arg("queue"),
      py::arg("mem"),
      py::arg("color"),
      py::arg("origin"),
      py::arg("region"),
      py::arg("wait_for")=py::none()
      );
#endif

  // }}}

  // {{{ memory_map
  {
    typedef memory_map cls;
    py::class_<cls>(m, "MemoryMap", py::dynamic_attr())
      .def("release", &cls::release,
          py::arg("queue").none(true)=nullptr,
          py::arg("wait_for")=py::none()
          )
      ;
  }

  m.def("enqueue_map_buffer", enqueue_map_buffer,
      py::arg("queue"),
      py::arg("buf"),
      py::arg("flags"),
      py::arg("offset"),
      py::arg("shape"),
      py::arg("dtype"),
      py::arg("order")="C",
      py::arg("strides")=py::none(),
      py::arg("wait_for")=py::none(),
      py::arg("is_blocking")=true);
  m.def("enqueue_map_image", enqueue_map_image,
      py::arg("queue"),
      py::arg("img"),
      py::arg("flags"),
      py::arg("origin"),
      py::arg("region"),
      py::arg("shape"),
      py::arg("dtype"),
      py::arg("order")="C",
      py::arg("strides")=py::none(),
      py::arg("wait_for")=py::none(),
      py::arg("is_blocking")=true);

  // }}}

  // {{{ svm

#if PYOPENCL_CL_VERSION >= 0x2000
  {
    typedef svm_arg_wrapper cls;
    py::class_<cls>(m, "SVM", py::dynamic_attr())
      .def(py::init<py::object>())
      ;
  }

  {
    typedef svm_allocation cls;
    py::class_<cls>(m, "SVMAllocation", py::dynamic_attr())
      .def(py::init<std::shared_ptr<context>, size_t, cl_uint, cl_svm_mem_flags>())
      .DEF_SIMPLE_METHOD(release)
      .DEF_SIMPLE_METHOD(enqueue_release)
      .def("_ptr_as_int", &cls::ptr_as_int)
      .def(py::self == py::self)
      .def(py::self != py::self)
      .def("__hash__", &cls::ptr_as_int)
      ;
  }

  m.def("_enqueue_svm_memcpyw", enqueue_svm_memcpy,
      py::arg("queue"),
      py::arg("is_blocking"),
      py::arg("dst"),
      py::arg("src"),
      py::arg("wait_for")=py::none()
      );

  m.def("_enqueue_svm_memfill", enqueue_svm_memfill,
      py::arg("queue"),
      py::arg("dst"),
      py::arg("pattern"),
      py::arg("byte_count")=py::none(),
      py::arg("wait_for")=py::none()
      );

  m.def("_enqueue_svm_map", enqueue_svm_map,
      py::arg("queue"),
      py::arg("is_blocking"),
      py::arg("flags"),
      py::arg("svm"),
      py::arg("wait_for")=py::none()
      );

  m.def("_enqueue_svm_unmap", enqueue_svm_unmap,
      py::arg("queue"),
      py::arg("svm"),
      py::arg("wait_for")=py::none()
      );
#endif

#if PYOPENCL_CL_VERSION >= 0x2010
  m.def("_enqueue_svm_migrate_mem", enqueue_svm_migratemem,
      py::arg("queue"),
      py::arg("svms"),
      py::arg("flags")=py::none(),
      py::arg("wait_for")=py::none()
      );
#endif

  // }}}

  // {{{ sampler
  {
    typedef sampler cls;
    py::class_<cls>(m, "Sampler", py::dynamic_attr())
      .def(py::init<context const &, bool, cl_addressing_mode, cl_filter_mode>())
      .DEF_SIMPLE_METHOD(get_info)
      .def(py::self == py::self)
      .def(py::self != py::self)
      .def("__hash__", &cls::hash)
      PYOPENCL_EXPOSE_TO_FROM_INT_PTR(cl_sampler)
      ;
  }

  // }}}

  // {{{ program
  {
    typedef program cls;
    py::enum_<cls::program_kind_type>(m, "program_kind")
      .value("UNKNOWN", cls::KND_UNKNOWN)
      .value("SOURCE", cls::KND_SOURCE)
      .value("BINARY", cls::KND_BINARY)
      ;

    py::class_<cls>(m, "_Program", py::dynamic_attr())
      .def(
          py::init(
            [](context &ctx, std::string const &src)
            {
              return create_program_with_source(ctx, src);
            }),
          py::arg("context"),
          py::arg("src"))
      .def(
          py::init(
            [](context &ctx, py::sequence devices, py::sequence binaries)
            {
              return create_program_with_binary(ctx, devices, binaries);
            }),
          py::arg("context"),
          py::arg("devices"),
          py::arg("binaries"))
#if (PYOPENCL_CL_VERSION >= 0x1020) && \
      ((PYOPENCL_CL_VERSION >= 0x1030) && defined(__APPLE__))
      .def_static("create_with_built_in_kernels",
          create_program_with_built_in_kernels,
          py::arg("context"),
          py::arg("devices"),
          py::arg("kernel_names"),
          py::return_value_policy<py::manage_new_object>())
#endif
      .DEF_SIMPLE_METHOD(kind)
      .DEF_SIMPLE_METHOD(get_info)
      .DEF_SIMPLE_METHOD(get_build_info)
      .def("_build", &cls::build,
          py::arg("options")="",
          py::arg("devices")=py::none())
#if PYOPENCL_CL_VERSION >= 0x1020
      .def("compile", &cls::compile,
          py::arg("options")="",
          py::arg("devices")=py::none(),
          py::arg("headers")=py::list())
      .def_static("link", &link_program,
          py::arg("context"),
          py::arg("programs"),
          py::arg("options")="",
          py::arg("devices")=py::none()
          )
#endif
      .def(py::self == py::self)
      .def(py::self != py::self)
      .def("__hash__", &cls::hash)
      .def("all_kernels", create_kernels_in_program)
      PYOPENCL_EXPOSE_TO_FROM_INT_PTR(cl_program)
      ;
  }

#if PYOPENCL_CL_VERSION >= 0x1020
  m.def("unload_platform_compiler", unload_platform_compiler);
#endif

  // }}}

  // {{{ kernel

  {
    typedef kernel cls;
    py::class_<cls>(m, "Kernel", py::dynamic_attr())
      .def(py::init<const program &, std::string const &>())
      .DEF_SIMPLE_METHOD(get_info)
      .DEF_SIMPLE_METHOD(get_work_group_info)
      .def("_set_arg_null", &cls::set_arg_null)
      .def("_set_arg_buf", &cls::set_arg_buf)
#if PYOPENCL_CL_VERSION >= 0x1020
      .def("_set_arg_svm", &cls::set_arg_svm)
#endif
      .DEF_SIMPLE_METHOD(set_arg)
#if PYOPENCL_CL_VERSION >= 0x1020
      .DEF_SIMPLE_METHOD(get_arg_info)
#endif
      .def(py::self == py::self)
      .def(py::self != py::self)
      .def("__hash__", &cls::hash)
      PYOPENCL_EXPOSE_TO_FROM_INT_PTR(cl_kernel)
      ;
  }

  {
    typedef local_memory cls;
    py::class_<cls>(m, "LocalMemory", py::dynamic_attr())
      .def(
          py::init<size_t>(),
          py::arg("size"))
      .def_property_readonly("size", &cls::size)
      ;
  }


  m.def("enqueue_nd_range_kernel", enqueue_nd_range_kernel,
      py::arg("queue"),
      py::arg("kernel"),
      py::arg("global_work_size"),
      py::arg("local_work_size"),
      py::arg("global_work_offset")=py::none(),
      py::arg("wait_for")=py::none(),
      py::arg("g_times_l")=false
      );

  // TODO: clEnqueueNativeKernel
  // }}}

  // {{{ GL interop
  DEF_SIMPLE_FUNCTION(have_gl);

#ifdef HAVE_GL

#ifdef __APPLE__
  DEF_SIMPLE_FUNCTION(get_apple_cgl_share_group);
#endif /* __APPLE__ */

  {
    typedef gl_buffer cls;
    py::class_<cls, memory_object>(m, "GLBuffer", py::dynamic_attr())
      .def(
          py::init(
            [](context &ctx, cl_mem_flags flags, GLuint bufobj)
            {
              return create_from_gl_buffer(ctx, flags, bufobj);
            }),
          py::arg("context"),
          py::arg("flags"),
          py::arg("bufobj"))
      .def("get_gl_object_info", get_gl_object_info)
      ;
  }

  {
    typedef gl_renderbuffer cls;
    py::class_<cls, memory_object>(m, "GLRenderBuffer", py::dynamic_attr())
      .def(
          py::init(
            [](context &ctx, cl_mem_flags flags, GLuint bufobj)
            {
              return create_from_gl_renderbuffer(ctx, flags, bufobj);
            }),
          py::arg("context"),
          py::arg("flags"),
          py::arg("bufobj"))
      .def("get_gl_object_info", get_gl_object_info)
      ;
  }

  {
    typedef gl_texture cls;
    py::class_<cls, image>(m, "GLTexture", py::dynamic_attr())
      .def(
          py::init(
            [](context &ctx, cl_mem_flags flags, GLenum texture_target,
              GLint miplevel, GLuint texture, unsigned dims)
            {
              return create_from_gl_texture(ctx, flags, texture_target, miplevel, texture, dims);
            }),
          py::arg("context"),
          py::arg("flags"),
          py::arg("texture_target"),
          py::arg("miplevel"),
          py::arg("texture"),
          py::arg("dims"))
      .def("get_gl_object_info", get_gl_object_info)
      .DEF_SIMPLE_METHOD(get_gl_texture_info)
      ;
  }

  m.def("enqueue_acquire_gl_objects", enqueue_acquire_gl_objects,
      py::arg("queue"),
      py::arg("mem_objects"),
      py::arg("wait_for")=py::none()
      );
  m.def("enqueue_release_gl_objects", enqueue_release_gl_objects,
      py::arg("queue"),
      py::arg("mem_objects"),
      py::arg("wait_for")=py::none()
      );

#if defined(cl_khr_gl_sharing) && (cl_khr_gl_sharing >= 1)
  m.def("get_gl_context_info_khr", get_gl_context_info_khr,
      py::arg("properties"),
      py::arg("param_name"),
      py::arg("platform")=py::none()
      );
#endif

#endif
  // }}}

  // {{{ CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD

  {
    typedef cl_device_topology_amd cls;
    py::class_<cls>(m, "DeviceTopologyAmd")
      .def(py::init(
            [](cl_char bus, cl_char device, cl_char function)
            {
              cl_device_topology_amd result;
              result.pcie.bus = bus;
              result.pcie.device = device;
              result.pcie.function = function;
              return result;
            }),
          py::arg("bus")=0,
          py::arg("device")=0,
          py::arg("function")=0)

      .def_property("type",
          [](cls &t) { return t.pcie.type; },
          [](cls &t, cl_uint val) { t.pcie.type = val; })

      .def_property("bus",
          [](cls &t) { return t.pcie.bus; },
          [](cls &t, cl_char val) { t.pcie.bus = val; })
      .def_property("device",
          [](cls &t) { return t.pcie.device; },
          [](cls &t, cl_char val) { t.pcie.device = val; })
      .def_property("function",
          [](cls &t) { return t.pcie.function; },
          [](cls &t, cl_char val) { t.pcie.function = val; })
      ;
  }

  // }}}
}


// vim: foldmethod=marker