// 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 <boost/python/tuple.hpp>
#include <boost/python/stl_iterator.hpp>
#include "bytes.hpp"
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/file_storage.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/torrent_status.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/peer_info.hpp>
#include "libtorrent/announce_entry.hpp"
#include <libtorrent/disk_interface.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

namespace {

	list url_seeds(torrent_handle& handle)
	{
		list ret;
		std::set<std::string> urls;
		{
			allow_threading_guard guard;
			urls = handle.url_seeds();
		}

		for (std::set<std::string>::iterator i(urls.begin()), end(urls.end()); i != end; ++i)
			ret.append(*i);
		return ret;
	}

#if TORRENT_ABI_VERSION < 4
	list http_seeds(torrent_handle& handle)
	{
		list ret;
		std::set<std::string> urls;
		{
			allow_threading_guard guard;
			urls = handle.http_seeds();
		}

		for (std::set<std::string>::iterator i(urls.begin()), end(urls.end()); i != end; ++i)
			ret.append(*i);
		return ret;
	}
#endif

	list piece_availability(torrent_handle& handle)
	{
		list ret;
		std::vector<int> avail;
		{
			allow_threading_guard guard;
			handle.piece_availability(avail);
		}

		for (auto const a : avail)
			ret.append(a);
		return ret;
	}

	list piece_priorities(torrent_handle& handle)
	{
		list ret;
		std::vector<download_priority_t> prio;
		{
			allow_threading_guard guard;
			prio = handle.get_piece_priorities();
		}

		for (auto const p : prio)
			ret.append(p);
		return ret;
	}

} // namespace unnamed

list file_progress(torrent_handle& handle, file_progress_flags_t const flags)
{
	std::vector<std::int64_t> p;

	{
		allow_threading_guard guard;
		std::shared_ptr<const torrent_info> ti = handle.torrent_file();
		if (ti)
		{
			p.reserve(ti->num_files());
			handle.file_progress(p, flags);
		}
	}

	list result;

	for (std::vector<std::int64_t>::iterator i(p.begin()), e(p.end()); i != e; ++i)
		result.append(*i);

	return result;
}

list get_peer_info(torrent_handle const& handle)
{
	std::vector<peer_info> pi;

	{
		allow_threading_guard guard;
		handle.get_peer_info(pi);
	}

	list result;

	for (std::vector<peer_info>::iterator i = pi.begin(); i != pi.end(); ++i)
		result.append(*i);

	return result;
}

namespace {
	template <typename T>
	T extract_fn(object o)
	{
		return boost::python::extract<T>(o);
	}
}

void prioritize_pieces(torrent_handle& info, object o)
{
	stl_input_iterator<object> begin(o), end;
	if (begin == end) return;

	// determine which overload should be selected. the one taking a list of
	// priorities or the one taking a list of piece -> priority mappings
	bool const is_piece_list =
		extract<std::pair<piece_index_t, download_priority_t>>(*begin).check();

	if (is_piece_list)
	{
		std::vector<std::pair<piece_index_t, download_priority_t>> piece_list;
		std::
			transform(begin, end, std::back_inserter(piece_list), &extract_fn<std::pair<piece_index_t, download_priority_t>>);
		info.prioritize_pieces(piece_list);
	}
	else
	{
		std::vector<download_priority_t> priority_vector;
		std::
			transform(begin, end, std::back_inserter(priority_vector), &extract_fn<download_priority_t>);
		info.prioritize_pieces(priority_vector);
	}
}

void prioritize_files(torrent_handle& info, object o)
{
	stl_input_iterator<download_priority_t> begin(o), end;
	info.prioritize_files(std::vector<download_priority_t>(begin, end));
}

list file_priorities(torrent_handle& handle)
{
	list ret;
	std::vector<download_priority_t> priorities = handle.get_file_priorities();

	for (auto const p : priorities)
		ret.append(p);

	return ret;
}

download_priority_t file_priority0(torrent_handle& h, file_index_t index)
{
	return h.file_priority(index);
}

