/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once

#include "base/flat_set.h"

class RPCError {
public:
	RPCError(const MTPrpcError &error);

	int32 code() const {
		return _code;
	}

	const QString &type() const {
		return _type;
	}

	const QString &description() const {
		return _description;
	}

	enum {
		NoError,
		TimeoutError
	};

	static RPCError Local(const QString &type, const QString &description) {
		return MTP_rpc_error(
			MTP_int(0),
			MTP_bytes(
			("CLIENT_"
				+ type
				+ (description.length()
					? (": " + description)
					: QString())).toUtf8()));
	}

private:
	int32 _code;
	QString _type, _description;

};

namespace MTP {

inline bool isFloodError(const RPCError &error) {
	return error.type().startsWith(qstr("FLOOD_WAIT_"));
}

inline bool isTemporaryError(const RPCError &error) {
	return error.code() < 0 || error.code() >= 500 || isFloodError(error);
}

inline bool isDefaultHandledError(const RPCError &error) {
	return isTemporaryError(error);
}

} // namespace MTP

class RPCAbstractDoneHandler { // abstract done
public:
	[[nodiscard]] virtual bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) = 0;
	virtual ~RPCAbstractDoneHandler() {
	}

};
using RPCDoneHandlerPtr = std::shared_ptr<RPCAbstractDoneHandler>;

class RPCAbstractFailHandler { // abstract fail
public:
	virtual bool operator()(mtpRequestId requestId, const RPCError &e) = 0;
	virtual ~RPCAbstractFailHandler() {
	}
};
using RPCFailHandlerPtr = std::shared_ptr<RPCAbstractFailHandler>;

struct RPCResponseHandler {
	RPCResponseHandler() = default;
	RPCResponseHandler(RPCDoneHandlerPtr &&done, RPCFailHandlerPtr &&fail)
	: onDone(std::move(done))
	, onFail(std::move(fail)) {
	}

	RPCDoneHandlerPtr onDone;
	RPCFailHandlerPtr onFail;

};

class RPCDoneHandlerBare : public RPCAbstractDoneHandler { // done(from, end)
	using CallbackType = bool (*)(const mtpPrime *, const mtpPrime *);

public:
	RPCDoneHandlerBare(CallbackType onDone) : _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		return (*_onDone)(from, end);
	}

private:
	CallbackType _onDone;

};

class RPCDoneHandlerBareReq : public RPCAbstractDoneHandler { // done(from, end, req_id)
	using CallbackType = bool (*)(const mtpPrime *, const mtpPrime *, mtpRequestId);

public:
	RPCDoneHandlerBareReq(CallbackType onDone) : _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		return (*_onDone)(from, end, requestId);
	}

private:
	CallbackType _onDone;

};

template <typename TReturn, typename TResponse>
class RPCDoneHandlerPlain : public RPCAbstractDoneHandler { // done(result)
	using CallbackType = TReturn (*)(const TResponse &);

public:
	RPCDoneHandlerPlain(CallbackType onDone) : _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		auto response = TResponse();
		if (!response.read(from, end)) {
			return false;
		}
		(*_onDone)(std::move(response));
		return true;
	}

private:
	CallbackType _onDone;

};

template <typename TReturn, typename TResponse>
class RPCDoneHandlerReq : public RPCAbstractDoneHandler { // done(result, req_id)
	using CallbackType = TReturn (*)(const TResponse &, mtpRequestId);

public:
	RPCDoneHandlerReq(CallbackType onDone) : _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		auto response = TResponse();
		if (!response.read(from, end)) {
			return false;
		}
		(*_onDone)(std::move(response), requestId);
		return true;
	}

private:
	CallbackType _onDone;

};

template <typename TReturn>
class RPCDoneHandlerNo : public RPCAbstractDoneHandler { // done()
	using CallbackType = TReturn (*)();

public:
	RPCDoneHandlerNo(CallbackType onDone) : _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		(*_onDone)();
		return true;
	}

private:
	CallbackType _onDone;

};

template <typename TReturn>
class RPCDoneHandlerNoReq : public RPCAbstractDoneHandler { // done(req_id)
	using CallbackType = TReturn (*)(mtpRequestId);

public:
	RPCDoneHandlerNoReq(CallbackType onDone) : _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		(*_onDone)(requestId);
		return true;
	}

private:
	CallbackType _onDone;

};

