library BdpOwnership {

	using SafeMath for uint256;

	event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

	event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

	function ownerOf(address[16] _contracts, uint256 _tokenId) public view returns (address) {
		var owner = BdpOwnershipStorage(BdpContracts.getBdpOwnershipStorage(_contracts)).getTokenOwner(_tokenId);
		require(owner != address(0));
		return owner;
	}

	function balanceOf(address[16] _contracts, address _owner) public view returns (uint256) {
		return BdpOwnershipStorage(BdpContracts.getBdpOwnershipStorage(_contracts)).getOwnedTokensLength(_owner);
	}

	function approve(address[16] _contracts, address _to, uint256 _tokenId) public {
		var ownStorage = BdpOwnershipStorage(BdpContracts.getBdpOwnershipStorage(_contracts));

		address owner = ownerOf(_contracts, _tokenId);
		require(_to != owner);
		if (ownStorage.getTokenApproval(_tokenId) != 0 || _to != 0) {
			ownStorage.setTokenApproval(_tokenId, _to);
			Approval(owner, _to, _tokenId);
		}
	}

	/**
	 * @dev Clear current approval of a given token ID
	 * @param _tokenId uint256 ID of the token to be transferred
	 */
	function clearApproval(address[16] _contracts, address _owner, uint256 _tokenId) public {
		var ownStorage = BdpOwnershipStorage(BdpContracts.getBdpOwnershipStorage(_contracts));

		require(ownerOf(_contracts, _tokenId) == _owner);
		if (ownStorage.getTokenApproval(_tokenId) != 0) {
			BdpOwnershipStorage(BdpContracts.getBdpOwnershipStorage(_contracts)).setTokenApproval(_tokenId, 0);
			Approval(_owner, 0, _tokenId);
		}
	}

	/**
	 * @dev Clear current approval and transfer the ownership of a given token ID
	 * @param _from address which you want to send tokens from
	 * @param _to address which you want to transfer the token to
	 * @param _tokenId uint256 ID of the token to be transferred
	 */
	function clearApprovalAndTransfer(address[16] _contracts, address _from, address _to, uint256 _tokenId) public {
		require(_to != address(0));
		require(_to != ownerOf(_contracts, _tokenId));
		require(ownerOf(_contracts, _tokenId) == _from);

		clearApproval(_contracts, _from, _tokenId);
		removeToken(_contracts, _from, _tokenId);
		addToken(_contracts, _to, _tokenId);
		Transfer(_from, _to, _tokenId);
	}

	/**
	 * @dev Internal function to add a token ID to the list of a given address
	 * @param _to address representing the new owner of the given token ID
	 * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
	 */
	function addToken(address[16] _contracts, address _to, uint256 _tokenId) private {
		var ownStorage = BdpOwnershipStorage(BdpContracts.getBdpOwnershipStorage(_contracts));

		require(ownStorage.getTokenOwner(_tokenId) == address(0));

		// Set token owner
		ownStorage.setTokenOwner(_tokenId, _to);

		// Add token to tokenIds list
		var tokenIdsLength = ownStorage.pushTokenId(_tokenId);
		ownStorage.setTokenIdsIndex(_tokenId, tokenIdsLength.sub(1));

		uint256 ownedTokensLength = ownStorage.getOwnedTokensLength(_to);

		// Add token to ownedTokens list
		ownStorage.pushOwnedToken(_to, _tokenId);
		ownStorage.setOwnedTokensIndex(_tokenId, ownedTokensLength);

		// Increment total owned area
		var (area,,) = BdpCalculator.calculateArea(_contracts, _tokenId);
		ownStorage.incrementOwnedArea(_to, area);
	}

	/**
	 * @dev Internal function to remove a token ID from the list of a given address
	 * @param _from address representing the previous owner of the given token ID
	 * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
	 */
	function removeToken(address[16] _contracts, address _from, uint256 _tokenId) private {
		var ownStorage = BdpOwnershipStorage(BdpContracts.getBdpOwnershipStorage(_contracts));

		require(ownerOf(_contracts, _tokenId) == _from);

		// Clear token owner
		ownStorage.setTokenOwner(_tokenId, 0);

		removeFromTokenIds(ownStorage, _tokenId);
		removeFromOwnedToken(ownStorage, _from, _tokenId);

		// Decrement total owned area
		var (area,,) = BdpCalculator.calculateArea(_contracts, _tokenId);
		ownStorage.decrementOwnedArea(_from, area);
	}

	/**
	 * @dev Remove token from ownedTokens list
	 * Note that this will handle single-element arrays. In that case, both ownedTokenIndex and lastOwnedTokenIndex are going to
	 * be zero. Then we can make sure that we will remove _tokenId from the ownedTokens list since we are first swapping
	 * the lastOwnedToken to the first position, and then dropping the element placed in the last position of the list
	 */
	function removeFromOwnedToken(BdpOwnershipStorage _ownStorage, address _from, uint256 _tokenId) private {
		var ownedTokenIndex = _ownStorage.getOwnedTokensIndex(_tokenId);
		var lastOwnedTokenIndex = _ownStorage.getOwnedTokensLength(_from).sub(1);
		var lastOwnedToken = _ownStorage.getOwnedToken(_from, lastOwnedTokenIndex);
		_ownStorage.setOwnedToken(_from, ownedTokenIndex, lastOwnedToken);
		_ownStorage.setOwnedToken(_from, lastOwnedTokenIndex, 0);
		_ownStorage.decrementOwnedTokensLength(_from);
		_ownStorage.setOwnedTokensIndex(_tokenId, 0);
		_ownStorage.setOwnedTokensIndex(lastOwnedToken, ownedTokenIndex);
	}

	/**
	 * @dev Remove token from tokenIds list
	 */
	function removeFromTokenIds(BdpOwnershipStorage _ownStorage, uint256 _tokenId) private {
		var tokenIndex = _ownStorage.getTokenIdsIndex(_tokenId);
		var lastTokenIdIndex = _ownStorage.getTokenIdsLength().sub(1);
		var lastTokenId = _ownStorage.getTokenIdByIndex(lastTokenIdIndex);
		_ownStorage.setTokenIdByIndex(tokenIndex, lastTokenId);
		_ownStorage.setTokenIdByIndex(lastTokenIdIndex, 0);
		_ownStorage.decrementTokenIdsLength();
		_ownStorage.setTokenIdsIndex(_tokenId, 0);
		_ownStorage.setTokenIdsIndex(lastTokenId, tokenIndex);
	}

	/**
	 * @dev Mint token function
	 * @param _to The address that will own the minted token
	 * @param _tokenId uint256 ID of the token to be minted by the msg.sender
	 */
	function mint(address[16] _contracts, address _to, uint256 _tokenId) public {
		require(_to != address(0));
		addToken(_contracts, _to, _tokenId);
		Transfer(address(0), _to, _tokenId);
	}

	/**
	 * @dev Burns a specific token
	 * @param _tokenId uint256 ID of the token being burned
	 */
	function burn(address[16] _contracts, uint256 _tokenId) public {
		address owner = BdpOwnershipStorage(BdpContracts.getBdpOwnershipStorage(_contracts)).getTokenOwner(_tokenId);
		clearApproval(_contracts, owner, _tokenId);
		removeToken(_contracts, owner, _tokenId);
		Transfer(owner, address(0), _tokenId);
	}

}
function addToken(address[16] _contracts, address _to, uint256 _tokenId) private {
	var ownStorage = BdpOwnershipStorage(BdpContracts.getBdpOwnershipStorage(_contracts));

	require(ownStorage.getTokenOwner(_tokenId) == address(0));

	// Set token owner
	ownStorage.setTokenOwner(_tokenId, _to);

	// Add token to tokenIds list
	var tokenIdsLength = ownStorage.pushTokenId(_tokenId);
	ownStorage.setTokenIdsIndex(_tokenId, tokenIdsLength.sub(1));

	uint256 ownedTokensLength = ownStorage.getOwnedTokensLength(_to);

	// Add token to ownedTokens list
	ownStorage.pushOwnedToken(_to, _tokenId);
	ownStorage.setOwnedTokensIndex(_tokenId, ownedTokensLength);

	// Increment total owned area
	var (area,,) = BdpCalculator.calculateArea(_contracts, _tokenId);
	ownStorage.incrementOwnedArea(_to, area);
}

/**
 	 * @dev Internal function to add a token ID to the list of a given address
 	 * @param _to address representing the new owner of the given token ID
 	 * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
 	 */