contract LazerSoda is ERC721Enumerable, Ownable {

    using Strings for uint256;
    
    uint256 constant private _priceWithBloot = 0.05 ether;
    uint256 constant private _price = 0.06 ether;
    uint256 constant private MAX_TOTAL_PUBLIC = 300;
    uint256 constant private MAX_TOTAL_DONORS = 200;
    uint256 constant private MAX_TOTAL = 500;
    
    address private constant WITHDRAWAL_ADDRESS = 0x0A0Da512157d16f456C837a93a91c12f4B703672;
    address private constant BLOOTMF_ADDRESS = 0xff006C70Ccfe8552E344B299d28AA84CEfda8B11;
    address private constant BLOOT_ADDRESS = 0x4F8730E0b32B04beaa5757e5aea3aeF970E5B613;
    
    string _baseTokenURI;
    bool public _paused = true;
    uint256 private donorMintCount;
    uint256 private publicMintCount;
    
    mapping(address => bool) private diamondMap;
    mapping(address => bool) private founderMap;

    ERC721 bloot = ERC721(BLOOT_ADDRESS);
    IBlootMF blootMF = IBlootMF(BLOOTMF_ADDRESS);

    constructor(string memory baseURI) ERC721("Lazer Soda", "LAZERSODA")  {
        setBaseURI(baseURI);
    }
    
    // Public Mint
    //
    function mint(uint256 num) public payable {
        uint256 supply = totalSupply();
        require(!_paused, "Minting is paused" );
        require(publicMintCount + 1 <= MAX_TOTAL_PUBLIC, "Exceeds maximum supply");
        require(num < 10, "You can mint a maximum of 10 Sodas in one transaction" );
        require(msg.value >= _price * num, "Ether sent is not correct" );
       
        for(uint256 i = 1; i <= num; i++){
            publicMintCount++;
            _safeMint( msg.sender, supply + i );
        }
    }
    
    // Mint with Bloot or BlootMF
    //
    function mintWithBloot(uint256 num) public payable {
        require((bloot.balanceOf(msg.sender) >= 1) || (blootMF.balanceOf(msg.sender) >= 1), "You need to hold a Bloot or BlootMF to mint");
       
        uint256 supply = totalSupply();
        require(supply + 1 <= MAX_TOTAL, "Exceeds maximum supply");
        require(!_paused, "Minting is paused" );
        require(publicMintCount + 1 <= MAX_TOTAL_PUBLIC, "Exceeds maximum public supply");
        require(num < 10, "You can mint a maximum of 10 Sodas in one transaction" );
        require(msg.value >= _priceWithBloot * num, "Ether sent is not correct" );

        for(uint256 i = 1; i <= num; i++){
            publicMintCount++;
            _safeMint( msg.sender, supply + i );
        }
    }
    
    
    // Mint as Diamond (can mint one)
    //
    function mintAsDiamond() public payable {
        
        // check if paused
        require(!_paused, "Minting is paused" );
        
        // diamond-related checks
        require(blootMF.isDiamond(msg.sender), "Sender is not a Diamond");
        require(!diamondMap[msg.sender], "Sender has already minted as Diamond");
       
        // supply checks
        uint256 supply = totalSupply();
        require(supply + 1 <= MAX_TOTAL, "Exceeds maximum supply");
        require(donorMintCount + 1 <= MAX_TOTAL_DONORS, "Exceeds maximum supply for donors");
      
        // update state
        donorMintCount++;
        diamondMap[msg.sender] = true;
        
         _safeMint(msg.sender, supply + 1);
    }
    
    // Mint as Founder (can mint one)
    //
    function mintAsFounder() public payable {
        
        //check if paused
        require( !_paused, "Minting is paused" );
        
        // founder-related checks
        require( blootMF.isFounder(msg.sender), "Sender is not a Founder");
        require(!founderMap[msg.sender], "Sender has already minted as Founder");
        
        // check supply
        uint256 supply = totalSupply();
        require(supply + 1 <= MAX_TOTAL, "Exceeds maximum supply");
        require(donorMintCount + 1 <= MAX_TOTAL_DONORS, "Exceeds maximum supply for donors");
        
        // update state
        donorMintCount++;
        founderMap[msg.sender] = true;
        
         _safeMint(msg.sender, supply + 1);
    }
    
    // Owner mint (to mint residual NFTs to donors)
    //
    function mintAsOwner() public onlyOwner {
        uint256 supply = totalSupply();
        require( !_paused, "Minting is paused" );
        require(supply + 1 <= MAX_TOTAL, "Exceeds maximum supply");
         _safeMint(msg.sender, supply + 1);
    }
    
    function hasFounderMinted(address _founder) public view returns (bool) {
        return founderMap[_founder];
    }
    
    function hasDiamondMinted(address _diamond) public view returns (bool) {
        return diamondMap[_diamond];
    }
    
    function getDonorMintCount() public view returns (uint256) {
        return donorMintCount;
    }
    
    function getPublicMintCount() public view returns (uint256) {
        return publicMintCount;
    }

    function walletOfOwner(address _owner) public view returns(uint256[] memory) {
        uint256 tokenCount = balanceOf(_owner);

        uint256[] memory tokensId = new uint256[](tokenCount);
        for(uint256 i; i < tokenCount; i++){
            tokensId[i] = tokenOfOwnerByIndex(_owner, i);
        }
        return tokensId;
    }

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

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

    function pause(bool val) public onlyOwner {
        _paused = val;
    }

    function withdrawAll() public payable onlyOwner {
        uint256 bal = address(this).balance;
        require(payable(WITHDRAWAL_ADDRESS).send(bal));
    }
}
function mint(uint256 num) public payable {
        uint256 supply = totalSupply();
        require(!_paused, "Minting is paused" );
        require(publicMintCount + 1 <= MAX_TOTAL_PUBLIC, "Exceeds maximum supply");
        require(num < 10, "You can mint a maximum of 10 Sodas in one transaction" );
        require(msg.value >= _price * num, "Ether sent is not correct" );
       
        for(uint256 i = 1; i <= num; i++){
            publicMintCount++;
            _safeMint( msg.sender, supply + i );
        }
    }
// Public Mint
//