contract YCSM is ERC721Enumerable, Ownable {
    
    using SafeMath for uint256;
    
    bool public mintingActive;
    uint256 public mintingPrice = 0.05 ether;
    uint256 public mintingPerTxLimit = 1;
    uint256 public mintingStartTimestamp;
    mapping(address => bool) private purchased;
    address public poe;
    
    uint256 public maxSupply = 100;
    
    string public baseURI;
    
    
    constructor(uint256 mintingStart, string memory _uri, address poeToken) ERC721("YCSM", "YCSM") {
        mintingActive = false;
        mintingStartTimestamp = mintingStart;
        baseURI = _uri;
        poe = poeToken;
    }
    
    function hasPurchased(address buyer) public view returns (bool){
        return purchased[buyer];
    }


    function mint(uint256 _amount) public payable {
        require(mintingActive == true, "Minting is currently inactive");
        require(mintingStartTimestamp <= block.timestamp, "Sale not started yet");
        require(_amount <= mintingPerTxLimit, "Amount exceeded tx limit");
        require(totalSupply().add(_amount) <= maxSupply, "This tx woud exceed max supply");
        require(msg.value >= mintingPrice.mul(_amount), "Not enough ether sent");
        require(IERC20(poe).balanceOf(msg.sender)  == 1, "Must Authenticate to mint!");
        require(!hasPurchased(msg.sender), "Cannot buy: You've already purchased!");

        uint256 tsupply = totalSupply();
        for(uint256 i = 0; i < _amount; i++) {
            _safeMint(msg.sender, tsupply + i);
        }
        purchased[msg.sender] = true;
        
    }
    
    function adminMint(address receiver, uint256 _amount) public onlyOwner {
        require(totalSupply().add(_amount) <= maxSupply, "This tx would exceed max supply");
        
        uint256 tsupply = totalSupply();
        for(uint256 i = 0; i < _amount; i++) {
            _safeMint(receiver, tsupply + i);
        }
        
    }
    

    /**
    * Set Base URI
    */
    function setBaseURI(string memory newBaseURI) public onlyOwner {
        baseURI = newBaseURI;
    }
    
    /**
     * Get the metadata for a given tokenId
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        return bytes(baseURI).length > 0
            ? string(abi.encodePacked(baseURI, Strings.toString(tokenId + 1), ".json"))
            : "";
    }
    
    function setMintingPrice(uint256 newMintingPrice) public onlyOwner {
        mintingPrice = newMintingPrice;
    }
    
    function setMintingActive(bool newMintingState) public onlyOwner {
        mintingActive = newMintingState;
    }
    
    function setMintingStart(uint256 mintingStart) public onlyOwner {
        mintingStartTimestamp = mintingStart;
    }
    
    /**
     * Withdraw all funds from contract (Owner only)
     */
    function withdrawFunds() public onlyOwner {
        uint balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }
    
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
    require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

    return bytes(baseURI).length > 0
        ? string(abi.encodePacked(baseURI, Strings.toString(tokenId + 1), ".json"))
        : "";
}

/**
 * Get the metadata for a given tokenId
 */