contract BcaexOwnershipV2 is Ownable, AccessControl, ERC721 {
    using SafeMath for uint256;
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIds;
    // Mapping from token ID to the creator's address.
    mapping(uint256 => address) private tokenCreators;

    // Event indicating metadata was updated.
    event TokenURIUpdated(uint256 indexed _tokenId, string  _uri);

    constructor() public ERC721("BCAEX", "BCA") {
        addRootRole();
    }

    // ----------------------------
    // Create a new role identifier for the minter role
    bytes32 public constant ROOT_ROLE = keccak256("ROOT_ROLE");
    // Create a new role identifier for the minter role
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    function addRootRole() public onlyOwner {
        _setRoleAdmin(MINTER_ROLE, ROOT_ROLE);
        _setupRole(ROOT_ROLE,  msg.sender);
        _setupRole(MINTER_ROLE, msg.sender);
    }

    function addMinter(address _minter) public onlyOwner {
        _setupRole(MINTER_ROLE,  _minter);
    }

    function revokeMinter(address _minter) public onlyOwner {
        revokeRole(MINTER_ROLE,  _minter);
    }
    // ----------------------------

    /**
     * @dev Internal function creating a new token.
     * @param _uri string metadata uri associated with the token
     * @param _owner address of the _owner of the token.
     */
    function _createToken(string memory _uri, address _owner) internal returns (uint256) {
        _tokenIds.increment();
        uint256 newId = _tokenIds.current();
        _mint(_owner, newId);
        _setTokenURI(newId, _uri);
        tokenCreators[newId] = _owner;
        return newId;
    }

    /**
     * @dev Adds a new unique token to the supply.
     * @param _uri string metadata uri associated with the token.
     */
    function addNewToken(string memory _uri) public returns (uint256) {
      require(hasRole(MINTER_ROLE, msg.sender), "must be whitelisted to create tokens");
      _createToken(_uri, msg.sender);
    }

    /**
     * @dev Adds a new unique token to the supply by onlyOwner to creator.
     * @param _uri string metadata uri associated with the token.
     * @param _creator address of the owner of the token.
     */
    function addNewTokenToCreator(string memory _uri, address _creator) public onlyOwner returns (uint256) {
        return _createToken(_uri, _creator);
    }

    /**
     * @dev Adds a new unique token to the supply by onlyOwner to creator.
     * @param _uri string metadata uri associated with the token.
     * @param _creator address of the owner of the token.
     * @param _owner address of the owner of the token.
     */
    function addNewTokenToOwner(string memory _uri, address _creator, address _owner) public onlyOwner returns (uint256) {
        _tokenIds.increment();
        uint256 newId = _tokenIds.current();
        _mint(_owner, newId);
        _setTokenURI(newId, _uri);
        tokenCreators[newId] = _creator;
        return newId;
    }

    /**
    * @dev Gets the creator of the token.
    * @param _tokenId uint256 ID of the token.
    * @return address of the creator.
    */
    function tokenCreator(uint256 _tokenId) public view returns (address) {
        require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
        return tokenCreators[_tokenId];
    }

    /**
     * @dev Internal function for setting the token's creator.
     * @param _tokenId uint256 id of the token.
     * @param _creator address of the creator of the token.
     */
    function setTokenCreator(uint256 _tokenId, address _creator) public onlyOwner {
        require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
        tokenCreators[_tokenId] = _creator;
    }

    /**
     * @dev Updates the token metadata if the owner is also the creator.
     * @param _tokenId uint256 ID of the token.
     * @param _uri string metadata URI.
     */
    function updateTokenMetadata(uint256 _tokenId, string memory _uri)
      public
    {
        require(_isApprovedOrOwner(msg.sender, _tokenId), "BcaexOwnershipV2: transfer caller is not owner nor approved");
        require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
        _setTokenURI(_tokenId, _uri);
        emit TokenURIUpdated(_tokenId, _uri);
    }

    /**
     * @dev Updates the token metadata if the owner is also the creator.
     * @param _tokenId uint256 ID of the token.
     * @param _uri string metadata URI.
     */
    function updateTokenMetadataOnlyOwner(uint256 _tokenId, string memory _uri)
      public onlyOwner
    {
        require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
        _setTokenURI(_tokenId, _uri);
        emit TokenURIUpdated(_tokenId, _uri);
    }

    /**
     * @dev Deletes the token with the provided ID.
     * @param _tokenId uint256 ID of the token.
     */
    function deleteToken(uint256 _tokenId) public {
      require(_isApprovedOrOwner(msg.sender, _tokenId), "BcaexOwnershipV2: transfer caller is not owner nor approved");
      require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
      _burn(_tokenId);
      delete tokenCreators[_tokenId];
    }

    /**
     * @dev Deletes the token with the provided ID.
     * @param _tokenId uint256 ID of the token.
    */
    function deleteTokenOnlyOwner(uint256 _tokenId) public onlyOwner {
      require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
      _burn(_tokenId);
      delete tokenCreators[_tokenId];
    }
}
function setTokenCreator(uint256 _tokenId, address _creator) public onlyOwner {
    require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
    tokenCreators[_tokenId] = _creator;
}

/**
 * @dev Internal function for setting the token's creator.
 * @param _tokenId uint256 id of the token.
 * @param _creator address of the creator of the token.
 */