class RPCFailHandlerPlain : public RPCAbstractFailHandler { // fail(error)
	using CallbackType = bool (*)(const RPCError &);

public:
	RPCFailHandlerPlain(CallbackType onFail) : _onFail(onFail) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return (*_onFail)(e);
	}

private:
	CallbackType _onFail;

};

class RPCFailHandlerReq : public RPCAbstractFailHandler { // fail(error, req_id)
	using CallbackType = bool (*)(const RPCError &, mtpRequestId);

public:
	RPCFailHandlerReq(CallbackType onFail) : _onFail(onFail) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return (*_onFail)(e, requestId);
	}

private:
	CallbackType _onFail;

};

class RPCFailHandlerNo : public RPCAbstractFailHandler { // fail()
	using CallbackType = bool (*)();

public:
	RPCFailHandlerNo(CallbackType onFail) : _onFail(onFail) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return (*_onFail)();
	}

private:
	CallbackType _onFail;

};

class RPCFailHandlerNoReq : public RPCAbstractFailHandler { // fail(req_id)
	using CallbackType = bool (*)(mtpRequestId);

public:
	RPCFailHandlerNoReq(CallbackType onFail) : _onFail(onFail) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return (*_onFail)(requestId);
	}

private:
	CallbackType _onFail;

};

struct RPCCallbackClear {
	RPCCallbackClear(mtpRequestId id, int32 code = RPCError::NoError)
	: requestId(id)
	, errorCode(code) {
	}

	mtpRequestId requestId = 0;
	int32 errorCode = 0;

};

inline RPCDoneHandlerPtr rpcDone(bool (*onDone)(const mtpPrime *, const mtpPrime *)) { // done(from, end)
	return RPCDoneHandlerPtr(new RPCDoneHandlerBare(onDone));
}

inline RPCDoneHandlerPtr rpcDone(bool (*onDone)(const mtpPrime *, const mtpPrime *, mtpRequestId)) { // done(from, end, req_id)
	return RPCDoneHandlerPtr(new RPCDoneHandlerBareReq(onDone));
}

template <typename TReturn, typename TResponse>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(const TResponse &)) { // done(result)
	return RPCDoneHandlerPtr(new RPCDoneHandlerPlain<TReturn, TResponse>(onDone));
}

template <typename TReturn, typename TResponse>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(const TResponse &, mtpRequestId)) { // done(result, req_id)
	return RPCDoneHandlerPtr(new RPCDoneHandlerReq<TReturn, TResponse>(onDone));
}

template <typename TReturn>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)()) { // done()
	return RPCDoneHandlerPtr(new RPCDoneHandlerNo<TReturn>(onDone));
}

template <typename TReturn>
inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(mtpRequestId)) { // done(req_id)
	return RPCDoneHandlerPtr(new RPCDoneHandlerNoReq<TReturn>(onDone));
}

inline RPCFailHandlerPtr rpcFail(bool (*onFail)(const RPCError &)) { // fail(error)
	return RPCFailHandlerPtr(new RPCFailHandlerPlain(onFail));
}

inline RPCFailHandlerPtr rpcFail(bool (*onFail)(const RPCError &, mtpRequestId)) { // fail(error, req_id)
	return RPCFailHandlerPtr(new RPCFailHandlerReq(onFail));
}

inline RPCFailHandlerPtr rpcFail(bool (*onFail)()) { // fail()
	return RPCFailHandlerPtr(new RPCFailHandlerNo(onFail));
}

inline RPCFailHandlerPtr rpcFail(bool (*onFail)(mtpRequestId)) { // fail(req_id)
	return RPCFailHandlerPtr(new RPCFailHandlerNoReq(onFail));
}

class RPCSender;

class RPCOwnedDoneHandler : public RPCAbstractDoneHandler { // abstract done
public:
	RPCOwnedDoneHandler(RPCSender *owner);
	void invalidate() {
		_owner = nullptr;
	}
	~RPCOwnedDoneHandler();

protected:
	RPCSender *_owner = nullptr;

};

class RPCOwnedFailHandler : public RPCAbstractFailHandler { // abstract fail
public:
	RPCOwnedFailHandler(RPCSender *owner);
	void invalidate() {
		_owner = nullptr;
	}
	~RPCOwnedFailHandler();

protected:
	RPCSender *_owner = nullptr;

};

