contract BigToysMintPass is ERC165, IERC1155, IERC1155MetadataURI, Ownable {
    
    using Address for address;
    using Strings for uint256;
    using SafeMath for uint256;
    
    address private _manager;
    
    uint256 private _price = 300000000000000000; //300000000000000000
    uint256 private _totalAllowedMints = 1500;
    uint256 private _totalAllowedMintsPerUser = 10;
    uint256 private _totalMints;
    
    address _owner1 = 0xF54C396658E197c37828bf8812834C42c29A4Db1;
    address _owner2 = 0xe89e0199c7CCCA4Ec86177D0F9F9Dd11fa197c26;
    
    mapping(uint256 => mapping(address => uint256)) private _balances; // Mapping from token ID to account balances
    mapping(address => mapping(address => bool)) private _operatorApprovals; // Mapping from account to operator approvals
    mapping(address => uint256) private _mints;

    string private _uri;
    
    constructor() {
        _manager = msg.sender;
        _uri = "https://bigtoysnft.com/api/nft/0";
    }
    
    //Read Functions======================================================================================================================================================
    
    function balanceOf(address account, uint256 id) public view override returns (uint256) {
        require(account != address(0), "ERC1155: balance query for the zero address");
        return _balances[id][account];
    }
    
    function uri(uint256 id) external view override returns (string memory) {
        return _uri;
    }

    function balanceOfBatch(address[] memory accounts, uint256[] memory ids) external view override returns (uint256[] memory) {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }
    
    
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC1155).interfaceId ||
            interfaceId == type(IERC1155MetadataURI).interfaceId ||
            super.supportsInterface(interfaceId);
    }
    
    function isApprovedForAll(address account, address operator) public view override returns (bool) {
        return _operatorApprovals[account][operator];
    }
    
    function webData(address user) external view returns(uint256 totalAllowedMintsPerUser, uint256 leftNFT, uint256 userMints, uint256 price) {
        totalAllowedMintsPerUser = _totalAllowedMintsPerUser;
        leftNFT = _totalAllowedMints - _totalMints;
        userMints = _mints[user];
        price = _price;
    }
    
    
    function getItemPrice() external view returns (uint256) {
		return _price;
	}
	
    //Owner Functions
    function changePrice(uint256 newPrice) external onlyOwner{
        _price = newPrice;
    }
    
    function changeTotalAllowedMintsPerUser(uint256 newTotalAllowedMintsPerUser) external onlyOwner {
        _totalAllowedMintsPerUser = newTotalAllowedMintsPerUser;
    }
    
    function changeTotalAllowedMints(uint256 newTotalAllowedMints) external onlyOwner {
        _totalAllowedMints = newTotalAllowedMints;
    }
    
    function setUri(string calldata Uri) external onlyOwner {
       _uri = Uri;
    }
    
    // withdraw the earnings to pay for the artists & devs :)
	function withdraw() public payable onlyOwner {
	    require(address(this).balance > 0, "No balance to withdraw");
	    
	    
	    uint256 sixtyfive = address(this).balance.mul(65).div(100); //65%
	    uint256 thirtyfive = address(this).balance.mul(35).div(100); //35%
	    require(payable(_owner1).send(sixtyfive));
	    require(payable(_owner2).send(thirtyfive));
	
	}
    
    function giveawayMint(address[] calldata winners) external onlyOwner {
        uint256 length = winners.length;
        for(uint256 t; t < length; ++t) {
            
        ++_balances[0][winners[t]];
        
        emit TransferSingle(msg.sender, address(0), winners[t], 0, 1);
        }
        
        _totalMints += length;
    }
    
    //User Functions======================================================================================================================================================
    function setApprovalForAll(address operator, bool approved) external override {
        require(msg.sender != operator, "ERC1155: setting approval status for self");

        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function safeTransferFrom(address from,address to,uint256 id,uint256 amount,bytes memory data) external override {
        require(
            from == msg.sender || isApprovedForAll(from, msg.sender),
            "ERC1155: caller is not owner nor approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }

    function safeBatchTransferFrom(address from,address to,uint256[] memory ids,uint256[] memory amounts,bytes memory data) public virtual override {
        require(
            from == msg.sender || isApprovedForAll(from, msg.sender),
            "ERC1155: transfer caller is not owner nor approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }
    
    function mintBigToyPass(address account, uint256 amount) external payable {
        require(msg.value == _price * amount, "Big Toys NFT: Insufficient balance");
        require(_mints[msg.sender] + amount <= _totalAllowedMintsPerUser, "Big Toys NFT: Max mints reached");
        require(_totalMints + amount <= _totalAllowedMints, "Big Toys NFT: Insufficient NFTs for minting");
        
        _mints[msg.sender] += amount;
        _totalMints += amount;
        
        _balances[0][account] += amount;
        
        emit TransferSingle(msg.sender, address(0), account, 0, amount);

    }
    
    //Internal Functions
    function _safeTransferFrom(address from,address to,uint256 id,uint256 amount,bytes memory data) internal virtual {

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(msg.sender, from, to, id, amount);

        _doSafeTransferAcceptanceCheck(msg.sender, from, to, id, amount, data);
    }

    function _safeBatchTransferFrom(address from,address to,uint256[] memory ids,uint256[] memory amounts,bytes memory data) internal virtual {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(msg.sender, from, to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(msg.sender, from, to, ids, amounts, data);
    }
   
    function _doSafeTransferAcceptanceCheck(address operator, address from, address to, uint256 id, uint256 amount, bytes memory data) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver(to).onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155Receiver(to).onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

}
function changePrice(uint256 newPrice) external onlyOwner{
    _price = newPrice;
}

//Owner Functions