contract Rabbots is ERC721Enumerable, Ownable {
    
    uint256 public constant MAX_SUPPLY = 10000;
    uint256 public constant RESERVED_SUPPLY = 350;
    uint256 internal _price;

    uint private nonce = 0;
    uint[MAX_SUPPLY-RESERVED_SUPPLY] private indices; 

    string internal _baseTokenURI;
    uint256 public numTokens;
    bool internal baseURISet = false;
    bool public saleHasBegun = false;
    address public _vault;
    
    constructor ()
    ERC721("Rabbots", "RabbotsRabbits") 
    {
        numTokens = 0;
    }

    /* Public functions */
    function mint(uint256 numToMint) public payable
    {        
        require(saleHasBegun, 'Cannot be done - sale has not yet begun.');
        require(numTokens < MAX_SUPPLY, 'All Rabbits have been claimed from The Warren.');
        require(numToMint > 0 && numToMint <= 15, 'You can mint no fewer than 1, and no more than 15 at a time.');
        require(numTokens + numToMint <= MAX_SUPPLY, "Cannot be done - this will mint more than the maximum supply.");
        require(msg.value >= _price * numToMint, 'Ether value sent is insufficient.');

        uint id;
        for (uint256 i = 0; i < numToMint; i++)
        {
            id = randomIndex();
            _mint(msg.sender, id);
            numTokens = numTokens + 1;
        }
    }

    function getPrice() external view returns (uint256) 
    {
        return _price;
    }

    function _baseURI() internal view virtual override returns (string memory) 
    {
        return _baseTokenURI;
    }   

    /* Special thanks to Larva Labs for the following */    
    function randomIndex() internal returns (uint) 
    {
    uint totalSize = MAX_SUPPLY - numTokens;
    uint index = uint(keccak256(abi.encodePacked(nonce, msg.sender, block.difficulty, block.timestamp))) % totalSize;
    uint value = 0;

    if (indices[index] != 0) {
        value = indices[index];
    } else {
        value = index;
    }

    // Move last value to selected position
    if (indices[totalSize - 1] == 0) {
        // Array position not initialized, so use position
        indices[index] = totalSize - 1;
    } else {
        // Array position holds a value so use that
        indices[index] = indices[totalSize - 1];
    }
    nonce++;
    // Don't allow a zero index, start counting at 1
    return value + RESERVED_SUPPLY;
    }

    /* Owner functions */
    function setBaseURI(string memory baseTokenURI) public onlyOwner
    { 
        require(!saleHasBegun, "Cannot be done - sale has already begun.");
        _baseTokenURI = baseTokenURI;
        baseURISet = true;
    }

    function reserve(uint256 numToReserve) public onlyOwner
    {
        require(!saleHasBegun, "Sale has already begun.");
        require(numTokens < RESERVED_SUPPLY, 'All reserved Rabbits have been claimed.');
        require(numToReserve > 0, 'You can reserve no fewer than 1 at a time.');
        require(numTokens + numToReserve <= RESERVED_SUPPLY, "Cannot be done - this will mint more than the reserved supply.");

        for (uint256 i = 0; i < numToReserve; i++)
        {
            _mint(msg.sender, numTokens + 1);
            numTokens = numTokens + 1;
        }
    }
    
    function setVault(address newVaultAddress) public onlyOwner
    {
        _vault = newVaultAddress;
    }

    function beginSale(uint256 price) public onlyOwner
    {
        require(!saleHasBegun, "Sale has already begun.");
        require(address(_vault) != address(0), 'Vault does not exist yet.');
        require(baseURISet, "Base URI has not yet been set.");
        require(price >= 0.01 ether, "_price must be set higher.");
        _price = price;
        saleHasBegun = true;
    }

    function withdrawFunds(uint256 amount) public onlyOwner
    {
        require(address(_vault) != address(0), 'Vault does not exist yet.');
        require(payable(_vault).send(amount));
    }
}
//        #&@@@*                                                     ..            
//    @@@@@@@@@@@@@@%                                          %@@@@@@@@@@@@       
//  /@@@@#       @@@@@@                                    ,@@@@@@@.   .@@@@@@@#   
//  @@@@(          #@@@@.                                ,@@@@@@           .@@@@@( 
// ,@@@@/            @@@@#                              &@@@@&                @@@@(
//  @@@@@             @@@@@                           .@@@@@                  %@@@@
//   @@@@@            .@@@@@                         &@@@@.                  *@@@@@
//    (@@@@@            @@@@%                       @@@@&                .%@@@@@@, 
//      .@@@@@(          @@@@*                    .@@@@*            ,@@@@@@@@@#    
//         @@@@@%         @@@@.                  ,@@@@.         (@@@@@@@&          
//           #@@@@@       .@@@@*                @@@@@       .@@@@@@@%              
//             %@@@@@(     @@@@(              ,@@@@%      @@@@@@@.                 
//                @@@@@@.  .@@@@             /@@@@#     @@@@@@/                    
//                  @@@@@/  @@@@@  ,,,*/(/**.@@@@/    (@@@@#                       
//                   .@@@@%.#@@@@@@@@@@@@@@@@@@@@@&/ &@@@@                         
//                     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                          
//                   #@@@@@@@@#.                 .%@@@@@@@%                        
//                 (@@@@@@&. ,.                      .&@@@@@@.                     
//               .@@@@@@/                              .(@@@@@@                    
//               @@@@@@, .         *                      %@@@@@.                  
//              @@@@@@  #@@@@#@@@@&,         &@@/          ,@@@@@.                 
//             .@@@@@/    (@@@@@.             /@@@@@@@@@#   #@@@@&                 
//             ,@@@@@.    @@@@@@@(.         .@@@@@@@@&..    *@@@@@                 
//              @@@@@(   (@@@ ,@@@@&.      ,@@@/  ,@@@/ .   @@@@@@                 
//              /@@@@@.           ##       .       (@@*    %@@@@@.                 
//               *@@@@@(                                 /@@@@@&                   
//                 @@@@@@%                            *@@@@@@.                     
//                   &@@@@@@@@@@@(.               .   @@@@@                        
//                      %@@@@@@@@, .@@/     .#.       @@@@.                        
//                         @@@@@@  @@@@%    @@@&     /@@@@                         
//                         #@@@@@@@@@@@@@@@@@@@@#,   %@@@#                         
//                        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*                        
//                        @@@@&/   .@@@@    @@@@    ,/#&&*                         
//                                  (@@&     (#                                    
//
// Rabbots NFT
//
// 10,0000 ultra-cute Rabbits
// 10,000 Robots of mass destruction
// ...what could possibly go wrong? ;)
// 
// Website: https://rabbots.io/
// Twitter: https://twitter.com/RabbotsNFT
function setBaseURI(string memory baseTokenURI) public onlyOwner
{ 
    require(!saleHasBegun, "Cannot be done - sale has already begun.");
    _baseTokenURI = baseTokenURI;
    baseURISet = true;
}

/* Owner functions */