contract AWitchersVoyage is ERC721, Ownable {
    
    using SafeMath for uint256;

    
    string public baseExtension = ".json";

     uint256 public cost = 0.055 ether;

    uint256 public MAX_WITCHERS = 6666;
    
    uint256 public MAX_WITCHERS_MINT_AMOUNT = 20;
    uint256 public WITCHERS_PER_ADDRESS_LIMIT = 20;
    uint256 public MAX_WITCHERS_RESERVE = 66;

    bool public paused = false;
    bool public revealed = false;
     bool public ONLY_WHITELISTED_WITCHERS = true;
  address[] public whitelistedAddresses;
      mapping(address => uint256) public addressMintedBalance;

    address public devAddress = 0x84F8e36eE0ced324D9Bf30069eC1C279F10c814f;
    address public subDevAddress1 = 0x84F8e36eE0ced324D9Bf30069eC1C279F10c814f;
    address public subDevAddress2 = 0x0000000000000000000000000000000000000000;
 
 
    
    address payable private devguy = payable(devAddress);

    constructor ()
    ERC721("A Witchers Voyage", "AWV") { }
    

     // public
  function mint(uint256 _MAX_WITCHERS_MINT_AMOUNT) public payable {
    require(!paused, "the contract is paused");
    uint256 supply = totalSupply();
    require(MAX_WITCHERS_MINT_AMOUNT > 0, "need to mint at least 1 NFT");
    require(_MAX_WITCHERS_MINT_AMOUNT <= MAX_WITCHERS_MINT_AMOUNT, "max mint amount per session exceeded");
    require(supply + _MAX_WITCHERS_MINT_AMOUNT <= MAX_WITCHERS, "max NFT limit exceeded");
  
  if (msg.sender != owner()) {
        if(ONLY_WHITELISTED_WITCHERS == true) {
            require(isWhitelisted (msg.sender), "user is not whitelisted");
            uint256 ownerMintedCount = addressMintedBalance[msg.sender];
            require(ownerMintedCount + _MAX_WITCHERS_MINT_AMOUNT <= WITCHERS_PER_ADDRESS_LIMIT, "max NFT per address exceeded");
        }
        require(msg.value >= cost * _MAX_WITCHERS_MINT_AMOUNT, "insufficient funds");
    }

    for (uint256 i = 1; i <= _MAX_WITCHERS_MINT_AMOUNT; i++) {
      addressMintedBalance[msg.sender]++;
      _safeMint(msg.sender, supply + i);
    }
  }
function isWhitelisted(address _user) public view returns (bool) {
    for (uint i = 0; i < whitelistedAddresses.length; i++) {
      if (whitelistedAddresses[i] == _user) {
          return true;
      }
    }
    return false;
  }    
   function setOnlyWhitelisted(bool _state) public onlyOwner {
    ONLY_WHITELISTED_WITCHERS = _state;
  }
  
  function whitelistUsers(address[] calldata _users) public onlyOwner {
    delete whitelistedAddresses;
    whitelistedAddresses = _users;
  }
   
    function withdraw() external 
    {
        require(msg.sender == devAddress || msg.sender == owner(), "Invalid sender");

        uint devPart = address(this).balance / 100 * 0;
        devguy.transfer(devPart);
        payable(owner()).transfer(address(this).balance);
    }
    

    function setSubDevAddresses(address _subDevAddress1, address _subDevAddress2) public {
        require(msg.sender == devAddress || msg.sender == owner(), "Invalid sender");
        subDevAddress1 = _subDevAddress1;
        subDevAddress2 = _subDevAddress2;
    }
     function setSubDevAddress1(address _subDevAddress1) public {
        require(msg.sender == devAddress || msg.sender == owner(), "Invalid sender");
        subDevAddress1 = _subDevAddress1;
    }
    
        function setSubDevAddress2( address _subDevAddress2) public {
        require(msg.sender == devAddress || msg.sender == owner(), "Invalid sender");
        subDevAddress2 = _subDevAddress2;
    }

     function pause(bool _state) public onlyOwner {
    paused = _state;
    }


  function setBaseURI(string memory baseURI) public  {
          require(msg.sender == devAddress || msg.sender == owner() || msg.sender == subDevAddress1 || msg.sender == subDevAddress2, "Invalid sender");
        _setBaseURI(baseURI);
    }   

    function setCost(uint256 _newCost) public onlyOwner {
    cost = _newCost;
    }

    function mintReservedwitchers(uint256 numberOfTokens) public  {
        require(msg.sender == devAddress || msg.sender == owner(), "Invalid sender");
        require(numberOfTokens > 0 && numberOfTokens <= MAX_WITCHERS_RESERVE, "Not enough reserve left for team");

        for(uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply();
            _safeMint(msg.sender, mintIndex);
        }

        MAX_WITCHERS_RESERVE = MAX_WITCHERS_RESERVE.sub(numberOfTokens);
    }


    function burn(uint256 tokenId) external onlyOwner{
    require(ownerOf(tokenId) == msg.sender, "NOT_OWNER_OF_TOKEN");
    
    _burn(tokenId);
    }
    
    function multiBurn(uint256 startTokenId,uint256 endTokenId) external  onlyOwner{
    for(uint i = startTokenId; i <= endTokenId; i++) {
        require(ownerOf(i) == msg.sender, "NOT_OWNER_OF_TOKEN");
            _burn(i); 
        }
   
    }    
    
    function giveAway(address to) public payable  {
        require(msg.sender == devAddress || msg.sender == owner() || msg.sender == subDevAddress1 || msg.sender == subDevAddress2, "Invalid sender");
        require(totalSupply().add(1) <= MAX_WITCHERS, "Purchase would exceed max supply of WITCHERS");
            uint256 mintIndex = totalSupply();
            if (totalSupply() < MAX_WITCHERS) {
                _safeMint(to, mintIndex);
            }
        
    }

   
        }
function mint(uint256 _MAX_WITCHERS_MINT_AMOUNT) public payable {
  require(!paused, "the contract is paused");
  uint256 supply = totalSupply();
  require(MAX_WITCHERS_MINT_AMOUNT > 0, "need to mint at least 1 NFT");
  require(_MAX_WITCHERS_MINT_AMOUNT <= MAX_WITCHERS_MINT_AMOUNT, "max mint amount per session exceeded");
  require(supply + _MAX_WITCHERS_MINT_AMOUNT <= MAX_WITCHERS, "max NFT limit exceeded");
  
if (msg.sender != owner()) {
      if(ONLY_WHITELISTED_WITCHERS == true) {
          require(isWhitelisted (msg.sender), "user is not whitelisted");
          uint256 ownerMintedCount = addressMintedBalance[msg.sender];
          require(ownerMintedCount + _MAX_WITCHERS_MINT_AMOUNT <= WITCHERS_PER_ADDRESS_LIMIT, "max NFT per address exceeded");
      }
      require(msg.value >= cost * _MAX_WITCHERS_MINT_AMOUNT, "insufficient funds");
  }

  for (uint256 i = 1; i <= _MAX_WITCHERS_MINT_AMOUNT; i++) {
    addressMintedBalance[msg.sender]++;
    _safeMint(msg.sender, supply + i);
  }
}

// public