template <typename TReceiver>
class RPCDoneHandlerBareOwned : public RPCOwnedDoneHandler { // done(from, end)
	using CallbackType = bool (TReceiver::*)(const mtpPrime *, const mtpPrime *);

public:
	RPCDoneHandlerBareOwned(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		return _owner
			? (static_cast<TReceiver*>(_owner)->*_onDone)(from, end)
			: true;
	}

private:
	CallbackType _onDone;

};

template <typename TReceiver>
class RPCDoneHandlerBareOwnedReq : public RPCOwnedDoneHandler { // done(from, end, req_id)
	using CallbackType = bool (TReceiver::*)(const mtpPrime *, const mtpPrime *, mtpRequestId);

public:
	RPCDoneHandlerBareOwnedReq(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		return _owner
			? (static_cast<TReceiver*>(_owner)->*_onDone)(from, end, requestId)
			: true;
	}

private:
	CallbackType _onDone;

};

template <typename TReturn, typename TReceiver, typename TResponse>
class RPCDoneHandlerOwned : public RPCOwnedDoneHandler { // done(result)
	using CallbackType = TReturn (TReceiver::*)(const TResponse &);

public:
	RPCDoneHandlerOwned(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		auto response = TResponse();
		if (!response.read(from, end)) {
			return false;
		}
		if (_owner) {
			(static_cast<TReceiver*>(_owner)->*_onDone)(std::move(response));
		}
		return true;
	}

private:
	CallbackType _onDone;

};

template <typename TReturn, typename TReceiver, typename TResponse>
class RPCDoneHandlerOwnedReq : public RPCOwnedDoneHandler { // done(result, req_id)
	using CallbackType = TReturn (TReceiver::*)(const TResponse &, mtpRequestId);

public:
	RPCDoneHandlerOwnedReq(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		auto response = TResponse();
		if (!response.read(from, end)) {
			return false;
		}
		if (_owner) {
			(static_cast<TReceiver*>(_owner)->*_onDone)(std::move(response), requestId);
		}
		return true;
	}

private:
	CallbackType _onDone;

};

template <typename TReturn, typename TReceiver>
class RPCDoneHandlerOwnedNo : public RPCOwnedDoneHandler { // done()
	using CallbackType = TReturn (TReceiver::*)();

public:
	RPCDoneHandlerOwnedNo(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)();
		return true;
	}

private:
	CallbackType _onDone;

};

template <typename TReturn, typename TReceiver>
class RPCDoneHandlerOwnedNoReq : public RPCOwnedDoneHandler { // done(req_id)
	using CallbackType = TReturn (TReceiver::*)(mtpRequestId);

public:
	RPCDoneHandlerOwnedNoReq(TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(requestId);
		return true;
	}

private:
	CallbackType _onDone;

};

template <typename T, typename TReceiver>
class RPCBindedDoneHandlerBareOwned : public RPCOwnedDoneHandler { // done(b, from, end)
	using CallbackType = bool (TReceiver::*)(T, const mtpPrime *, const mtpPrime *);

public:
	RPCBindedDoneHandlerBareOwned(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		return _owner
			? (static_cast<TReceiver*>(_owner)->*_onDone)(_b, from, end)
			: true;
	}

private:
	CallbackType _onDone;
	T _b;

};

template <typename T, typename TReceiver>
class RPCBindedDoneHandlerBareOwnedReq : public RPCOwnedDoneHandler { // done(b, from, end, req_id)
	using CallbackType = bool (TReceiver::*)(T, const mtpPrime *, const mtpPrime *, mtpRequestId);

public:
	RPCBindedDoneHandlerBareOwnedReq(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		return _owner
			? (static_cast<TReceiver*>(_owner)->*_onDone)(_b, from, end, requestId)
			: true;
	}

private:
	CallbackType _onDone;
	T _b;

};

template <typename T, typename TReturn, typename TReceiver, typename TResponse>
class RPCBindedDoneHandlerOwned : public RPCOwnedDoneHandler { // done(b, result)
	using CallbackType = TReturn (TReceiver::*)(T, const TResponse &);

public:
	RPCBindedDoneHandlerOwned(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone), _b(b) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		auto response = TResponse();
		if (!response.read(from, end)) {
			return false;
		}
		if (_owner) {
			(static_cast<TReceiver*>(_owner)->*_onDone)(_b, std::move(response));
		}
		return true;
	}

