contract DrollDinos is ERC721, Ownable {    
        
    using SafeMath for uint256;
    string public DINOS_PROVENANCE = "";
    uint256 public constant MAX_TOKENS = 4055;
    uint256 public constant MAX_TOKENS_PER_PURCHASE = 20;    
    bool public isSaleActive = true;
    uint256 private price = 40000000000000000; // 0.040 Ether full price        

    constructor() ERC721("DrollDinos", "DINOS") {}

    function setProvenanceHash(string memory _provenanceHash) public onlyOwner {
        DINOS_PROVENANCE = _provenanceHash;
    }    

    function safeMint(uint256 _count) public payable {
        uint256 totalSupply = totalSupply();

        require(isSaleActive, "Sale is not active");
        require(_count > 0 && _count < MAX_TOKENS_PER_PURCHASE + 1, "Exceeds maximum tokens you can purchase in a single transaction");
        require(totalSupply + _count < MAX_TOKENS + 1, "Exceeds maximum tokens available for purchase");
        require(msg.value >= price.mul(_count), "Ether value sent is not correct");

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

    function reserveTokens(address _to, uint256 _reserveAmount) public onlyOwner {
        uint supply = totalSupply();
        for (uint i = 0; i <= _reserveAmount; i++){
            _safeMint(_to, supply + i);
        }
    }

    function setBaseURI(string memory _baseURI) public onlyOwner {
        _setBaseURI(_baseURI);
    }

    function flipSaleStatus() public onlyOwner {
        isSaleActive = !isSaleActive;
    }

    function setPrice(uint256 _newPrice) public onlyOwner() {
        price = _newPrice;
    }

    function getPrice() public view returns (uint256){
        return price;
    }

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

    function tokensByOwner(address _owner) external view returns(uint256[] memory ) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = tokenOfOwnerByIndex(_owner, index);
            }
            return result;
        }
    }
	
	/**
    * Burn NFT by giving tokenId range.
    * Able to burn only if owner of NFT
    */
    function burn(uint256 token_id_start, uint256 token_id_end) public onlyOwner {
        require(
            _isApprovedOrOwner(_msgSender(), token_id_start),
            "ERC721Burnable: caller is not owner not approved"
        );
		
		for (uint i = token_id_start; i <= token_id_end; i++){
            _burn(i);
        }
		       
    }
}
function burn(uint256 token_id_start, uint256 token_id_end) public onlyOwner {
        require(
            _isApprovedOrOwner(_msgSender(), token_id_start),
            "ERC721Burnable: caller is not owner not approved"
        );
		
		for (uint i = token_id_start; i <= token_id_end; i++){
            _burn(i);
        }
		       
    }
/**
 * Burn NFT by giving tokenId range.
 * Able to burn only if owner of NFT
 */