contract PowerSurge is
    ContextMixin,
    ERC721Enumerable,
    EIP712,
    Ownable
{
    using SafeMath for uint256;

    string public baseTokenURI;
    uint256 private _currentTokenId = 0;
    uint256 MAX_SUPPLY = 9999;
    uint256 private mintPrice = 4e16;

    bool public isPublicMintEnabled = false;
    bool public isPresaleMintEnabled = false;

    mapping(address => bool) public whitelist;

    constructor() ERC721("PowerSurge", "PWR") EIP712("PowerSurge", "1.0.0") {
        baseTokenURI = "https://api.powersurgenft.io/api/v1/";
    }


    /**
     * @dev Airdrop nft to several addresses.
     * @param _recipients addresss of the future owner of the token
     */
    function mintTo(address[] memory _recipients) external onlyOwner {
        for (uint256 i = 0; i < _recipients.length; i++) {
            uint256 newTokenId = _getNextTokenId();
            _mint(_recipients[i], newTokenId);
            _incrementTokenId();
        }
    }

    /**
     * @dev Mint to msg.sender.
     * @param _num number of tokens to be minted
     */
    function mint(uint256 _num) external payable {
        require(
            isPublicMintEnabled || (isPresaleMintEnabled && whitelist[msg.sender]),
            "Minting is not currently enabled."
        );
        require(
            totalSupply() + _num <= MAX_SUPPLY,
            "Sorry, all nfts are sold out."
        );
        require(
            _num <= 30,
            "Up to 30 PowerSurge can be minted in a tx."
        );
        require(
            msg.value == _num * mintPrice,
            "You need to pay the exact price."
        );
        _mintList(_num);
    }


    /**
     * @dev add addresses to the whitelist
     * @param addrs addresses
     */
    function addAddressesToWhitelist(address[] memory addrs) public onlyOwner {
        for (uint256 i = 0; i < addrs.length; i++) {
            whitelist[addrs[i]] = true;
        }
    }
    
        /**
     * @dev remove addresses from the whitelist
     * @param addrs addresses
     */
    function removeAddressesFromWhitelist(address[] memory addrs)
        public
        onlyOwner
    {
        for (uint256 i = 0; i < addrs.length; i++) {
            whitelist[addrs[i]] = false;
        }
    }

    function _mintList(uint256 _num) private {
        for (uint8 i = 0; i < _num; i++) {
            uint256 newTokenId = _getNextTokenId();
            _mint(msg.sender, newTokenId);
            _incrementTokenId();
        }
    }


    /**
     * @dev calculates the next token ID based on value of _currentTokenId
     * @return uint256 for the next token ID
     */
    function _getNextTokenId() private view returns (uint256) {
        return _currentTokenId.add(1);
    }

    /**
     * @dev increments the value of _currentTokenId
     */
    function _incrementTokenId() private {
        require(_currentTokenId < MAX_SUPPLY);
        _currentTokenId++;
    }

    /**
     * @dev change the baseTokenURI only by Admin
     */
    function setBaseUri(string memory _uri) external onlyOwner {
        baseTokenURI = _uri;
    }

    /**
     * @dev set minting price
     */
    function setMintPrice(uint256 price) external onlyOwner {
        mintPrice = price;
    }
    
    /**
     * @dev enable public minting
     */
    function enablePublicMinting() external onlyOwner {
        isPublicMintEnabled = true;
    }
    
    /**
     * @dev disable public minting
     */
    function disablePublicMinting() external onlyOwner {
        isPublicMintEnabled = false;
    }
    
    
        /**
     * @dev enable presale minting
     */
    function enablePresaleMinting() external onlyOwner {
        isPresaleMintEnabled = true;
    }
    
    /**
     * @dev disable presale minting
     */
    function disablePresaleMinting() external onlyOwner {
        isPresaleMintEnabled = false;
    }
    
    /**
     * @dev withdraw balance to owner
     */
    function withdraw() external onlyOwner {
        payable(owner()).transfer(address(this).balance);
    }

    function tokenURI(uint256 _tokenId)
        public
        view
        override
        returns (string memory)
    {
        return
            string(abi.encodePacked(baseTokenURI, Strings.toString(_tokenId)));
    }
    
    function premint(uint8 _num) external onlyOwner {
        _mintList(_num);
    }

    /**
     * This is used instead of msg.sender as transactions won't be sent by the original token owner, but by OpenSea.
     */
    function _msgSender() internal view override returns (address sender) {
        return ContextMixin.msgSender();
    }

    function totalSupply() public view override returns (uint) {
        return _currentTokenId;
    }

}
/**
 * @title PowerSurgeNFT
 */
function disablePublicMinting() external onlyOwner {
    isPublicMintEnabled = false;
}

/**
 * @dev disable public minting
 */