contract GenVibes is ERC721, ERC721Enumerable, Pausable, Ownable {
    event Mint(address indexed _from, uint _tokenID, uint _hash);

    using Counters for Counters.Counter;

    Counters.Counter public mintCounter;
    Counters.Counter public fopunkMintCounter;
    address fp_addr = 0xDb1cbD0B58fb4f7C9f6857025DA29f42371fB659;
    FourierPunks fp;
    uint mintPrice = 0.05 ether;
    uint public constant MAX_FOPUNK_MINTS = 420;
    uint public constant MAX_PAID_MINTS = 1000;
    mapping(uint => bool) fopunk_mint;
    mapping(uint => uint) public tokenHash;
    string private _baseURIextended = 'https://api.genvibes.io/token?id=';
    mapping(uint=>string) private generationCode;
    string public generationCodeHash = '3c2ededf3eef5651e02617d790f1084c0469834a8c0644f922bea0043150443e';
    string public p5jsversion = '1.4.0';

    function updateCodeHash(string memory _newhash) public onlyOwner{
        generationCodeHash = _newhash;
    }

    function uploadCode(uint part, string memory code) public onlyOwner{
        generationCode[part] = code;
    }

    function getGenerationCode(uint part) public view returns (string memory){
        return generationCode[part];
    }

    function checkFoPunk(uint fopunkID) public view returns (bool freeMint){
        require(fopunkID < 420 && fopunkID >=0,"Not valid FourierPunk ID");
        return !fopunk_mint[fopunkID];
    }

    function fpMints() public view returns (uint m){
        return fopunkMintCounter.current();
    }

    function regMints() public view returns (uint m){
        return mintCounter.current();
    }

    function mintWithFoPunk(uint fopunkID) public whenNotPaused{
        require(fp.ownerOf(fopunkID) == msg.sender,"Not owner of that FourierPunk");
        require(checkFoPunk(fopunkID),"Already used");
        require(fopunkMintCounter.current() < MAX_FOPUNK_MINTS,"No free mints left");
        fopunk_mint[fopunkID] = true;
        uint nextTokenId = mintCounter.current()+fopunkMintCounter.current();
        fopunkMintCounter.increment();
        uint hash = generateHash(nextTokenId);
        tokenHash[nextTokenId] = hash;
        _safeMint(msg.sender, nextTokenId);
        emit Mint(msg.sender, nextTokenId, hash);
    }

    function withdraw() public onlyOwner{
        payable(msg.sender).transfer(address(this).balance);
    }

    function mint() public payable whenNotPaused{
        require(msg.value >= mintPrice,"Not enough");
        require(mintCounter.current() < MAX_PAID_MINTS,"No paid mints left");
        uint nextTokenId = mintCounter.current()+fopunkMintCounter.current();
        mintCounter.increment();
        tokenHash[nextTokenId] = generateHash(nextTokenId);
        uint hash = generateHash(nextTokenId);
        tokenHash[nextTokenId] = hash;
        _safeMint(msg.sender, nextTokenId);
        emit Mint(msg.sender, nextTokenId, hash);
    }

    function generateHash(uint inp) internal view returns (uint randomNumber){
        return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, msg.sender, inp))) % 1e20;
    }

    constructor() ERC721("GenVibes", "GENV") {
        fp = FourierPunks(fp_addr);
        _pause();
    }

    function setBaseURI(string memory baseURI_) external onlyOwner() {
        _baseURIextended = baseURI_;
    }

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

    function pause() public onlyOwner {
        _pause();
    }

    function unpause() public onlyOwner {
        _unpause();
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    // The following functions are overrides required by Solidity.

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
function supportsInterface(bytes4 interfaceId)
    public
    view
    override(ERC721, ERC721Enumerable)
    returns (bool)
{
    return super.supportsInterface(interfaceId);
}

// The following functions are overrides required by Solidity.