void file_priority1(torrent_handle& h, file_index_t index, download_priority_t prio)
{
	return h.file_priority(index, prio);
}

void dict_to_announce_entry(dict d, announce_entry& ae)
{
	ae.url = extract<std::string>(d["url"]);
	if (d.has_key("tier")) ae.tier = extract<std::uint8_t>(d["tier"]);
	if (d.has_key("fail_limit")) ae.fail_limit = extract<std::uint8_t>(d["fail_limit"]);
}

void replace_trackers(torrent_handle& h, object trackers)
{
	object iter(trackers.attr("__iter__")());

	std::vector<announce_entry> result;

	for (;;)
	{
		handle<> entry(allow_null(PyIter_Next(iter.ptr())));

		if (entry == handle<>()) break;

		if (extract<announce_entry>(object(entry)).check())
		{
			result.push_back(extract<announce_entry>(object(entry)));
		}
		else
		{
			dict d;
			d = extract<dict>(object(entry));
			announce_entry ae;
			dict_to_announce_entry(d, ae);
			result.push_back(ae);
		}
	}

	allow_threading_guard guard;
	h.replace_trackers(result);
}

void add_tracker(torrent_handle& h, dict d)
{
	announce_entry ae;
	dict_to_announce_entry(d, ae);
	h.add_tracker(ae);
}

namespace {
	using std::chrono::system_clock;

	object to_ptime(time_point tpt)
	{
		object ret;
		if (tpt > min_time())
		{
			ret = long_(system_clock::to_time_t(
				system_clock::now() + duration_cast<system_clock::duration>(tpt - clock_type::now())
			));
		}
		return ret;
	}
}

list trackers(torrent_handle& h)
{
	list ret;
	std::vector<announce_entry> const trackers = h.trackers();
	for (std::vector<announce_entry>::const_iterator i = trackers.begin(), end(trackers.end());
		 i != end;
		 ++i)
	{
		dict d;
		d["url"] = i->url;
		d["trackerid"] = i->trackerid;
		d["tier"] = i->tier;
		d["fail_limit"] = i->fail_limit;
		d["source"] = i->source;
		d["verified"] = i->verified;

#if TORRENT_ABI_VERSION == 1
		if (!i->endpoints.empty())
		{
			announce_endpoint const& aep = i->endpoints.front();
			announce_infohash const& aih = aep.info_hashes[protocol_version::V1];
			d["message"] = aih.message;
			dict last_error;
			last_error["value"] = aih.last_error.value();
			last_error["category"] = aih.last_error.category().name();
			d["last_error"] = last_error;
			d["next_announce"] = to_ptime(aih.next_announce);
			d["min_announce"] = to_ptime(aih.min_announce);
			d["scrape_incomplete"] = aih.scrape_incomplete;
			d["scrape_complete"] = aih.scrape_complete;
			d["scrape_downloaded"] = aih.scrape_downloaded;
			d["fails"] = aih.fails;
			d["updating"] = aih.updating;
			d["start_sent"] = aih.start_sent;
			d["complete_sent"] = aih.complete_sent;
		}
		else
		{
			d["message"] = std::string();
			dict last_error;
			last_error["value"] = 0;
			last_error["category"] = "";
			d["last_error"] = last_error;
			d["next_announce"] = object();
			d["min_announce"] = object();
			d["scrape_incomplete"] = 0;
			d["scrape_complete"] = 0;
			d["scrape_downloaded"] = 0;
			d["fails"] = 0;
			d["updating"] = false;
			d["start_sent"] = false;
			d["complete_sent"] = false;
		}
#endif

		list aeps;
		for (auto const& aep : i->endpoints)
		{
			dict e;
			e["local_address"] = boost::python::make_tuple(
				aep.local_endpoint.address().to_string(), aep.local_endpoint.port()
			);

			list aihs;
			for (auto const& aih : aep.info_hashes)
			{
				dict i;
				i["message"] = aih.message;
				dict last_error;
				last_error["value"] = aih.last_error.value();
				last_error["category"] = aih.last_error.category().name();
				i["last_error"] = last_error;
				i["next_announce"] = to_ptime(aih.next_announce);
				i["min_announce"] = to_ptime(aih.min_announce);
				i["scrape_incomplete"] = aih.scrape_incomplete;
				i["scrape_complete"] = aih.scrape_complete;
				i["scrape_downloaded"] = aih.scrape_downloaded;
				i["fails"] = aih.fails;
				i["updating"] = aih.updating;
				i["start_sent"] = aih.start_sent;
				i["complete_sent"] = aih.complete_sent;
				aihs.append(std::move(i));
			}
			e["info_hashes"] = std::move(aihs);

#if TORRENT_ABI_VERSION <= 2
			announce_infohash const& aih = aep.info_hashes[protocol_version::V1];
			e["message"] = aih.message;
			dict last_error;
			last_error["value"] = aih.last_error.value();
			last_error["category"] = aih.last_error.category().name();
			e["last_error"] = last_error;
			e["next_announce"] = to_ptime(aih.next_announce);
			e["min_announce"] = to_ptime(aih.min_announce);
			e["scrape_incomplete"] = aih.scrape_incomplete;
			e["scrape_complete"] = aih.scrape_complete;
			e["scrape_downloaded"] = aih.scrape_downloaded;
			e["fails"] = aih.fails;
			e["updating"] = aih.updating;
			e["start_sent"] = aih.start_sent;
			e["complete_sent"] = aih.complete_sent;
#endif
			aeps.append(std::move(e));
		}
		d["endpoints"] = std::move(aeps);

#if TORRENT_ABI_VERSION == 1
		d["send_stats"] = i->send_stats;
#endif
		ret.append(std::move(d));
	}
	return ret;
}

