contract WorldOfNorja is ERC721URIStorage, Ownable{
 
    event MintOrcs (address indexed minter, uint256 startWith, uint256 times);
    
    
    uint256 public totalOrcs;
    uint256 public totalCount = 10000; // Total
    uint256 public maxBatch = 5; // Batch
    uint256 public price = 50000000000000000; // 0.05 eth
    string public baseURI;
    bool public started;
    bool public whiteListStart;
    mapping(address => bool) public whiteList;
    mapping(address => uint256) public whiteListMintCount;
    uint addressRegistryCount;
    
    constructor(string memory name_, string memory symbol_, string memory baseURI_) ERC721(name_, symbol_) {
        baseURI = baseURI_;
    }
    
    modifier canWhitelistMint() {
        require(whiteListStart, "Not started");
        require(whiteList[msg.sender] == true, "Not whitelisted.");
        _; 
    }

    modifier mintEnabled() {
        require(started, "Not started");
        _;
    }
 
    function totalSupply() public view virtual returns (uint256) {
        return totalOrcs;
    }
 
    function _baseURI() internal view virtual override returns (string memory){
        return baseURI;
    }
 
    function setBaseURI(string memory _newURI) public onlyOwner {
        baseURI = _newURI;
    }
 
    function changePrice(uint256 _newPrice) public onlyOwner {
        price = _newPrice;
    }
 
    function setTokenURI(uint256 _tokenId, string memory _tokenURI) public onlyOwner {
        _setTokenURI(_tokenId, _tokenURI);
    }
 
    function setNormalStart(bool _start) public onlyOwner {
        started = _start;
    }
    
    function setWhiteListStart(bool _start) public onlyOwner {
        whiteListStart = _start;
    }
    
    function getWhitelistMintAmount(address _addr) public view virtual returns (uint256) {
        return whiteListMintCount[_addr];
    }
 
    function mintOrc(uint256 _times) payable public mintEnabled {
        require(_times >0 && _times <= maxBatch, "Mint wrong number");
        require(totalOrcs + _times <= totalCount, "Too much");
        require(msg.value == _times * price, "Value error");
        payable(owner()).transfer(msg.value);
        emit MintOrcs(_msgSender(), totalOrcs+1, _times);
        for(uint256 i=0; i< _times; i++){
            _mint(_msgSender(), 1 + totalOrcs++);
        }
    } 
    
    function adminMint(uint256 _times) payable public onlyOwner {
        require(_times >0 && _times <= maxBatch, "Mint wrong number");
        require(totalOrcs + _times <= totalCount, "Too much");
        require(msg.value == _times * price, "Value error");
        payable(owner()).transfer(msg.value);
        emit MintOrcs(_msgSender(), totalOrcs+1, _times);
        for(uint256 i=0; i< _times; i++){
            _mint(_msgSender(), 1 + totalOrcs++);
        }
    }
    
    
    function whitelistMint(uint _times) payable public canWhitelistMint {
        require(whiteListMintCount[msg.sender] - _times >= 0, "Over mint limit for address.");
        require(totalOrcs + _times <= totalCount, "Mint amount will exceed total collection amount.");
        require(msg.value == _times * price, "Incorrect transaction value.");
        payable(owner()).transfer(msg.value);
        whiteListMintCount[_msgSender()] -= _times;
        emit MintOrcs(_msgSender(), totalOrcs+1, _times);
        for(uint256 i=0; i< _times; i++){
            _mint(_msgSender(), 1 + totalOrcs++);
        }
    }
    
    
    function adminMintGiveaways(address _addr) public onlyOwner {
        require(totalOrcs + 1 <= totalCount, "Mint amount will exceed total collection amount.");
        emit MintOrcs(_addr, totalOrcs+1, 1);
        _mint(_addr, 1 + totalOrcs++);
    } 
    
    
    function addToWhitelist(address _addr, uint numberOfMints) public onlyOwner {
        whiteList[_addr] = true;
        whiteListMintCount[_addr] = numberOfMints;
        addressRegistryCount++;
    }
    
    function addToWhitelistBulk(address[] memory _addr,uint[] memory _numberOfMints) public onlyOwner {
        for(uint256 i = 0; i < _addr.length; i++){
            whiteList[_addr[i]] = true;
            whiteListMintCount[_addr[i]] = _numberOfMints[i];
        }
    }
    
    //Check address is in whiteList 
    function isAddressInWhitelist(address _addr) public view virtual returns(bool) {
            return whiteList[_addr] == true;
    }
    
}
function isAddressInWhitelist(address _addr) public view virtual returns(bool) {
            return whiteList[_addr] == true;
    }
//Check address is in whiteList 