private:
	CallbackType _onDone;
	T _b;

};

template <typename T, typename TReturn, typename TReceiver, typename TResponse>
class RPCBindedDoneHandlerOwnedReq : public RPCOwnedDoneHandler { // done(b, result, req_id)
	using CallbackType = TReturn (TReceiver::*)(T, const TResponse &, mtpRequestId);

public:
	RPCBindedDoneHandlerOwnedReq(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _onDone(onDone), _b(b) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		auto response = TResponse();
		if (!response.read(from, end)) {
			return false;
		}
		if (_owner) {
			(static_cast<TReceiver*>(_owner)->*_onDone)(_b, std::move(response), requestId);
		}
		return true;
	}

private:
	CallbackType _onDone;
	T _b;

};

template <typename T, typename TReturn, typename TReceiver>
class RPCBindedDoneHandlerOwnedNo : public RPCOwnedDoneHandler { // done(b)
	using CallbackType = TReturn (TReceiver::*)(T);

public:
	RPCBindedDoneHandlerOwnedNo(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(_b);
		return true;
	}

private:
	CallbackType _onDone;
	T _b;

};

template <typename T, typename TReturn, typename TReceiver>
class RPCBindedDoneHandlerOwnedNoReq : public RPCOwnedDoneHandler { // done(b, req_id)
	using CallbackType = TReturn (TReceiver::*)(T, mtpRequestId);

public:
	RPCBindedDoneHandlerOwnedNoReq(T b, TReceiver *receiver, CallbackType onDone) : RPCOwnedDoneHandler(receiver), _b(b), _onDone(onDone) {
	}
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		if (_owner) (static_cast<TReceiver*>(_owner)->*_onDone)(_b, requestId);
		return true;
	}

private:
	CallbackType _onDone;
	T _b;

};

template <typename TReceiver>
class RPCFailHandlerOwned : public RPCOwnedFailHandler { // fail(error)
	using CallbackType = bool (TReceiver::*)(const RPCError &);

public:
	RPCFailHandlerOwned(TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(e) : true;
	}

private:
	CallbackType _onFail;

};

template <typename TReceiver>
class RPCFailHandlerOwnedReq : public RPCOwnedFailHandler { // fail(error, req_id)
	using CallbackType = bool (TReceiver::*)(const RPCError &, mtpRequestId);

public:
	RPCFailHandlerOwnedReq(TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(e, requestId) : true;
	}

private:
	CallbackType _onFail;

};

template <typename TReceiver>
class RPCFailHandlerOwnedNo : public RPCOwnedFailHandler { // fail()
	using CallbackType = bool (TReceiver::*)();

public:
	RPCFailHandlerOwnedNo(TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)() : true;
	}

private:
	CallbackType _onFail;

};

template <typename TReceiver>
class RPCFailHandlerOwnedNoReq : public RPCOwnedFailHandler { // fail(req_id)
	using CallbackType = bool (TReceiver::*)(mtpRequestId);

public:
	RPCFailHandlerOwnedNoReq(TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(requestId) : true;
	}

private:
	CallbackType _onFail;

};

template <typename T, typename TReceiver>
class RPCBindedFailHandlerOwned : public RPCOwnedFailHandler { // fail(b, error)
	using CallbackType = bool (TReceiver::*)(T, const RPCError &);

public:
	RPCBindedFailHandlerOwned(T b, TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail), _b(b) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(_b, e) : true;
	}

private:
	CallbackType _onFail;
	T _b;

};

template <typename T, typename TReceiver>
class RPCBindedFailHandlerOwnedReq : public RPCOwnedFailHandler { // fail(b, error, req_id)
	using CallbackType = bool (TReceiver::*)(T, const RPCError &, mtpRequestId);

public:
	RPCBindedFailHandlerOwnedReq(T b, TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail), _b(b) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(_b, e, requestId) : true;
	}

private:
	CallbackType _onFail;
	T _b;

};

template <typename T, typename TReceiver>
class RPCBindedFailHandlerOwnedNo : public RPCOwnedFailHandler { // fail(b)
	using CallbackType = bool (TReceiver::*)(T);

public:
	RPCBindedFailHandlerOwnedNo(T b, TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail), _b(b) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(_b) : true;
	}

private:
	CallbackType _onFail;
	T _b;

};

