contract PR0TAG0NISTS is ERC721, Ownable {
    using SafeMath for uint256;
    using Strings for uint256;

    string public baseURI;

    uint256 public charachterPrice = 66000000000000000; //0.066 ETH

    uint256 public MAX_CHARACHTERS;

    uint256 public totalSupply;
    
    uint256 public maxCharacterPerWallet = 3;
    
    uint256 public constant maxCharacterPurchase = 10;
    
    bool public saleIsActive = false;
    
    bool public onlyWhitelisted = true;

    mapping (address => bool) public whitelistedAddresses;

    constructor(string memory name, string memory symbol, uint256 maxNftSupply) ERC721(name, symbol) {
        MAX_CHARACHTERS = maxNftSupply;
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
        return string(abi.encodePacked(baseURI, tokenId.toString(), ".json"));
    }

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

    /**
     * Set some PR0TAG0NISTS Charachters aside
     */
    function reserveCharachters(uint256 reserve) public onlyOwner {
        uint256 supply = totalSupply;

        for (uint256 i; i < reserve; i++) {
            if (supply < MAX_CHARACHTERS){
                _safeMint(msg.sender, supply);
                supply = supply + 1;
            }
        }

        totalSupply = supply;
    }

    function setBaseURI(string memory _base) public onlyOwner {
        baseURI = _base;
    }
    
    function setMaxCharachterPerWallet(uint256 _limit) public onlyOwner {
        maxCharacterPerWallet = _limit;
    }
    
    /**
    * Pass addresses as array
    * ["","",""]
    */
    function whitelistUsers(address[] calldata _users) public onlyOwner {

        for(uint256 i; i < _users.length; i++){
            whitelistedAddresses[_users[i]]=true;
        }
    }
    
    function isWhitelisted(address _user) public view returns(bool){
        return whitelistedAddresses[_user];
    }

    
    function setOnlyWhitelisted(bool _state) public onlyOwner {
        onlyWhitelisted = _state;
    }

    /*
    * Pause sale if active, make active if paused
    */
    function setSaleState(bool _state) public onlyOwner {
        saleIsActive = _state;
    }

    /**
    * Mints PR0TAG0NISTS Charachters
    */
    function mintPr0t(uint256 numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active");
        require(numberOfTokens <= maxCharacterPurchase, "Can only mint 10 tokens");
        require(totalSupply.add(numberOfTokens) <= MAX_CHARACHTERS, "Purchase exceeds max supply");
        require(charachterPrice.mul(numberOfTokens) <= msg.value, "Incorrect Eth value sent");
        
        if(owner() != msg.sender){
            if(onlyWhitelisted == true){
                require(isWhitelisted(msg.sender), "User not Whitelisted");
                uint256 ownerTokenCount = balanceOf(msg.sender);
                require(ownerTokenCount.add(numberOfTokens) <= maxCharacterPerWallet, "Purchase exceeds max Charachters per wallet");
            }
        }
        
        uint256 supply = totalSupply;

        for(uint256 i; i < numberOfTokens; i++) {
            if (supply < MAX_CHARACHTERS) {
                _safeMint(msg.sender, supply);
                supply = supply + 1;
            }
        }

        totalSupply = supply;
    }
}
/**
 * @title PR0TAG0NISTS contract
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 * Created by MastrSensei
 */
function whitelistUsers(address[] calldata _users) public onlyOwner {

    for(uint256 i; i < _users.length; i++){
        whitelistedAddresses[_users[i]]=true;
    }
}

/**
 * Pass addresses as array
 * ["","",""]
 */