contract DragonOwnership is DragonBase {

    /// @notice Name and symbol of the non fungible token, as defined in ERC721.
    string public constant name = "Drakons";
    string public constant symbol = "DRKNS";

    //bytes4 constant InterfaceSignature_ERC165 = bytes4(keccak256('supportsInterface(bytes4)'));

    //bytes4 constant InterfaceSignature_ERC721 =
    //bytes4(keccak256('name()')) ^
    //bytes4(keccak256('symbol()')) ^
    //bytes4(keccak256('totalSupply()')) ^
    //bytes4(keccak256('balanceOf(address)')) ^
    //bytes4(keccak256('ownerOf(uint256)')) ^
    //bytes4(keccak256('approve(address,uint256)')) ^
    //bytes4(keccak256('transfer(address,uint256)')) ^
    //bytes4(keccak256('transferFrom(address,address,uint256)')) ^
    //bytes4(keccak256('tokensOfOwner(address)')) ^
    //bytes4(keccak256('tokenMetadata(uint256,string)'));

    /// @notice Introspection interface as per ERC-165 (https://github.com/ethereum/EIPs/issues/165).
    ///  Returns true for any standardized interfaces implemented by this contract. We implement
    ///  ERC-165 (obviously!) and ERC-721.
    //function supportsInterface(bytes4 _interfaceID) external view returns (bool)
    //{
    //    return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721));
    //}

    function setTokenURIAffixes(string calldata _prefix, string calldata _suffix) external onlyCEO {
        tokenURIPrefix = _prefix;
        tokenURISuffix = _suffix;
    }

    /// @dev Checks if a given address is the current owner of a particular Dragon.
    /// @param _claimant the address we are validating against.
    /// @param _tokenId dragon id, only valid when > 0
    function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return dragonIndexToOwner[_tokenId] == _claimant;
    }

    /// @dev Checks if a given address currently has transferApproval for a particular Dragon.
    /// @param _claimant the address we are confirming dragon is approved for.
    /// @param _tokenId dragon id, only valid when > 0
    function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return dragonIndexToApproved[_tokenId] == _claimant;
    }

    /// @dev Marks an address as being approved for transferFrom(), overwriting any previous
    ///  approval. Setting _approved to address(0) clears all transfer approval.
    ///  NOTE: _approve() does NOT send the Approval event. This is intentional because
    ///  _approve() and transferFrom() are used together for putting Dragons on auction, and
    ///  there is no value in spamming the log with Approval events in that case.
    function _approve(uint256 _tokenId, address _approved) internal {
        dragonIndexToApproved[_tokenId] = _approved;
    }

    /// @notice Returns the number of Dragons owned by a specific address.
    /// @param _owner The owner address to check.
    /// @dev Required for ERC-721 compliance
    function balanceOf(address _owner) public view returns (uint256 count) {
        return ownershipTokenCount[_owner];
    }

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT. When transfer is complete, this function
    ///  checks if `_to` is a smart contract (code size > 0). If so, it calls
    ///  `onERC721Received` on `_to` and throws if the return value is not
    ///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) public payable
    {
        require(_to != address(0));
        require(_to != address(this));
        require(_to != address(saleAuction));
        require(_to != address(siringAuction));

        // Check for approval and valid ownership
        //require(_approvedFor(msg.sender, _tokenId));
        //require(_owns(_from, _tokenId));
        address owner = ownerOf(_tokenId);
        require(owner == _from);
        require (owner == msg.sender || dragonIndexToApproved[_tokenId] == msg.sender || authorised[owner][msg.sender]);

        // Reassign ownership, clearing pending approvals and emitting Transfer event.
        _transfer(_from, _to, _tokenId);

        uint32 size;
        assembly {
            size := extcodesize(_to)
        }

        if(size > 0) {
            ERC721TokenReceiver receiver = ERC721TokenReceiver(_to);
            require(receiver.onERC721Received(msg.sender,_from,_tokenId,data) == bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")));
        }
    }

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to "".
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable
    {
        safeTransferFrom(_from, _to, _tokenId, "");
    }

    /// @notice Transfers a Dragon to another address. If transferring to a smart
    ///  contract be VERY CAREFUL to ensure that it is aware of ERC-721 (or
    ///  BlockDragonz specifically) or your Dragon may be lost forever. Seriously.
    /// @param _to The address of the recipient, can be a user or contract.
    /// @param _tokenId The ID of the Dragon to transfer.
    function transfer(
        address _to,
        uint256 _tokenId
    )
    external
    whenNotPaused
    {
        // Safety check to prevent against an unexpected 0x0 default.
        require(_to != address(0));
        // Disallow transfers to this contract to prevent accidental misuse.
        // The contract should never own any dragons (except very briefly
        // after a firstGen dragon is created and before it goes on auction).
        require(_to != address(this));
        // Disallow transfers to the auction contracts to prevent accidental
        // misuse. Auction contracts should only take ownership of dragons
        // through the allow + transferFrom flow.
        require(_to != address(saleAuction));
        require(_to != address(siringAuction));

        // You can only send your own dragon.
        require(_owns(msg.sender, _tokenId));

        // Reassign ownership, clear pending approvals, emit Transfer event.
        _transfer(msg.sender, _to, _tokenId);
    }

    /// @notice Returns the address currently assigned ownership of a given Dragon.
    /// @dev Required for ERC-721 compliance.
    function ownerOf(uint256 _tokenId) public view isValidToken(_tokenId) returns (address)
    {
        return dragonIndexToOwner[_tokenId];
    }

    /// @notice Grant another address the right to transfer a specific Dragon via
    ///  transferFrom(). This is the preferred flow for transfering NFTs to contracts.
    /// @param _approved The address to be granted transfer approval. Pass address(0) to
    ///  clear all approvals.
    /// @param _tokenId The ID of the Dragon that can be transferred if this call succeeds.
    /// @dev Required for ERC-721 compliance.
    //function approve( address _to, uint256 _tokenId) external whenNotPaused {
    function approve(address _approved, uint256 _tokenId) external payable whenNotPaused {
        // Only an owner can grant transfer approval.
        //require(_owns(msg.sender, _tokenId) || authorised[owner][msg.sender]);
        address owner = dragonIndexToOwner[_tokenId];
        require(owner == msg.sender || authorised[owner][msg.sender]);

        // Register the approval (replacing any previous approval).
        _approve(_tokenId, _approved);

        // Emit approval event.
        //emit Approval(msg.sender, _approved, _tokenId);
        emit Approval(owner, _approved, _tokenId);
    }

    /// @notice Get the approved address for a single NFT
    /// @dev Throws if `_tokenId` is not a valid NFT.
    /// @param _tokenId The NFT to find the approved address for
    /// @return The approved address for this NFT, or the zero address if there is none
    function getApproved(uint256 _tokenId) external view isValidToken(_tokenId) returns (address)
    {
        return dragonIndexToApproved[_tokenId];
    }


    /// @notice Transfer a Dragon owned by another address, for which the calling address
    ///  has previously been granted transfer approval by the owner.
    /// @param _from The address that owns the Dragon to be transfered.
    /// @param _to The address that should take ownership of the Dragon. Can be any address,
    ///  including the caller.
    /// @param _tokenId The ID of the Dragon to be transferred.
    /// @dev Required for ERC-721 compliance.
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable whenNotPaused
    {
        // Safety check to prevent against an unexpected 0x0 default.
        require(_to != address(0));
        // Disallow transfers to this contract to prevent accidental misuse.
        // The contract should never own any dragons (except very briefly
        // after a firstGen dragon is created and before it goes on auction).
        require(_to != address(this));
        // Check for approval and valid ownership
        //require(_approvedFor(msg.sender, _tokenId));
        //require(_owns(_from, _tokenId));
        address owner = ownerOf(_tokenId);
        require(owner == _from);
        require (owner == msg.sender || dragonIndexToApproved[_tokenId] == msg.sender || authorised[owner][msg.sender]);

        // Reassign ownership (also clears pending approvals and emits Transfer event).
        _transfer(_from, _to, _tokenId);
    }

    /// @notice Returns the total number of Dragons currently in existence.
    /// @dev Required for ERC-721 compliance.
    function totalSupply() public view returns (uint) {
        return dragons.length - 1;
    }

    /// @notice Returns a list of all Dragon IDs assigned to an address.
    /// @param _owner The owner whose Dragons we are interested in.
    /// @dev This method MUST NEVER be called by smart contract code. First, it's fairly
    ///  expensive (it walks the entire Dragon array looking for dragons belonging to owner),
    ///  but it also returns a dynamic array, which is only supported for web3 calls, and
    ///  not contract-to-contract calls.
    function tokensOfOwner(address _owner) external view returns(uint256[] memory ownerTokens) {
        uint256 tokenCount = balanceOf(_owner);

        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 totalDragons = totalSupply();
            uint256 resultIndex = 0;

            // We count on the fact that all dragons have IDs starting at 1 and increasing
            // sequentially up to the totalDragon count.
            uint256 dragonId;

            for (dragonId = 1; dragonId <= totalDragons; dragonId++) {
                if (dragonIndexToOwner[dragonId] == _owner) {
                    result[resultIndex] = dragonId;
                    resultIndex++;
                }
            }

            return result;
        }
    }

    /// @notice Enumerate valid NFTs
    /// @dev Throws if `_index` >= `totalSupply()`.
    /// @param _index A counter less than `totalSupply()`
    /// @return The token identifier for the `_index`th NFT,
    ///  (sort order not specified)
    function tokenByIndex(uint256 _index) external view returns (uint256)
    {
        return _index;
    }

    /// @notice Enumerate NFTs assigned to an owner
    /// @dev Throws if `_index` >= `balanceOf(_owner)` or if
    ///  `_owner` is the zero address, representing invalid NFTs.
    /// @param _owner An address where we are interested in NFTs owned by them
    /// @param _index A counter less than `balanceOf(_owner)`
    /// @return The token identifier for the `_index`th NFT assigned to `_owner`,
    ///   (sort order not specified)
    //function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256)
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 dragonId)
    {
        uint256 count = 0;
        for (uint256 i = 1; i <= totalSupply(); ++i) {
            if (dragonIndexToOwner[i] == _owner) {
                if (count == _index) {
                    return i;
                } else {
                    count++;
                }
            }
        }
        revert();
    }
}
/// @title The facet of the BlockDragons core contract that manages ownership, ERC-721 (draft) compliant.
/// @author Zynappse Corporation (https://www.zynapse.com)
/// @dev Ref: https://github.com/ethereum/EIPs/issues/721
///  @dev Refer to the Dragon contract documentation for details in contract interactions.
function setTokenURIAffixes(string calldata _prefix, string calldata _suffix) external onlyCEO {
    tokenURIPrefix = _prefix;
    tokenURISuffix = _suffix;
}

//function supportsInterface(bytes4 _interfaceID) external view returns (bool)
//{
//    return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721));
//}