abstract contract CollectionI is IERC165, IERC721Receiver, IERC1155Receiver  {

    /**
     * @dev True is this is the prototype, false if this is an active (clone/proxy) collection contract.
     */
    bool public isPrototype;

    /**
     * @dev The linked Collections factory (the ERC721 contract).
     */
    CollectionsI public collections;

    /**
     * @dev The linked notification contract (e.g. achievements).
     */
    address public notificationContract;

    /**
     * @dev Initializes a new Collection. Needs to be called by the Collections factory.
     */
    function initialRegister(address _notificationContract,
                             string calldata _ensName,
                             string calldata _ensSubdomainName,
                             address _ensSubdomainRegistrarAddress,
                             address _ensReverseRegistrarAddress)
    external virtual;

    /**
     * @dev Get collection owner from ERC 721 parent (Collections factory).
     */
    function ownerAddress() external view virtual returns (address);

    /**
     * @dev Determine if the Collection owns a specific asset.
     */
    function ownsAsset(address _tokenAddress, uint256 _tokenId) external view virtual returns(bool);

    /**
     * @dev Get count of owned assets.
     */
    function ownedAssetsCount() external view virtual returns (uint256);

    /**
     * @dev Make sure ownership of a certain asset is recorded correctly (added if the collection owns it or removed if it doesn't).
     */
    function syncAssetOwnership(address _tokenAddress, uint256 _tokenId) external virtual;

    /**
     * @dev Transfer an owned asset to a new owner (for ERC1155, a single item of that asset).
     */
    function safeTransferTo(address _tokenAddress, uint256 _tokenId, address _to) external virtual;

    /**
     * @dev Transfer a certain amount of an owned asset to a new owner (for ERC721, _value is ignored).
     */
    function safeTransferTo(address _tokenAddress, uint256 _tokenId, address _to, uint256 _value) external virtual;

    /**
     * @dev Destroy and burn an empty Collection. Can only be called by owner and only on empty collections.
     */
    function destroy() external virtual;

    /**
     * @dev Forward calls to external contracts. Can only be called by owner.
     * Given a contract address and an already-encoded payload (with a function call etc.),
     * we call that contract with this payload, e.g. to trigger actions in the name of the collection.
     */
    function externalCall(address payable _remoteAddress, bytes calldata _callPayload) external virtual payable;

    /**
     * @dev Register ENS name. Can only be called by owner.
     */
    function registerENS(string calldata _name, address _registrarAddress) external virtual;

    /**
     * @dev Register Reverse ENS name. Can only be called by owner.
     */
    function registerReverseENS(address _reverseRegistrarAddress, string calldata _name) external virtual;
}
// Convert to interface once https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2113 is solved.
function syncAssetOwnership(address _tokenAddress, uint256 _tokenId) external virtual;

/**
 * @dev Make sure ownership of a certain asset is recorded correctly (added if the collection owns it or removed if it doesn't).
 */