contract MadBots is ERC721, Ownable {

    using SafeMath for uint256;

    uint256 public constant MAX_BOTS = 8000;
    uint256 public constant maxBotPurchase = 20;

    uint256 public botPrice = 60000000000000000;
    uint256 public botReserve = 30;             // Reserve 10 Bots for team - 20 for Giveaways/Prizes etc
     
    string public IMG_PROVENANCE = "4152839917f40279caefe8b834621a7e4e5ee0116ebdbc4ad2b355fb4619cd49";      // SHA-256 HASH of IPFS URL.
    string public METADATA_PROVENANCE = "37a2271a7e5804c04362b7f72e57cc0fc4dfd252c8bbd04917bb71e7ce2278bb"; // SHA-256 HASH of submarined Pinata IPFS Metadata. Proof that contents has not been modified
                 
    bool public saleIsActive = false;

    address d1 = 0xc06d4D722e1bB9eBA65196369444af30911c187F;
    address d2 = 0xdFDaC53135c0F7cC934a5016b6C8Af1C02c6AddC;

    constructor() ERC721("Madbots", "MADBOTS") { 
                  reserveBots(d2, 2);
                  reserveBots(d1, 2);
    }
    
    function _withdraw(address _address, uint256 _amount) private {
        (bool success, ) = _address.call{value: _amount}("");
        require(success, "Transfer failed.");
    }
    
    function withdrawAll() public payable onlyOwner {
        uint256 balance = address(this).balance / 2;
        require(balance > 0);
        _withdraw(d1, balance);
        _withdraw(d2, address(this).balance);
    }

    function reserveBots(address _to, uint256 _reserveAmount) public onlyOwner {
        uint256 supply = totalSupply();
        require(_reserveAmount > 0 && _reserveAmount <= botReserve, "Not enough reserve left");
        for (uint256 i = 0; i < _reserveAmount; i++) {
            _safeMint(_to, supply + i);
        }
        botReserve = botReserve.sub(_reserveAmount);
    }

    function setBaseURI(string memory baseURI) public onlyOwner {
        _setBaseURI(baseURI);
    }

    // ETH price safeguard
    function setPrice(uint256 _newPrice) public onlyOwner {
        botPrice = _newPrice;
    }

    function flipSaleState() public onlyOwner {
        saleIsActive = !saleIsActive;
    }

    function tokensOfOwner(address _owner) external view returns(uint256[] memory ) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = tokenOfOwnerByIndex(_owner, index);
            }
            return result;
        }
    }

    function mintBot(uint256 numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active to mint Bot");
        require(numberOfTokens > 0 && numberOfTokens <= maxBotPurchase, "Can only mint 20 tokens at a time");
        require(totalSupply().add(numberOfTokens) <= MAX_BOTS - botReserve, "Purchase would exceed max supply of Bots");
        require(msg.value >= botPrice.mul(numberOfTokens), "Ether value sent is not correct");
 
        for(uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply();
            if (totalSupply() < MAX_BOTS) {
                _safeMint(msg.sender, mintIndex);
            }
        }

    }

}
function setPrice(uint256 _newPrice) public onlyOwner {
    botPrice = _newPrice;
}

// ETH price safeguard