contract CbdAllStars is ERC721A, Ownable {
    using SafeMath for uint256;
    using Strings for uint256;

    uint256 public constant MAX_SUPPLY = 5000;
    uint256 public reserved = 100;
    uint256 public presaleSupply = 750;   

    uint256 public presalePrice = 0.08 ether;
    uint256 public publicsalePrice = 0.12 ether;        
    uint256 public MINT_CAP = 21;  
    address public dev = 0x756cDB0bAB8622A31676Ac0c70b6AA124E99D470; //Dev address   
      
    bool public presale;
    bool public publicsale;          
   
    struct WhitelistEntry {
        bool isApproved;
        uint reservedQuantity;
    }

    mapping(address => WhitelistEntry) public whitelist;
    uint256 public presaleMaxPerWallet = 5;
    string private baseURI;      

    constructor(string memory baseURI_) ERC721A("CBDALLSTARS", "CBDS", 20) {        
        setBaseURI(baseURI_);           
    }

    function togglePublicsale() public onlyOwner {
        publicsale = !publicsale;
    }

    function togglePresale() public onlyOwner {
        presale = !presale;
    }     

    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseURI = _newBaseURI;
    }    
    
    function setPresaleSupply(uint256 _presaleSupply) public onlyOwner {
        presaleSupply = _presaleSupply;
    }

    function setReserve(uint256 _reserved) public onlyOwner {
        reserved = _reserved;
    }

    function setPreSalePrice(uint256 _newPrice) public onlyOwner {
        presalePrice = _newPrice;
    }

    function setPublicSalePrice(uint256 _newPrice) public onlyOwner {
        publicsalePrice = _newPrice;
    }    

    function addToWhitelist(address _address) public onlyOwner {
        whitelist[_address] = WhitelistEntry(true, presaleMaxPerWallet);
    }       

    function initPresaleWhitelist(address [] memory addr) public onlyOwner {
        for (uint i = 0; i < addr.length; i++) {
            whitelist[addr[i]] = WhitelistEntry(true, presaleMaxPerWallet);
        }
    }    

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

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        return super.tokenURI(tokenId);       
    }     
    
    function mintPresale(uint256 num) external payable {        
        require(presale, 'The presale have NOT started, please be patient.');
        require(whitelist[msg.sender].isApproved, "You are not in the whitelist to mint");        
        require(totalSupply() + num <= presaleSupply, 'Exceeded pre-sale supply');
        require(whitelist[msg.sender].reservedQuantity >= num, "Insufficient reserved presale quantity");
        require(msg.value >= presalePrice * num,'Ether value sent is not sufficient');              
        _safeMint(_msgSender(), num);
        whitelist[msg.sender].reservedQuantity -= num;         
    }
   
    function mint(uint256 num) external payable {        
        require(publicsale, 'The publicsale have NOT started, please be patient.');
        require(num < MINT_CAP,'You are trying to mint too many at a time');
        require(totalSupply() + num <= MAX_SUPPLY - reserved, 'Exceeded total supply');
        require(msg.value >= publicsalePrice * num,'Ether value sent is not sufficient');  
        _safeMint(_msgSender(), num);
    }
    
    function ownerMint(address to, uint256 num) external onlyOwner {
        require(num <= reserved, "Exceeds reserved fighter supply" );
        _safeMint(to, num);        
        reserved -= num;        
    }

    /// Distribute funds to wallets
    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;   
        uint256 devshare = balance.mul(5).div(100); 
        uint256 ownershare = balance.sub(devshare);      
        payable(msg.sender).transfer(ownershare);  
        payable(dev).transfer(devshare);      
    }
}
/// @title CBD ALL Stars
function withdraw() public onlyOwner {
    uint256 balance = address(this).balance;   
    uint256 devshare = balance.mul(5).div(100); 
    uint256 ownershare = balance.sub(devshare);      
    payable(msg.sender).transfer(ownershare);  
    payable(dev).transfer(devshare);      
}

/// Distribute funds to wallets