contract LarvaChicksClub is ERC721Enumerable, Ownable {
    using SafeMath for uint256;
    using Address for address;

    string public baseURI;
    string public baseExtension = ".json";
    uint16 public counter = 0;
    uint8 public giveaway = 10;
    uint8 public constant maxPublicPurchase = 10;
    uint16 public maxSupply = 5000;
    bool public publicSale = true; 

    mapping(address => uint256) addressBlockBought;

    constructor(string memory tokenBaseUri) ERC721("Larva Chicks Club", "CHICK")  {
        // starting tokens from 1
        counter = counter + 1;

        setBaseURI(tokenBaseUri);
        mintForGiveaway();
    }


    /**
     * mint Larva Chicks
     */
    function mintChicks(uint8 numberOfTokens) public payable {
        uint256 supply = totalSupply();
        require(publicSale, "Public mint is not live right now.");
        require(addressBlockBought[msg.sender] < block.timestamp, "Not allowed to Mint on the same Block");
        require(!Address.isContract(msg.sender),"Contracts are not allowed to mint");
        require(numberOfTokens <= maxPublicPurchase, "You can mint a maximum of 10 Chicks");
        require(supply + numberOfTokens <= maxSupply, "Exceeds maximum Chicks supply" );
        

        addressBlockBought[msg.sender] = block.timestamp;

        for(uint8 i; i < numberOfTokens; i++){
            _safeMint(msg.sender, counter);
            counter = counter + 1;
        }
    }

    /**
     * reserve Chicks for giveaways
     */
    function mintForGiveaway() public onlyOwner {
        require(giveaway > 0, "Giveaway has been minted!");

        for (uint8 i = 0; i < giveaway; i++) {
            _safeMint(msg.sender, counter);
            counter = counter + 1;
        }

        giveaway -= giveaway;
    }

    /**
     * Returns Chicks of the Caller
     */
    function chicksOfOwner(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 toggleSale() public onlyOwner{
        publicSale = !publicSale;
    }

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

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

    function tokenURI(uint256 tokenId)
    public
    view
    virtual
    override
    returns (string memory)
    {
        require( _exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        
        string memory tokenIdString = uint2str(tokenId);
        string memory currentBaseURI = _baseURI();
        
        return bytes(currentBaseURI).length > 0 ? string(abi.encodePacked(currentBaseURI,  tokenIdString, baseExtension))
        : "";
    }

    /**
     * Withdraw Ether
     */
    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0, "No balance to withdraw");

        (bool success, ) = msg.sender.call{value: balance}("");
        require(success, "Failed to withdraw payment");
    }

    function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
        if (_i == 0) {
            return "0";
        }
        uint j = _i;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len;
        while (_i != 0) {
            k = k-1;
            uint8 temp = (48 + uint8(_i - _i / 10 * 10));
            bytes1 b1 = bytes1(temp);
            bstr[k] = b1;
            _i /= 10;
        }
        return string(bstr);
    }
    
}
function chicksOfOwner(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;
    }
/**
 * Returns Chicks of the Caller
 */