list get_download_queue(torrent_handle& handle)
{
	list ret;

	std::vector<partial_piece_info> downloading;

	{
		allow_threading_guard guard;
		downloading = handle.get_download_queue();
	}

	for (std::vector<partial_piece_info>::iterator i = downloading.begin(), end(downloading.end());
		 i != end;
		 ++i)
	{
		dict partial_piece;
		partial_piece["piece_index"] = i->piece_index;
		partial_piece["blocks_in_piece"] = i->blocks_in_piece;
		list block_list;
		for (int k = 0; k < i->blocks_in_piece; ++k)
		{
			dict block_info;
			block_info["state"] = i->blocks[k].state;
			block_info["num_peers"] = i->blocks[k].num_peers;
			block_info["bytes_progress"] = i->blocks[k].bytes_progress;
			block_info["block_size"] = i->blocks[k].block_size;
			block_info["peer"] = boost::python::make_tuple(
				i->blocks[k].peer().address().to_string(), i->blocks[k].peer().port()
			);
			block_list.append(block_info);
		}
		partial_piece["blocks"] = block_list;

		ret.append(partial_piece);
	}

	return ret;
}

void set_metadata(torrent_handle& handle, std::string const& buf) { handle.set_metadata(buf); }

#if TORRENT_ABI_VERSION == 1

std::shared_ptr<const torrent_info> get_torrent_info(torrent_handle const& h)
{
	allow_threading_guard guard;
	return h.torrent_file();
}

#endif // TORRENT_ABI_VERSION

// TODO: this overload should probably be deprecated
void add_piece_str(
	torrent_handle& th, piece_index_t piece, char const* data, add_piece_flags_t const flags
)
{
	python_deprecated("add_piece with str is deprecated");
	th.add_piece(piece, data, flags);
}

void add_piece_bytes(
	torrent_handle& th, piece_index_t piece, bytes data, add_piece_flags_t const flags
)
{
	std::vector<char> buffer;
	buffer.reserve(data.arr.size());
	std::copy(data.arr.begin(), data.arr.end(), std::back_inserter(buffer));
	th.add_piece(piece, std::move(buffer), flags);
}

