contract FractionalBricks is ERC721, ERC721Enumerable, Ownable {
  using Strings for uint256;
 

  string baseURI;
  string public notRevealedURI;
  // Immutable integers
  uint256 public cost = 700000000000000000; // 0.07 eth
  uint256 public  maxSupply = 10000;
  uint256 public  maxMintAmount = 1000;
  bool public paused = false;
  bool public publicMintIsActive = false;
  bool public revealed = false;
  bool public governanceWhitelisted = true;
  bool public onlyWhitelisted = true;
  address payable public payments;
  address[] public whitelistedAddresses;
  address[] public governanceWhitelistedAddresses;
 
  /**
  * Contract initialization.
  * The `constructor` is executed only once when the contract is created.
  */
  constructor(
    string memory _name,
    string memory _symbol,
    string memory _initBaseURI,
    string memory _initNotRevealedURI,
    address _payments)
    ERC721(_name, _symbol) {
    setBaseURI(_initBaseURI);
    setNotRevealedURI(_initNotRevealedURI);
    payments = payable(_payments);
    _safeMint(msg.sender, 1);
  }
 
  function governanceMint(uint256 _mintAmount) public payable {
    require(!paused, "Contract is paused!");
    require(_mintAmount == 1);
    uint256 supply = totalSupply();
    require(_mintAmount <= maxMintAmount, "Can only mint 1000 NFT's at a time!");
    require(supply + _mintAmount <= maxSupply, "Purchase would exceed max supply of this collection");
    if (msg.sender != owner()) {
        if(governanceWhitelisted == true){
            require(isGovernanceWhitelisted(msg.sender), "User is not governance whitelisted!");
            uint256 ownerTokenCount = balanceOf(msg.sender);
            require(ownerTokenCount < maxMintAmount);
            for(uint256 i = 1; i <= _mintAmount; i++){
                _safeMint(msg.sender, supply + i);
            }
        }
    }
  }
 
 
  // admin can mint them for giveaways, airdrops etc
    function adminMint(uint256 qty, address to) public onlyOwner {
        uint256 giveaway = 150;
        require(qty <= giveaway);
        require(qty > 0, "minimum 1 token");
        require(totalSupply() + qty <= maxSupply, "out of stock");
        for (uint256 i = 0; i < qty; i++) {
            _safeMint(to, totalSupply() + 1);
        }
    }
   
  function presaleMint(uint256 _mintAmount) public payable {
    require(!paused, "Contract is paused!");
    require(_mintAmount > 0);
    require(_mintAmount <= maxMintAmount, "Can only mint 1000 NFT's at a time!");
    require(totalSupply() + _mintAmount <= maxSupply, "Purchase would exceed max supply of this collection");
    if (msg.sender != owner()) {
        if(onlyWhitelisted == true){
            uint256 ownerTokenCount = balanceOf(msg.sender);
            require(ownerTokenCount < maxMintAmount);
            require(msg.value >= cost * _mintAmount, "Not Enough Funds!");
            for(uint256 i = 1; i <= _mintAmount; i++){
                _safeMint(msg.sender, totalSupply() + i);
            }
        }
    }
  }

  function Mint(uint256 _mintAmount) public payable {
    require(!paused, "Contract is paused!");
    require(_mintAmount > 0);
    require(_mintAmount <= maxMintAmount, "Can only mint 1000 NFT's at a time!");
    require(totalSupply() + _mintAmount <= maxSupply, "Purchase would exceed max supply of this collection");
    require(msg.value >= cost * _mintAmount, "Not Enough Funds!");
    if( publicMintIsActive == true){
        require(msg.value >= cost * _mintAmount, "Not Enough Funds!");
        for(uint256 i = 1; i <= _mintAmount; i++){
            _safeMint(msg.sender, totalSupply() + i);
        }
    }
  }
 
  function isGovernanceWhitelisted(address _user) public view returns(bool){
     for(uint256 i = 0; i < governanceWhitelistedAddresses.length; i++){
         if(governanceWhitelistedAddresses[i] == _user){
            return true;
         }
     }
     return false;
  }
 
  function isWhitelisted(address _user) public view returns(bool){
     for(uint256 i = 0; i < whitelistedAddresses.length; i++){
         if(whitelistedAddresses[i] == _user){
            return true;
         }
     }
     return false;
  }

  function walletOfOwner(address _owner) public view returns (uint256[] memory) {
    uint256 ownerTokenCount = balanceOf(_owner);
    uint256[] memory tokenIds = new uint256[](ownerTokenCount);
    for (uint256 i; i < ownerTokenCount; i++) {
      tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
    }
    return tokenIds;
  }

  function tokenURI(uint256 _tokenId) public view virtual override returns (string memory){
    require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
    //Check to the see if the function has been revealed or not if not show URI of Unrevealed image
    if(revealed == false){
        return notRevealedURI;
    }
    else{
    string memory currentBaseURI = _baseURI();
    return string(abi.encodePacked(currentBaseURI, "/", _tokenId.toString(), ".json"));
    }
}
  /**
     * Override for ERC721 & ERC721Enumerable
     */
    function supportsInterface(bytes4 interfaceId) public
        view
        override (ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
 
  /**
  * Override for ERC721 and ERC271Enumerable
  */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 tokenId
    ) internal override(ERC721, ERC721Enumerable) {
      super._beforeTokenTransfer(from, to, tokenId);
    }

  //only owner functions
  function reveal() public onlyOwner() {
      revealed = true;
  }
 
  function setCost(uint256 _newCost) public onlyOwner() {
    cost = _newCost;
  }

  function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner() {
    maxMintAmount = _newmaxMintAmount;
  }

  function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
    notRevealedURI = _notRevealedURI;
  }
 
  function _baseURI() internal view virtual override returns (string memory) {
    return baseURI;
  }
 
  /**
     * @dev Internal function to set the base URI for all token IDs. It is
     * automatically added as a prefix to the value returned in {tokenURI},
     * or to the token ID if {tokenURI} is empty.
  */
  function setBaseURI(string memory _newBaseURI) public onlyOwner {
    baseURI = _newBaseURI;
  }

 

  function setActivePublicMint(bool _state) public onlyOwner {
    publicMintIsActive = _state;
  }
 

  function setOnlyWhitelisted(bool _state) public onlyOwner {
    onlyWhitelisted = _state;
  }
 
 function setgovernanceWhitelisted(bool _state) public onlyOwner {
    onlyWhitelisted = _state;
  }
  /**
  * Read-only function to retrieve the number of NFTs that can be minted in one txn
  */
  function getCurrentMintLimit() public pure returns (uint64) {
    return 1000;
  }
 
  function whitelistUsers(address[] calldata _users) public onlyOwner{
    delete whitelistedAddresses;
    whitelistedAddresses = _users;
  }
 
  function governanceWhitelistUsers(address[] calldata _users) public onlyOwner{
    delete governanceWhitelistedAddresses;
    governanceWhitelistedAddresses = _users;
  }
 
  function withdraw() public payable onlyOwner {
    (bool success, ) = payable(payments).call{value: address(this).balance}("");
    require(success);
  }
}
/*
 Fractional Bricks to the mooon baby!
 */
function setBaseURI(string memory _newBaseURI) public onlyOwner {
  baseURI = _newBaseURI;
}

/**
 * @dev Internal function to set the base URI for all token IDs. It is
 * automatically added as a prefix to the value returned in {tokenURI},
 * or to the token ID if {tokenURI} is empty.
 */