contract Defenders is ERC721 {

    modifier callerIsUser() {
        require(tx.origin == msg.sender, "The caller is another contract");
        _;
    }

    modifier claimStarted(uint256 amount) {
        require(
           ( whitelistSaleActive && whitelist[msg.sender] && amount==1) || publicSaleActive,
            "For you, Sale is not active"
        );

        _;
    }

    uint256 private claimPrice =  0.035 ether;
    uint256 private totalTokens = 10000;
    uint256 private totalMintedTokens = 0;
    uint128 private reserved = 91; 
    uint256 private totalReserved = 0;
    uint256 private claimUpto = 91;
    string private round = "Private(91)";
    string private baseURI =
        "https://defendersvscretins.mypinata.cloud/ipfs/Qmep4LhKqHF8w6bp8XL4Dq7rAfFaWXr7L6FXxbka7p5QQx/";
    address comeBackAlive1 = 0x93Bda139023d582C19D70F55561f232D3CA6a54c;
    address comeBackAlive2 = 0xa1b1bbB8070Df2450810b8eB2425D543cfCeF79b;
    address EPlus = 0xf611791fB2e1b2f4844Edf84D5588BAEd74E9DB7;
    address dev = 0xcF21aCc0684Dd9969B48D43c96502C39386f2C6D;
    mapping(address => bool) whitelist;
    bool private whitelistSaleActive = false;
    bool private publicSaleActive = false;
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIdentifiers;

    constructor() ERC721("Defenders of Kiev", "DOK") {}

    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        payable(comeBackAlive1).transfer((balance * 10) / 100);
        payable(comeBackAlive2).transfer((balance * 10) / 100);
        payable(EPlus).transfer((balance * 20) / 100);
        payable(dev).transfer((balance * 5) / 100);
        payable(msg.sender).transfer(address(this).balance);
    }

    /**
     * @dev Sets the base URI for the API that provides the NFT data.
     */
    function setBaseTokenURI(string memory _uri) external onlyOwner {
        baseURI = _uri;
    }


    function setRound(string memory _round,uint256 addTokens,uint256 _claimPrice,bool _whitelistSaleActive, bool _publicSaleActive) external onlyOwner {
        round=_round;
        claimUpto=claimUpto+addTokens;
        claimPrice = _claimPrice;
        whitelistSaleActive=_whitelistSaleActive;
        publicSaleActive=_publicSaleActive;
    }


    function getRound() external view returns (string memory,uint256 ,uint256 ,bool , bool ) {
        return (round, claimUpto, claimPrice, whitelistSaleActive, publicSaleActive);
    }

    function reserveNFT(uint256 amount) external onlyOwner {        
        require(totalMintedTokens < totalTokens, "No tokens left to reserve");
        require(totalReserved+amount <= reserved,"NFTS have been already reserved, you can not reserve more");
        uint256[] memory tokenIds = new uint256[](amount);

        totalMintedTokens += amount;
	      	totalReserved += amount;

        for (uint256 i; i < amount; i++) {
          _tokenIdentifiers.increment();
            tokenIds[i] = _tokenIdentifiers.current();
        }
        _batchMint(msg.sender, tokenIds);
    }


    function claimTokens(uint256 amount)
        external
        payable
        callerIsUser
        claimStarted(amount)
    {
        require(
            msg.value >= claimPrice * amount,
            "Not enough Ether to claim the tokens"
        );

        require(totalTokens >= totalMintedTokens+amount, "No tokens left to be claimed");
        require(claimUpto >= totalMintedTokens+amount, "No more tokens can be claimed in this round");

        uint256[] memory tokenIds = new uint256[](amount);
        totalMintedTokens += amount;

        for (uint256 i; i < amount; i++) {
            _tokenIdentifiers.increment();
            tokenIds[i] = _tokenIdentifiers.current();
        }
        if(whitelistSaleActive){
            whitelist[msg.sender]=false;
        }
        _batchMint(msg.sender, tokenIds);
    }

    /**
     * @dev Returns the tokenId by index
     */
    function tokenByIndex(uint256 tokenId) external view returns (uint256) {
        require(
            _exists(tokenId),
            "ERC721: operator query for nonexistent token"
        );

        return tokenId;
    }

    /**
     * @dev Returns the base URI for the tokens API.
     */
    function baseTokenURI() external view returns (string memory) {
        return baseURI;
    }

    /**
     * @dev Returns the total supply
     */
    function totalSupply() external view virtual returns (uint256) {
        return totalMintedTokens;
    }

    /**
     * @dev See {ERC721}.
     */
    function _baseURI() internal view virtual override returns (string memory) {
        return baseURI;
    }

    function setWhitelistAll(address[] memory _adds) public onlyOwner {
        for(uint256 i = 0; i < _adds.length; i++) {
            address tmp = address(_adds[i]);
            whitelist[tmp] = true;
        }
    }

}
function totalSupply() external view virtual returns (uint256) {
    return totalMintedTokens;
}

/**
 * @dev Returns the total supply
 */