template <typename T, typename TReceiver>
class RPCBindedFailHandlerOwnedNoReq : public RPCOwnedFailHandler { // fail(b, req_id)
	using CallbackType = bool (TReceiver::*)(T, mtpRequestId);

public:
	RPCBindedFailHandlerOwnedNoReq(T b, TReceiver *receiver, CallbackType onFail) : RPCOwnedFailHandler(receiver), _onFail(onFail), _b(b) {
	}
	bool operator()(mtpRequestId requestId, const RPCError &e) override {
		return _owner ? (static_cast<TReceiver*>(_owner)->*_onFail)(_b, requestId) : true;
	}

private:
	CallbackType _onFail;
	T _b;

};

class RPCSender {
public:
	template <typename TReceiver> // done(from, end)
	RPCDoneHandlerPtr rpcDone(bool (TReceiver::*onDone)(const mtpPrime *, const mtpPrime *)) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerBareOwned<TReceiver>(static_cast<TReceiver*>(this), onDone));
	}

	template <typename TReceiver> // done(from, end, req_id)
	RPCDoneHandlerPtr rpcDone(bool (TReceiver::*onDone)(const mtpPrime *, const mtpPrime *, mtpRequestId)) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerBareOwnedReq<TReceiver>(static_cast<TReceiver*>(this), onDone));
	}

	template <typename TReturn, typename TReceiver, typename TResponse> // done(result)
	RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(const TResponse &)) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerOwned<TReturn, TReceiver, TResponse>(static_cast<TReceiver*>(this), onDone));
	}

	template <typename TReturn, typename TReceiver, typename TResponse> // done(result, req_id)
	RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(const TResponse &, mtpRequestId)) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerOwnedReq<TReturn, TReceiver, TResponse>(static_cast<TReceiver*>(this), onDone));
	}

	template <typename TReturn, typename TReceiver> // done()
	RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)()) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerOwnedNo<TReturn, TReceiver>(static_cast<TReceiver*>(this), onDone));
	}

	template <typename TReturn, typename TReceiver> // done(req_id)
	RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(mtpRequestId)) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerOwnedNoReq<TReturn, TReceiver>(static_cast<TReceiver*>(this), onDone));
	}

	template <typename TReceiver> // fail(error)
	RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(const RPCError &)) {
		return RPCFailHandlerPtr(new RPCFailHandlerOwned<TReceiver>(static_cast<TReceiver*>(this), onFail));
	}

	template <typename TReceiver> // fail(error, req_id)
	RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(const RPCError &, mtpRequestId)) {
		return RPCFailHandlerPtr(new RPCFailHandlerOwnedReq<TReceiver>(static_cast<TReceiver*>(this), onFail));
	}

	template <typename TReceiver> // fail()
	RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)()) {
		return RPCFailHandlerPtr(new RPCFailHandlerOwnedNo<TReceiver>(static_cast<TReceiver*>(this), onFail));
	}

	template <typename TReceiver> // fail(req_id)
	RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(mtpRequestId)) {
		return RPCFailHandlerPtr(new RPCFailHandlerOwnedNo<TReceiver>(static_cast<TReceiver*>(this), onFail));
	}

	template <typename T, typename TReceiver> // done(b, from, end)
	RPCDoneHandlerPtr rpcDone(bool (TReceiver::*onDone)(T, const mtpPrime *, const mtpPrime *), T b) {
		return RPCDoneHandlerPtr(new RPCBindedDoneHandlerBareOwned<T, TReceiver>(b, static_cast<TReceiver*>(this), onDone));
	}

	template <typename T, typename TReceiver> // done(b, from, end, req_id)
	RPCDoneHandlerPtr rpcDone(bool (TReceiver::*onDone)(T, const mtpPrime *, const mtpPrime *, mtpRequestId), T b) {
		return RPCDoneHandlerPtr(new RPCBindedDoneHandlerBareOwnedReq<T, TReceiver>(b, static_cast<TReceiver*>(this), onDone));
	}

	template <typename T, typename TReturn, typename TReceiver, typename TResponse> // done(b, result)
	RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T, const TResponse &), T b) {
		return RPCDoneHandlerPtr(new RPCBindedDoneHandlerOwned<T, TReturn, TReceiver, TResponse>(b, static_cast<TReceiver*>(this), onDone));
	}

	template <typename T, typename TReturn, typename TReceiver, typename TResponse> // done(b, result, req_id)
	RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T, const TResponse &, mtpRequestId), T b) {
		return RPCDoneHandlerPtr(new RPCBindedDoneHandlerOwnedReq<T, TReturn, TReceiver, TResponse>(b, static_cast<TReceiver*>(this), onDone));
	}

	template <typename T, typename TReturn, typename TReceiver> // done(b)
	RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T), T b) {
		return RPCDoneHandlerPtr(new RPCBindedDoneHandlerOwnedNo<T, TReturn, TReceiver>(b, static_cast<TReceiver*>(this), onDone));
	}

	template <typename T, typename TReturn, typename TReceiver> // done(b, req_id)
	RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(T, mtpRequestId), T b) {
		return RPCDoneHandlerPtr(new RPCBindedDoneHandlerOwnedNoReq<T, TReturn, TReceiver>(b, static_cast<TReceiver*>(this), onDone));
	}

	template <typename T, typename TReceiver> // fail(b, error)
	RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(T, const RPCError &), T b) {
		return RPCFailHandlerPtr(new RPCBindedFailHandlerOwned<T, TReceiver>(b, static_cast<TReceiver*>(this), onFail));
	}

	template <typename T, typename TReceiver> // fail(b, error, req_id)
	RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(T, const RPCError &, mtpRequestId), T b) {
		return RPCFailHandlerPtr(new RPCBindedFailHandlerOwnedReq<T, TReceiver>(b, static_cast<TReceiver*>(this), onFail));
	}

	template <typename T, typename TReceiver> // fail(b)
	RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(T), T b) {
		return RPCFailHandlerPtr(new RPCBindedFailHandlerOwnedNo<T, TReceiver>(b, static_cast<TReceiver*>(this), onFail));
	}

	template <typename T, typename TReceiver> // fail(b, req_id)
	RPCFailHandlerPtr rpcFail(bool (TReceiver::*onFail)(T, mtpRequestId), T b) {
		return RPCFailHandlerPtr(new RPCBindedFailHandlerOwnedNo<T, TReceiver>(b, static_cast<TReceiver*>(this), onFail));
	}

	virtual void rpcClear() {
		rpcInvalidate();
	}

	virtual ~RPCSender() {
		rpcInvalidate();
	}

