contract ERC721Mintable is ERC721Enumerable ,Ownable{
    using SafeMath for uint256;
    using Address for address;

    mapping(uint256 => mapping (bytes32 => uint256)) internal Config;
    // id => creators
    mapping(uint256 => string) public tokenUris;
    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */

    constructor (string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }
    
    // function test_owners(uint256 _id) external view returns (address) {
    //     return _owners[_id];
    // }
    
    // function test_exists(uint256 tokenId) external view returns (bool) {
    //     return _owners[tokenId] != address(0);
    // }
    
    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 _id) external view override returns (string memory) {
        require(_exists(_id), "ERC721Metadata: URI query for nonexistent token");
        return tokenUris[_id];
    }
    
    function setURI(string calldata _uri, uint256 _id) external onlyManager {
        //require(msg.sender == creators[_id]);
        tokenUris[_id] = _uri;
        emit URI(_uri, _id);
    }
    
    function setBatchURI(string calldata _uri, uint256[] calldata _id) external onlyManager {
        for (uint256 i = 0; i < _id.length; ++i) {
            uint256 id = _id[i];
            tokenUris[id] = _uri;
            emit URI(_uri, id);
        }
    }
    
    
    function setMultipleBatchURI(string[] calldata _uri, uint256[] calldata _id) external onlyManager {
        require(_id.length == _uri.length, "_ids and _uri array length must match.");
        for (uint256 i = 0; i < _id.length; ++i) {
            uint256 id = _id[i];
            string memory uri = _uri[i];
            tokenUris[id] = uri;
            emit URI(uri, id);
        }
    }
    
    
    function safeMint(uint256 _id, address _to, string calldata _uri) public onlyManager {
        require(_to != address(0), "ERC721: mint to the zero address");
        require(!_exists(_id), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), _to, _id);

        _balances[_to] += 1;
        _owners[_id] = _to;
        tokenUris[_id] = _uri;

        emit Transfer(address(0), _to, _id);
        emit URI(_uri, _id);
        
        
        if (_to.isContract()) {
            require(_checkOnERC721Received(address(0), _to, _id, ''), "ERC721: transfer to non ERC721Receiver implementer");
        }
    }
    
    // Batch mint tokens. Assign directly to _to[].
    function safeBatchMint(uint256[] calldata _id, address[] calldata _to, string calldata _uri) external onlyManager {
        require(_id.length == _to.length, "_ids and _to array length must match.");
        //require(msg.sender == creators[_id]);
        for (uint256 i = 0; i < _to.length; ++i) {

            address to = _to[i];
            uint256 id = _id[i];
            
            safeMint(id, to, _uri);
        }
    }
    
    //mint tokens. Assign directly to _to[].
    function safeMultipleBatchMint(uint256[] calldata _id, address[] calldata _to, string[] calldata _uri) external onlyManager {
        require(_id.length == _to.length, "_ids and _to array length must match.");
        require(_id.length == _uri.length, "_ids and _uri array length must match.");
        for (uint256 i = 0; i < _to.length; ++i) {

            address to = _to[i];
            uint256 id = _id[i];
            string calldata uri = _uri[i];
            
            safeMint(id, to, uri);
        }
    }
    
    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeBatchTransferFrom(address _from, address[] calldata _to, uint256[] calldata _tokenId) external {
        require(_to.length == _tokenId.length, "_to and _tokenId array length must match.");
        for (uint256 i = 0; i < _to.length; ++i) {
            address to = _to[i];
            uint256 tokenId = _tokenId[i];
            safeTransferFrom(_from, to, tokenId, "");
        }
    }
    
    function setSingleProperty(bytes32 _property, uint256 _id, uint256 _value) external onlyManager {
        Config[_id][_property] = _value;
    }
    
    function setMultipleProperty(bytes32 _property, uint256[] calldata _id, uint256[] calldata _value) external onlyManager {
        //require(msg.sender == creators[_id]);
        for (uint256 i = 0; i < _id.length; ++i) {
            Config[_id[i]][_property] = _value[i];
        }
    }
    
    function setBatchProperty(bytes32[] calldata _property, uint256[] calldata _id, uint256[] calldata _value) external onlyManager {
        //require(msg.sender == creators[_id]);
        for (uint256 i = 0; i < _id.length; ++i) {
            Config[_id[i]][_property[i]] = _value[i];
        }
    }
    
    function getSingleProperty(bytes32 _property, uint256 _id) public view returns (uint256 _value) {
        return Config[_id][_property];
    }
    
    function getMultipleProperty(bytes32 _property, uint256[] calldata _id) public view returns (uint256[] memory _value) {
        uint256[] memory K = new uint256[](_id.length);
        //require(msg.sender == creators[_id]);
        for (uint256 i = 0; i < _id.length; ++i) {
            K[i] = Config[_id[i]][_property];
        }
        
        return K;
    }
    
    function getBatchProperty(bytes32[] calldata _property, uint256[] calldata _id) public view returns (uint256[] memory _value) {
        uint256[] memory K = new uint256[](_id.length);
        //require(msg.sender == creators[_id]);
        for (uint256 i = 0; i < _id.length; ++i) {
            K[i] = Config[_id[i]][_property[i]];
        }
        
        return K;
    }
    
    
}
function safeBatchMint(uint256[] calldata _id, address[] calldata _to, string calldata _uri) external onlyManager {
    require(_id.length == _to.length, "_ids and _to array length must match.");
    //require(msg.sender == creators[_id]);
    for (uint256 i = 0; i < _to.length; ++i) {

        address to = _to[i];
        uint256 id = _id[i];
            
        safeMint(id, to, _uri);
    }
}

// Batch mint tokens. Assign directly to _to[].