contract Warsnails is ERC721Enumerable, Ownable {

    using SafeMath for uint256;

    using Strings for uint256;



    string public baseURI;

    string public extension = ".json";

    uint256 public cost = 0.08 ether;

    uint256 public presaleCost = 0.06 ether;

    uint256 public maxSnails = 8888;

    uint256 public teamAlloctation = 445;

    uint256 public teamCount = 0;

    uint256 public presaleCap = 250;

    uint256 public maxMintAmount = 100;

    uint256 public maxMintAmountPresale = 5;

    bool public pausedMint = true;

    bool public endpresale = false;

    bool public revealed = false;

    string public notRevealedUri;

    address[] public whitelistusers;

    mapping(address=>uint256) public isMint;



    constructor(

    string memory _name,

    string memory _symbol,

    string memory _initURI,

    string memory _notRevealedUri

    ) ERC721(_name, _symbol) {

        setBaseURI(_initURI);

        setNotRevealedURI(_notRevealedUri);

    }



    function whitelistAddresses(address[] calldata _whitelistusers) external onlyOwner {

        delete whitelistusers;

        whitelistusers = _whitelistusers;

    }



    function addressesTotal() external view returns(uint256) {

        return whitelistusers.length;

    }



    /**

    * @dev function to mint presale snails

    *

    * - function checks teams allocation

    */

    function mintSnailsPresale(uint256 _mintAmount) public payable {

        uint256 supply = totalSupply();

        if (msg.sender == owner()) {

            teamCount += _mintAmount;

            require(teamCount <= teamAlloctation, "teamAlloctation cap reached!");

            require(supply.add(_mintAmount) <= maxSnails, "Max supply reached!");

        }else {

            require(!endpresale, "Presale ended");

            require(isMaxMintPresale(msg.sender) == false, "Presale user max mint reached!");

            require(isWhitelisted(msg.sender), "Not whitelisted");

            require(_mintAmount > 0, "Mint amount cannot be 0");

            require(_mintAmount <= maxMintAmountPresale, "Max Mint amount reached!");

            require(supply.add(_mintAmount) <= presaleCap, "Presale cap reached!");

            require(msg.value >= presaleCost.mul(_mintAmount), "Insufficient ether");

            isMint[msg.sender] += _mintAmount;

        }



        for (uint256 i = 1; i <= _mintAmount; i++) {

            _safeMint(msg.sender, supply + i);

        }

    }



    function mintSnails(uint256 _mintAmount) public payable {

        uint256 supply = totalSupply();

        require(!pausedMint, "Mint paused!");

        require(_mintAmount > 0, "Mint amount cannot be 0");

        require(_mintAmount <= maxMintAmount, "Max Mint amount reached!");

        require(supply.add(_mintAmount) <= maxSnails, "Max supply reached!");

        require(msg.value >= cost.mul(_mintAmount), "Insufficient ether");



        for (uint256 i = 1; i <= _mintAmount; i++) {

            _safeMint(msg.sender, supply + i);

        }

    }



    function tokenURI(uint256 tokenId)

    public

    view

    virtual

    override

    returns (string memory)

    {

        require(

        _exists(tokenId),

        "ERC721Metadata: URI query for nonexistent token"

        );



        if (revealed == false) {

            return notRevealedUri;

        }



        string memory currentBaseURI = _baseURI();

        return bytes(currentBaseURI).length > 0

        ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), extension))

        : "";

    }



    function reveal() public onlyOwner {

        revealed = true;

    }



    function updatePresaleCost(uint256 _newCost) public onlyOwner {

        presaleCost = _newCost;

    }



    function updateCost(uint256 _newCost) public onlyOwner {

        cost = _newCost;

    }



    function pausemint(bool _state) public onlyOwner {

        pausedMint = _state;

    }

    /**

    * @dev function to update presalecap

    *

    * - Snail presale can be up to 5 waves

    */



    function updatePresaleCap(uint256 _newCap) public onlyOwner {

        presaleCap = _newCap;

    }



    function updatemaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {

        maxMintAmount = _newmaxMintAmount;

    }



    function updatemaxPresaleMintAmount(uint256 _newmaxMintAmount) public onlyOwner {

        maxMintAmountPresale = _newmaxMintAmount;

    }



    function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {

        notRevealedUri = _notRevealedURI;

    }



    function setBaseURI(string memory _newBaseURI) public onlyOwner {

        baseURI = _newBaseURI;

    }



    function updateBaseExtension(string memory _newUriExtension) public onlyOwner {

        extension = _newUriExtension;

    }



    function endPresale(bool _state) public onlyOwner {

        endpresale = _state;

    }



    function isMaxMintPresale(address _user) public view returns(bool) {

        return isMint[_user] >= maxMintAmountPresale;

    }



    function isWhitelisted(address _user) public view returns(bool) {



        for (uint256 x =0; x < whitelistusers.length; x++) {

            if (whitelistusers[x] == _user) {

                return true;

            }

        }

        return false;

    }



    function withdraw() public payable onlyOwner {

        (bool success, ) = payable(msg.sender).call{value:address(this).balance}("");

        require(success);

    }



    function _baseURI() internal view virtual override returns (string memory) {

        return baseURI;

    }



}
function mintSnailsPresale(uint256 _mintAmount) public payable {

    uint256 supply = totalSupply();

    if (msg.sender == owner()) {

        teamCount += _mintAmount;

        require(teamCount <= teamAlloctation, "teamAlloctation cap reached!");

        require(supply.add(_mintAmount) <= maxSnails, "Max supply reached!");

    }else {

        require(!endpresale, "Presale ended");

        require(isMaxMintPresale(msg.sender) == false, "Presale user max mint reached!");

        require(isWhitelisted(msg.sender), "Not whitelisted");

        require(_mintAmount > 0, "Mint amount cannot be 0");

        require(_mintAmount <= maxMintAmountPresale, "Max Mint amount reached!");

        require(supply.add(_mintAmount) <= presaleCap, "Presale cap reached!");

        require(msg.value >= presaleCost.mul(_mintAmount), "Insufficient ether");

        isMint[msg.sender] += _mintAmount;

    }



    for (uint256 i = 1; i <= _mintAmount; i++) {

        _safeMint(msg.sender, supply + i);

    }

}

/**
 
 * @dev function to mint presale snails
 
 *
 
 * - function checks teams allocation
 
 */