protected:
	void rpcInvalidate() {
		for (auto handler : base::take(_rpcDoneHandlers)) {
			handler->invalidate();
		}
		for (auto handler : base::take(_rpcFailHandlers)) {
			handler->invalidate();
		}
	}

private:
	base::flat_set<RPCOwnedDoneHandler*> _rpcDoneHandlers;
	base::flat_set<RPCOwnedFailHandler*> _rpcFailHandlers;

	void rpcRegHandler(RPCOwnedDoneHandler *handler) {
		_rpcDoneHandlers.emplace(handler);
	}

	void rpcUnregHandler(RPCOwnedDoneHandler *handler) {
		_rpcDoneHandlers.remove(handler);
	}

	void rpcRegHandler(RPCOwnedFailHandler *handler) {
		_rpcFailHandlers.emplace(handler);
	}

	void rpcUnregHandler(RPCOwnedFailHandler *handler) {
		_rpcFailHandlers.remove(handler);
	}

	friend class RPCOwnedDoneHandler;
	friend class RPCOwnedFailHandler;

};

using MTPStateChangedHandler = void (*)(int32 dcId, int32 state);
using MTPSessionResetHandler = void (*)(int32 dcId);

template <typename Base, typename FunctionType>
class RPCHandlerImplementation : public Base {
protected:
	using Lambda = FnMut<FunctionType>;
	using Parent = RPCHandlerImplementation<Base, FunctionType>;

public:
	RPCHandlerImplementation(Lambda handler) : _handler(std::move(handler)) {
	}

protected:
	Lambda _handler;

};

template <typename FunctionType>
using RPCDoneHandlerImplementation = RPCHandlerImplementation<RPCAbstractDoneHandler, FunctionType>;

class RPCDoneHandlerImplementationBare : public RPCDoneHandlerImplementation<bool(const mtpPrime*, const mtpPrime*)> { // done(from, end)
public:
	using RPCDoneHandlerImplementation<bool(const mtpPrime*, const mtpPrime*)>::Parent::Parent;
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		return this->_handler ? this->_handler(from, end) : true;
	}

};