class dummy5
{};
class dummy
{};
class dummy4
{};
class dummy6
{};
class dummy7
{};
class dummy8
{};
class dummy15
{};
class dummy16
{};

void torrent_handle_set_max_uploads(torrent_handle& h, int i)
{
	if (i < 2 && i != -1)
	{
		PyErr_SetString(PyExc_ValueError, "max_uploads must be at least 2");
		throw_error_already_set();
	}
	h.set_max_uploads(i);
}

void torrent_handle_set_max_connections(torrent_handle& h, int i)
{
	if (i < 2 && i != -1)
	{
		PyErr_SetString(PyExc_ValueError, "max_connections must be at least 2");
		throw_error_already_set();
	}
	h.set_max_connections(i);
}

void rename_file0(torrent_handle& th, file_index_t index, string_view const& path)
{
	th.rename_file(index, std::string(path));
}

void rename_file1(torrent_handle& th, file_index_t index, bytes const& path)
{
	python_deprecated("rename_file with a bytes path is deprecated");
	th.rename_file(index, path.arr);
}

using by_value = return_value_policy<return_by_value>;
void bind_torrent_handle()
{
	// arguments are: number of seconds and tracker index
	void (torrent_handle::*force_reannounce0)(int, int, reannounce_flags_t) const =
		&torrent_handle::force_reannounce;
	void (torrent_handle::*force_reannounce1)(int, std::string const&, reannounce_flags_t) const =
		&torrent_handle::force_reannounce;
	void (torrent_handle::*force_reannounce3)(int, reannounce_flags_t) const =
		&torrent_handle::force_reannounce;
	void (torrent_handle::*scrape_tracker0)() const = &torrent_handle::scrape_tracker;
	void (torrent_handle::*scrape_tracker1)(int) const = &torrent_handle::scrape_tracker;
	void (torrent_handle::*scrape_tracker2)(std::string) const = &torrent_handle::scrape_tracker;

#if TORRENT_ABI_VERSION == 1
	bool (torrent_handle::*super_seeding0)() const = &torrent_handle::super_seeding;
	void (torrent_handle::*super_seeding1)(bool) const = &torrent_handle::super_seeding;
#endif
	void (torrent_handle::*set_flags0)(torrent_flags_t) const = &torrent_handle::set_flags;
	void (torrent_handle::*set_flags1)(torrent_flags_t, torrent_flags_t) const =
		&torrent_handle::set_flags;

	download_priority_t (torrent_handle::*piece_priority0)(piece_index_t) const =
		&torrent_handle::piece_priority;
	void (torrent_handle::*piece_priority1)(piece_index_t, download_priority_t) const =
		&torrent_handle::piece_priority;

	void (torrent_handle::*move_storage0)(std::string const&, lt::move_flags_t) const =
		&torrent_handle::move_storage;

	bool (torrent_handle::*need_save_resume_data0)() const = &torrent_handle::need_save_resume_data;
	bool (torrent_handle::*need_save_resume_data1)(resume_data_flags_t) const =
		&torrent_handle::need_save_resume_data;

	std::vector<open_file_state> (torrent_handle::*file_status0)() const =
		&torrent_handle::file_status;

#define _ allow_threads

	enum_<move_flags_t>("move_flags_t")
		.value("always_replace_files", move_flags_t::always_replace_files)
		.value("fail_if_exist", move_flags_t::fail_if_exist)
		.value("dont_replace", move_flags_t::dont_replace)
		.value("reset_save_path", move_flags_t::reset_save_path)
		.value("reset_save_path_unchecked", move_flags_t::reset_save_path_unchecked);

#if TORRENT_ABI_VERSION == 1
	enum_<deprecated_move_flags_t>("deprecated_move_flags_t")
		.value("always_replace_files", deprecated_move_flags_t::always_replace_files)
		.value("fail_if_exist", deprecated_move_flags_t::fail_if_exist)
		.value("dont_replace", deprecated_move_flags_t::dont_replace);
#endif

	{
		scope s =
			class_<torrent_handle>("torrent_handle")
				.def(self == self)
				.def(self != self)
				.def(self < self)
				.def("__hash__", (std::size_t(*)(torrent_handle const&)) & libtorrent::hash_value)
				.def("get_peer_info", get_peer_info)
				.def("post_peer_info", &torrent_handle::post_peer_info)
				.def("status", _(&torrent_handle::status), arg("flags") = 0xffffffff)
				.def("post_status", &torrent_handle::post_status, arg("flags") = 0xffffffff)
				.def("get_download_queue", get_download_queue)
				.def("post_download_queue", &torrent_handle::post_download_queue)
				.def("file_progress", file_progress, arg("flags") = file_progress_flags_t{})
				.def(
					"post_file_progress",
					&torrent_handle::post_file_progress,
					arg("flags") = file_progress_flags_t{}
				)
				.def("trackers", trackers)
				.def("post_trackers", &torrent_handle::post_trackers)
				.def("replace_trackers", replace_trackers)
				.def("add_tracker", add_tracker)
				.def("add_url_seed", _(&torrent_handle::add_url_seed))
				.def("remove_url_seed", _(&torrent_handle::remove_url_seed))
				.def("url_seeds", url_seeds)
#if TORRENT_ABI_VERSION < 4
				.def("add_http_seed", depr(&torrent_handle::add_http_seed))
				.def("remove_http_seed", depr(&torrent_handle::remove_http_seed))
				.def("http_seeds", depr(http_seeds))
#endif
				.def("torrent_file", _(&torrent_handle::torrent_file))
				.def("set_metadata", set_metadata)
				.def("is_valid", _(&torrent_handle::is_valid))
				.def("pause", _(&torrent_handle::pause), arg("flags") = 0)
				.def("resume", _(&torrent_handle::resume))
				.def("clear_error", _(&torrent_handle::clear_error))
				.def("queue_position", _(&torrent_handle::queue_position))
				.def("queue_position_up", _(&torrent_handle::queue_position_up))
				.def("queue_position_down", _(&torrent_handle::queue_position_down))
				.def("queue_position_top", _(&torrent_handle::queue_position_top))
				.def("queue_position_bottom", _(&torrent_handle::queue_position_bottom))

				.def("add_piece", add_piece_str)
				.def("add_piece", add_piece_bytes)
				.def("read_piece", _(&torrent_handle::read_piece))
				.def("have_piece", _(&torrent_handle::have_piece))
				.def(
					"set_piece_deadline",
					_(&torrent_handle::set_piece_deadline),
					(arg("index"), arg("deadline"), arg("flags") = 0)
				)
				.def(
					"reset_piece_deadline", _(&torrent_handle::reset_piece_deadline), (arg("index"))
				)
				.def(
					"clear_piece_deadlines",
					_(&torrent_handle::clear_piece_deadlines),
					(arg("index"))
				)
				.def("piece_availability", &piece_availability)
				.def("post_piece_availability", &torrent_handle::post_piece_availability)
				.def("piece_priority", _(piece_priority0))
				.def("piece_priority", _(piece_priority1))
				.def("prioritize_pieces", &prioritize_pieces)
				.def("get_piece_priorities", &piece_priorities)
				.def("prioritize_files", &prioritize_files)
				.def("get_file_priorities", &file_priorities)
				.def("file_priority", &file_priority0)
				.def("file_priority", &file_priority1)
				.def("file_status", _(file_status0))
				.def("save_resume_data", _(&torrent_handle::save_resume_data), arg("flags") = 0)
				.def("need_save_resume_data", _(need_save_resume_data0))
				.def("need_save_resume_data", _(need_save_resume_data1), arg("flags"))
				.def(
					"force_reannounce",
					_(force_reannounce0),
					(arg("seconds"), arg("tracker_idx"), arg("flags") = reannounce_flags_t{})
				)
				.def(
					"force_reannounce",
					_(force_reannounce1),
					(arg("seconds"), arg("url"), arg("flags") = reannounce_flags_t{})
				)
				.def(
					"force_reannounce",
					_(force_reannounce3),
					(arg("seconds") = 0, arg("flags") = reannounce_flags_t{})
				)
#ifndef TORRENT_DISABLE_DHT
				.def("force_dht_announce", _(&torrent_handle::force_dht_announce))
#endif
				.def("scrape_tracker", _(scrape_tracker0))
				.def("scrape_tracker", _(scrape_tracker1), (arg("index")))
				.def("scrape_tracker", _(scrape_tracker2), (arg("url")))
				.def("flush_cache", &torrent_handle::flush_cache)
				.def("set_upload_limit", _(&torrent_handle::set_upload_limit))
				.def("upload_limit", _(&torrent_handle::upload_limit))
				.def("set_download_limit", _(&torrent_handle::set_download_limit))
				.def("download_limit", _(&torrent_handle::download_limit))
				.def(
					"connect_peer",
					&torrent_handle::connect_peer,
					(arg("endpoint"), arg("source") = 0, arg("flags") = 0xd)
				)
				.def("set_max_uploads", &torrent_handle_set_max_uploads)
				.def("max_uploads", _(&torrent_handle::max_uploads))
				.def("set_max_connections", &torrent_handle_set_max_connections)
				.def("max_connections", _(&torrent_handle::max_connections))
				.def(
					"move_storage",
					_(move_storage0),
					(arg("path"), arg("flags") = move_flags_t::always_replace_files)
				)
				.def("info_hash", _(&torrent_handle::info_hash))
				.def("info_hashes", _(&torrent_handle::info_hashes))
				.def("force_recheck", _(&torrent_handle::force_recheck))
				.def("get_renamed_files", _(&torrent_handle::get_renamed_files))
				.def("rename_file", &rename_file0)
				.def("rename_file", &rename_file1)
				.def(
					"set_ssl_certificate_buffer",
					&torrent_handle::set_ssl_certificate_buffer,
					(arg("cert"), arg("private_key"), arg("dh_params"))
				)
				.def(
					"set_ssl_certificate",
					&torrent_handle::set_ssl_certificate,
					(arg("cert"), arg("private_key"), arg("dh_params"), arg("passphrase") = "")
				)
				.def("flags", _(&torrent_handle::flags))
				.def("set_flags", _(set_flags0))
				.def("set_flags", _(set_flags1))
				.def("unset_flags", _(&torrent_handle::unset_flags))
		// deprecated
#if TORRENT_ABI_VERSION == 1
				.def("piece_priorities", depr(&piece_priorities))
				.def("file_priorities", depr(&file_priorities))
				.def("stop_when_ready", depr(&torrent_handle::stop_when_ready))
				.def("super_seeding", depr(super_seeding1))
				.def("auto_managed", depr(&torrent_handle::auto_managed))
				.def("set_priority", depr(&torrent_handle::set_priority))
				.def("get_torrent_info", depr(&get_torrent_info))
				.def("super_seeding", depr(super_seeding0))
				.def("write_resume_data", depr(&torrent_handle::write_resume_data))
				.def("is_seed", depr(&torrent_handle::is_seed))
				.def("is_finished", depr(&torrent_handle::is_finished))
				.def("has_metadata", depr(&torrent_handle::has_metadata))
				.def("use_interface", depr(&torrent_handle::use_interface))
				.def("name", depr(&torrent_handle::name))
				.def("is_paused", depr(&torrent_handle::is_paused))
				.def("is_auto_managed", depr(&torrent_handle::is_auto_managed))
				.def("set_upload_mode", depr(&torrent_handle::set_upload_mode))
				.def("set_share_mode", depr(&torrent_handle::set_share_mode))
				.def("apply_ip_filter", depr(&torrent_handle::apply_ip_filter))
				.def("set_sequential_download", depr(&torrent_handle::set_sequential_download))
				.def("set_peer_upload_limit", depr(&torrent_handle::set_peer_upload_limit))
				.def("set_peer_download_limit", depr(&torrent_handle::set_peer_download_limit))
				.def("set_ratio", depr(&torrent_handle::set_ratio))
				.def("save_path", depr(&torrent_handle::save_path))
				.def("set_tracker_login", depr(&torrent_handle::set_tracker_login))
#endif
			;

		s.attr("ignore_min_interval") = torrent_handle::ignore_min_interval;
		s.attr("high_priority") = torrent_handle::high_priority;
		s.attr("overwrite_existing") = torrent_handle::overwrite_existing;
		s.attr("piece_granularity") = torrent_handle::piece_granularity;
		s.attr("graceful_pause") = torrent_handle::graceful_pause;
		s.attr("flush_disk_cache") = torrent_handle::flush_disk_cache;
		s.attr("save_info_dict") = torrent_handle::save_info_dict;
		s.attr("only_if_modified") = torrent_handle::only_if_modified;
		s.attr("alert_when_available") = torrent_handle::alert_when_available;
		s.attr("query_distributed_copies") = torrent_handle::query_distributed_copies;
		s.attr("query_accurate_download_counters") =
			torrent_handle::query_accurate_download_counters;
		s.attr("query_last_seen_complete") = torrent_handle::query_last_seen_complete;
		s.attr("query_pieces") = torrent_handle::query_pieces;
		s.attr("query_verified_pieces") = torrent_handle::query_verified_pieces;
		s.attr("query_renamed_files") = torrent_handle::query_renamed_files;
	}

	class_<open_file_state>("open_file_state")
		.add_property("file_index", make_getter((&open_file_state::file_index), by_value()))
		.def_readonly("last_use", &open_file_state::last_use)
		.def_readonly("open_mode", &open_file_state::open_mode);

	{
		scope s = class_<dummy>("file_open_mode");
		s.attr("read_only") = file_open_mode::read_only;
		s.attr("write_only") = file_open_mode::write_only;
		s.attr("read_write") = file_open_mode::read_write;
		s.attr("rw_mask") = file_open_mode::rw_mask;
		s.attr("sparse") = file_open_mode::sparse;
		s.attr("no_atime") = file_open_mode::no_atime;
		s.attr("random_access") = file_open_mode::random_access;
#if TORRENT_ABI_VERSION == 1
		s.attr("locked") = 0;
#endif
		s.attr("mmapped") = file_open_mode::mmapped;
	}

	{
		scope s = class_<dummy16>("file_progress_flags_t");
		s.attr("piece_granularity") = torrent_handle::piece_granularity;
	}

	{
		scope s = class_<dummy6>("add_piece_flags_t");
		s.attr("overwrite_existing") = torrent_handle::overwrite_existing;
	}

	{
		scope s = class_<dummy7>("pause_flags_t");
		s.attr("graceful_pause") = torrent_handle::graceful_pause;
	}

	{
		scope s = class_<dummy4>("save_resume_flags_t");
		s.attr("flush_disk_cache") = torrent_handle::flush_disk_cache;
		s.attr("save_info_dict") = torrent_handle::save_info_dict;
		s.attr("only_if_modified") = torrent_handle::only_if_modified;
	}

	{
		scope s = class_<dummy15>("reannounce_flags_t");
		s.attr("ignore_min_interval") = torrent_handle::ignore_min_interval;
	}

	{
		scope s = class_<dummy8>("deadline_flags_t");
		s.attr("alert_when_available") = torrent_handle::alert_when_available;
	}

	{
		scope s = class_<dummy5>("status_flags_t");
		s.attr("query_distributed_copies") = torrent_handle::query_distributed_copies;
		s.attr("query_accurate_download_counters") =
			torrent_handle::query_accurate_download_counters;
		s.attr("query_last_seen_complete") = torrent_handle::query_last_seen_complete;
		s.attr("query_pieces") = torrent_handle::query_pieces;
		s.attr("query_verified_pieces") = torrent_handle::query_verified_pieces;
		s.attr("query_renamed_files") = torrent_handle::query_renamed_files;
	}
}

#ifdef _MSC_VER
#pragma warning(pop)
#endif
