// Copyright Daniel Wallin 2006. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include "boost_python.hpp"
#include <libtorrent/identify_client.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/bdecode.hpp>
#include "bytes.hpp"
#include "gil.hpp"

using namespace boost::python;
using namespace lt;

#ifdef _MSC_VER
#pragma warning(push)
// warning C4996: X: was declared deprecated
#pragma warning(disable : 4996)
#endif

struct bytes_to_python
{
	static PyObject* convert(bytes const& p)
	{
#if PY_MAJOR_VERSION >= 3
		PyObject* ret = PyBytes_FromStringAndSize(p.arr.c_str(), p.arr.size());
#else
		PyObject* ret = PyString_FromStringAndSize(p.arr.c_str(), p.arr.size());
#endif
		return ret;
	}
};

template <size_t N>
struct array_to_python
{
	static PyObject* convert(std::array<char, N> const& p)
	{
#if PY_MAJOR_VERSION >= 3
		PyObject* ret = PyBytes_FromStringAndSize(p.data(), p.size());
#else
		PyObject* ret = PyString_FromStringAndSize(p.data(), p.size());
#endif
		return ret;
	}
};

struct bytes_from_python
{
	bytes_from_python()
	{
		converter::registry::push_back(&convertible, &construct, type_id<bytes>());
	}

	static void* convertible(PyObject* x)
	{
#if PY_MAJOR_VERSION >= 3
		return (PyBytes_Check(x) || PyByteArray_Check(x)) ? x : nullptr;
#else
		return PyString_Check(x) ? x : nullptr;
#endif
	}

	static void construct(PyObject* x, converter::rvalue_from_python_stage1_data* data)
	{
#if PY_MAJOR_VERSION >= 3
		void* storage = ((converter::rvalue_from_python_storage<bytes>*)data)->storage.bytes;
		bytes* ret = new (storage) bytes();
		if (PyByteArray_Check(x))
		{
			ret->arr.resize(PyByteArray_Size(x));
			memcpy(&ret->arr[0], PyByteArray_AsString(x), ret->arr.size());
		}
		else
		{
			ret->arr.resize(PyBytes_Size(x));
			memcpy(&ret->arr[0], PyBytes_AsString(x), ret->arr.size());
		}
		data->convertible = storage;
#else
		void* storage = ((converter::rvalue_from_python_storage<bytes>*)data)->storage.bytes;
		bytes* ret = new (storage) bytes();
		ret->arr.resize(PyString_Size(x));
		memcpy(&ret->arr[0], PyString_AsString(x), ret->arr.size());
		data->convertible = storage;
#endif
	}
};

#if TORRENT_ABI_VERSION == 1
std::string identify_client_(const peer_id& p)
{
	python_deprecated("identify_client is deprecated");
	return lt::identify_client(p);
}

object client_fingerprint_(peer_id const& id)
{
	python_deprecated("client_fingerprint is deprecated");
	std::optional<fingerprint> result = client_fingerprint(id);
	return result ? object(*result) : object();
}
#endif

entry bdecode_(bytes const& data) { return bdecode(data.arr); }

bytes bencode_(entry const& e)
{
	bytes result;
	bencode(std::back_inserter(result.arr), e);
	return result;
}

void bind_utility()
{
	// TODO: it would be nice to install converters for sha1_hash as well
	to_python_converter<bytes, bytes_to_python>();
	to_python_converter<std::array<char, 32>, array_to_python<32>>();
	to_python_converter<std::array<char, 64>, array_to_python<64>>();
	bytes_from_python();

#if TORRENT_ABI_VERSION == 1
	def("identify_client", &identify_client_);
	def("client_fingerprint", &client_fingerprint_);
#endif
	def("bdecode", &bdecode_);
	def("bencode", &bencode_);
}

#ifdef _MSC_VER
#pragma warning(pop)
#endif