class RPCDoneHandlerImplementationBareReq : public RPCDoneHandlerImplementation<bool(const mtpPrime*, const mtpPrime*, mtpRequestId)> { // done(from, end, req_id)
public:
	using RPCDoneHandlerImplementation<bool(const mtpPrime*, const mtpPrime*, mtpRequestId)>::Parent::Parent;
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		return this->_handler ? this->_handler(from, end, requestId) : true;
	}

};

template <typename R, typename TResponse>
class RPCDoneHandlerImplementationPlain : public RPCDoneHandlerImplementation<R(const TResponse&)> { // done(result)
public:
	using RPCDoneHandlerImplementation<R(const TResponse&)>::Parent::Parent;
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		auto response = TResponse();
		if (!response.read(from, end)) {
			return false;
		}
		if (this->_handler) {
			this->_handler(std::move(response));
		}
		return true;
	}

};

template <typename R, typename TResponse>
class RPCDoneHandlerImplementationReq : public RPCDoneHandlerImplementation<R(const TResponse&, mtpRequestId)> { // done(result, req_id)
public:
	using RPCDoneHandlerImplementation<R(const TResponse&, mtpRequestId)>::Parent::Parent;
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		auto response = TResponse();
		if (!response.read(from, end)) {
			return false;
		}
		if (this->_handler) {
			this->_handler(std::move(response), requestId);
		}
		return true;
	}

};

template <typename R>
class RPCDoneHandlerImplementationNo : public RPCDoneHandlerImplementation<R()> { // done()
public:
	using RPCDoneHandlerImplementation<R()>::Parent::Parent;
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		if (this->_handler) {
			this->_handler();
		}
		return true;
	}

};

template <typename R>
class RPCDoneHandlerImplementationNoReq : public RPCDoneHandlerImplementation<R(mtpRequestId)> { // done(req_id)
public:
	using RPCDoneHandlerImplementation<R(mtpRequestId)>::Parent::Parent;
	bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
		if (this->_handler) {
			this->_handler(requestId);
		}
		return true;
	}

};

template <typename Lambda>
constexpr bool rpcDone_canCallBare_v = rpl::details::is_callable_plain_v<
	Lambda, const mtpPrime*, const mtpPrime*>;

template <typename Lambda>
constexpr bool rpcDone_canCallBareReq_v = rpl::details::is_callable_plain_v<
	Lambda, const mtpPrime*, const mtpPrime*, mtpRequestId>;

template <typename Lambda>
constexpr bool rpcDone_canCallNo_v = rpl::details::is_callable_plain_v<
	Lambda>;

template <typename Lambda>
constexpr bool rpcDone_canCallNoReq_v = rpl::details::is_callable_plain_v<
	Lambda, mtpRequestId>;

template <typename Function>
struct rpcDone_canCallPlain : std::false_type {
};

template <typename Lambda, typename Return, typename T>
struct rpcDone_canCallPlain<Return(Lambda::*)(const T&)> : std::true_type {
	using Arg = T;
};

template <typename Lambda, typename Return, typename T>
struct rpcDone_canCallPlain<Return(Lambda::*)(const T&)const>
	: rpcDone_canCallPlain<Return(Lambda::*)(const T&)> {
};

template <typename Function>
constexpr bool rpcDone_canCallPlain_v = rpcDone_canCallPlain<Function>::value;

template <typename Function>
struct rpcDone_canCallReq : std::false_type {
};

template <typename Lambda, typename Return, typename T>
struct rpcDone_canCallReq<Return(Lambda::*)(const T&, mtpRequestId)> : std::true_type {
	using Arg = T;
};

template <typename Lambda, typename Return, typename T>
struct rpcDone_canCallReq<Return(Lambda::*)(const T&, mtpRequestId)const>
	: rpcDone_canCallReq<Return(Lambda::*)(const T&, mtpRequestId)> {
};

template <typename Function>
constexpr bool rpcDone_canCallReq_v = rpcDone_canCallReq<Function>::value;

template <typename Function>
struct rpcDone_returnType;

template <typename Lambda, typename Return, typename ...Args>
struct rpcDone_returnType<Return(Lambda::*)(Args...)> {
	using type = Return;
};

template <typename Lambda, typename Return, typename ...Args>
struct rpcDone_returnType<Return(Lambda::*)(Args...)const> {
	using type = Return;
};

