contract CSportsOwnership is CSportsBase {

  /// @notice These are set in the contract constructor at deployment time
  string _name;
  string _symbol;
  string _tokenURI;

  // bool public implementsERC721 = true;
  //
  function implementsERC721() public pure returns (bool)
  {
      return true;
  }

  /// @notice A descriptive name for a collection of NFTs in this contract
  function name() external view returns (string) {
    return _name;
  }

  /// @notice An abbreviated name for NFTs in this contract
  function symbol() external view returns (string) {
    return _symbol;
  }

  /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
  /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
  ///  3986. The URI may point to a JSON file that conforms to the "ERC721
  ///  Metadata JSON Schema".
  function tokenURI(uint256 _tokenId) external view returns (string ret) {
    string memory tokenIdAsString = uintToString(uint(_tokenId));
    ret = string (abi.encodePacked(_tokenURI, tokenIdAsString, "/"));
  }

  /// @notice Find the owner of an NFT
  /// @dev NFTs assigned to zero address are considered invalid, and queries
  ///  about them do throw.
  /// @param _tokenId The identifier for an NFT
  /// @return The address of the owner of the NFT
  function ownerOf(uint256 _tokenId)
      public
      view
      returns (address owner)
  {
      owner = playerTokenToOwner[_tokenId];
      require(owner != address(0));
  }

  /// @notice Count all NFTs assigned to an owner
  /// @dev NFTs assigned to the zero address are considered invalid, and this
  ///  function throws for queries about the zero address.
  /// @param _owner An address for whom to query the balance
  /// @return The number of NFTs owned by `_owner`, possibly zero
  function balanceOf(address _owner) public view returns (uint256 count) {
      // I am not a big fan of  referencing a property on an array element
      // that may not exist. But if it does not exist, Solidity will return 0
      // which is right.
      return ownedTokens[_owner].length;
  }

  /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
  ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
  ///  THEY MAY BE PERMANENTLY LOST
  /// @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.
  /// @param _from The current owner of the NFT
  /// @param _to The new owner
  /// @param _tokenId The NFT to transfer
  function transferFrom(
      address _from,
      address _to,
      uint256 _tokenId
  )
      public
      whenNotPaused
  {
      require(_to != address(0));
      require (_tokenExists(_tokenId));

      // Check for approval and valid ownership
      require(_approvedFor(_to, _tokenId));
      require(_owns(_from, _tokenId));

      // Validate the sender
      require(_owns(msg.sender, _tokenId) || // sender owns the token
             (msg.sender == playerTokenToApproved[_tokenId]) || // sender is the approved address
             operators[_from][msg.sender]); // sender is an authorized operator for this token

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

  /// @notice Transfer ownership of a batch of NFTs -- THE CALLER IS RESPONSIBLE
  ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
  ///  THEY MAY BE PERMANENTLY LOST
  /// @dev Throws unless `msg.sender` is the current owner, an authorized
  ///  operator, or the approved address for all NFTs. Throws if `_from` is
  ///  not the current owner. Throws if `_to` is the zero address. Throws if
  ///  any `_tokenId` is not a valid NFT.
  /// @param _from - Current owner of the token being authorized for transfer
  /// @param _to - Address we are transferring to
  /// @param _tokenIds The IDs of the PlayerTokens that can be transferred if this call succeeds.
  function batchTransferFrom(
        address _from,
        address _to,
        uint32[] _tokenIds
  )
  public
  whenNotPaused
  {
    for (uint32 i = 0; i < _tokenIds.length; i++) {

        uint32 _tokenId = _tokenIds[i];

        // Check for approval and valid ownership
        require(_approvedFor(_to, _tokenId));
        require(_owns(_from, _tokenId));

        // Validate the sender
        require(_owns(msg.sender, _tokenId) || // sender owns the token
        (msg.sender == playerTokenToApproved[_tokenId]) || // sender is the approved address
        operators[_from][msg.sender]); // sender is an authorized operator for this token

        // Reassign ownership, clear pending approvals (not necessary here),
        // and emit Transfer event.
        _transfer(_from, _to, _tokenId);
    }
  }

  /// @notice Change or reaffirm the approved address for an NFT
  /// @dev The zero address indicates there is no approved address.
  ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
  ///  operator of the current owner.
  /// @param _to The new approved NFT controller
  /// @param _tokenId The NFT to approve
  function approve(
      address _to,
      uint256 _tokenId
  )
      public
      whenNotPaused
  {
      address owner = ownerOf(_tokenId);
      require(_to != owner);

      // Only an owner or authorized operator can grant transfer approval.
      require((msg.sender == owner) || (operators[ownerOf(_tokenId)][msg.sender]));

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

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

  /// @notice Change or reaffirm the approved address for an NFT
  /// @dev The zero address indicates there is no approved address.
  /// Throws unless `msg.sender` is the current NFT owner, or an authorized
  /// operator of the current owner.
  /// @param _to The address to be granted transfer approval. Pass address(0) to
  ///  clear all approvals.
  /// @param _tokenIds The IDs of the PlayerTokens that can be transferred if this call succeeds.
  function batchApprove(
        address _to,
        uint32[] _tokenIds
  )
  public
  whenNotPaused
  {
    for (uint32 i = 0; i < _tokenIds.length; i++) {

        uint32 _tokenId = _tokenIds[i];

        // Only an owner or authorized operator can grant transfer approval.
        require(_owns(msg.sender, _tokenId) || (operators[ownerOf(_tokenId)][msg.sender]));

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

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

  /// @notice Escrows all of the tokensIds passed by transfering ownership
  ///   to the teamContract. CAN ONLY BE CALLED BY THE CURRENT TEAM CONTRACT.
  /// @param _owner - Current owner of the token being authorized for transfer
  /// @param _tokenIds The IDs of the PlayerTokens that can be transferred if this call succeeds.
  function batchEscrowToTeamContract(
    address _owner,
    uint32[] _tokenIds
  )
    public
    whenNotPaused
  {
    require(teamContract != address(0));
    require(msg.sender == address(teamContract));

    for (uint32 i = 0; i < _tokenIds.length; i++) {

      uint32 _tokenId = _tokenIds[i];

      // Only an owner can transfer the token.
      require(_owns(_owner, _tokenId));

      // Reassign ownership, clear pending approvals (not necessary here),
      // and emit Transfer event.
      _transfer(_owner, teamContract, _tokenId);
    }
  }

  bytes4 constant TOKEN_RECEIVED_SIG = bytes4(keccak256("onERC721Received(address,uint256,bytes)"));

  /// @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 data) external payable {
    transferFrom(_from, _to, _tokenId);
    if (_isContract(_to)) {
        ERC721TokenReceiver receiver = ERC721TokenReceiver(_to);
        bytes4 response = receiver.onERC721Received.gas(50000)(msg.sender, _from, _tokenId, data);
        require(response == TOKEN_RECEIVED_SIG);
    }
  }

  /// @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 {
    require(_to != address(0));
    transferFrom(_from, _to, _tokenId);
    if (_isContract(_to)) {
        ERC721TokenReceiver receiver = ERC721TokenReceiver(_to);
        bytes4 response = receiver.onERC721Received.gas(50000)(msg.sender, _from, _tokenId, "");
        require(response == TOKEN_RECEIVED_SIG);
    }
  }

  /// @notice Count NFTs tracked by this contract
  /// @return A count of valid NFTs tracked by this contract, where each one of
  ///  them has an assigned and queryable owner not equal to the zero address
  function totalSupply() public view returns (uint) {
      return playerTokens.length;
  }

  /// @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 _tokenId) {
      require(owner != address(0));
      require(index < balanceOf(owner));
      return ownedTokens[owner][index];
  }

  /// @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) {
      require (_tokenExists(index));
      return index;
  }

  /// @notice Enable or disable approval for a third party ("operator") to manage
  ///  all of `msg.sender`'s assets
  /// @dev Emits the ApprovalForAll event. The contract MUST allow
  ///  multiple operators per owner.
  /// @param _operator Address to add to the set of authorized operators
  /// @param _approved True if the operator is approved, false to revoke approval
  function setApprovalForAll(address _operator, bool _approved) external {
        require(_operator != msg.sender);
        operators[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
  }

  /// @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 returns (address) {
      require(_tokenExists(_tokenId));
      return playerTokenToApproved[_tokenId];
  }

  /// @notice Query if a contract implements an interface
  /// @param interfaceID The interface identifier, as specified in ERC-165
  /// @dev Interface identification is specified in ERC-165. This function
  ///  uses less than 30,000 gas.
  /// @return `true` if the contract implements `interfaceID` and
  ///  `interfaceID` is not 0xffffffff, `false` otherwise
  function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
      return (
          interfaceID == this.supportsInterface.selector || // ERC165
          interfaceID == 0x5b5e139f || // ERC721Metadata
          interfaceID == 0x80ac58cd || // ERC-721
          interfaceID == 0x780e9d63);  // ERC721Enumerable
  }

  // Internal utility functions: These functions all assume that their input arguments
  // are valid. We leave it to public methods to sanitize their inputs and follow
  // the required logic.

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

  /// @dev Checks if a given address currently has transferApproval for a particular PlayerToken.
  /// @param _claimant the address we are confirming PlayerToken is approved for.
  /// @param _tokenId PlayerToken id, only valid when > 0
  function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) {
      return playerTokenToApproved[_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 PlayerToken on auction, and
  ///  there is no value in spamming the log with Approval events in that case.
  function _approve(uint256 _tokenId, address _approved) internal {
      playerTokenToApproved[_tokenId] = _approved;
  }

}
/// @title The facet of the CSports core contract that manages ownership, ERC-721 compliant.
/// @author CryptoSports, Inc. (http://cryptosports.team)
/// @dev Ref: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md#specification
/// See the CSportsCore contract documentation to understand how the various contract facets are arranged.
function _approve(uint256 _tokenId, address _approved) internal {
      playerTokenToApproved[_tokenId] = _approved;
  }
/// @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 PlayerToken on auction, and
///  there is no value in spamming the log with Approval events in that case.