template <typename Function>
using rpcDone_returnType_t = typename rpcDone_returnType<Function>::type;

template <
	typename Lambda,
	typename Function = crl::deduced_call_type<Lambda>>
RPCDoneHandlerPtr rpcDone(Lambda lambda) {
	using R = rpcDone_returnType_t<Function>;
	if constexpr (rpcDone_canCallBare_v<Lambda>) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBare(std::move(lambda)));
	} else if constexpr (rpcDone_canCallBareReq_v<Lambda>) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBareReq(std::move(lambda)));
	} else if constexpr (rpcDone_canCallNo_v<Lambda>) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNo<R>(std::move(lambda)));
	} else if constexpr (rpcDone_canCallNoReq_v<Lambda>) {
		return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNoReq<R>(std::move(lambda)));
	} else if constexpr (rpcDone_canCallPlain_v<Function>) {
		using T = typename rpcDone_canCallPlain<Function>::Arg;
		return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationPlain<R, T>(std::move(lambda)));
	} else if constexpr (rpcDone_canCallReq_v<Function>) {
		using T = typename rpcDone_canCallReq<Function>::Arg;
		return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationReq<R, T>(std::move(lambda)));
	} else {
		static_assert(false_t(lambda), "Unknown method.");
	}
}

template <typename FunctionType>
using RPCFailHandlerImplementation = RPCHandlerImplementation<RPCAbstractFailHandler, FunctionType>;

class RPCFailHandlerImplementationPlain : public RPCFailHandlerImplementation<bool(const RPCError&)> { // fail(error)
public:
	using Parent::Parent;
	bool operator()(mtpRequestId requestId, const RPCError &error) override {
		return _handler ? _handler(error) : true;
	}

};

class RPCFailHandlerImplementationReq : public RPCFailHandlerImplementation<bool(const RPCError&, mtpRequestId)> { // fail(error, req_id)
public:
	using Parent::Parent;
	bool operator()(mtpRequestId requestId, const RPCError &error) override {
		return this->_handler ? this->_handler(error, requestId) : true;
	}

};

class RPCFailHandlerImplementationNo : public RPCFailHandlerImplementation<bool()> { // fail()
public:
	using Parent::Parent;
	bool operator()(mtpRequestId requestId, const RPCError &error) override {
		return this->_handler ? this->_handler() : true;
	}

};

class RPCFailHandlerImplementationNoReq : public RPCFailHandlerImplementation<bool(mtpRequestId)> { // fail(req_id)
public:
	using Parent::Parent;
	bool operator()(mtpRequestId requestId, const RPCError &error) override {
		return this->_handler ? this->_handler(requestId) : true;
	}

};

template <typename Lambda>
constexpr bool rpcFail_canCallNo_v = rpl::details::is_callable_plain_v<
	Lambda>;

template <typename Lambda>
constexpr bool rpcFail_canCallNoReq_v = rpl::details::is_callable_plain_v<
	Lambda, mtpRequestId>;

template <typename Lambda>
constexpr bool rpcFail_canCallPlain_v = rpl::details::is_callable_plain_v<
	Lambda, const RPCError&>;

template <typename Lambda>
constexpr bool rpcFail_canCallReq_v = rpl::details::is_callable_plain_v<
	Lambda, const RPCError&, mtpRequestId>;

template <
	typename Lambda,
	typename Function = crl::deduced_call_type<Lambda>>
RPCFailHandlerPtr rpcFail(Lambda lambda) {
	if constexpr (rpcFail_canCallNo_v<Lambda>) {
		return RPCFailHandlerPtr(new RPCFailHandlerImplementationNo(std::move(lambda)));
	} else if constexpr (rpcFail_canCallNoReq_v<Lambda>) {
		return RPCFailHandlerPtr(new RPCFailHandlerImplementationNoReq(std::move(lambda)));
	} else if constexpr (rpcFail_canCallPlain_v<Lambda>) {
		return RPCFailHandlerPtr(new RPCFailHandlerImplementationPlain(std::move(lambda)));
	} else if constexpr (rpcFail_canCallReq_v<Lambda>) {
		return RPCFailHandlerPtr(new RPCFailHandlerImplementationReq(std::move(lambda)));
	} else {
		static_assert(false_t(lambda), "Unknown method.");
	}
}
