contract TheSecondBurn is ERC721, ReentrancyGuard, Ownable {
    using Strings for uint256;
    using SafeMath for uint256;
    using Counters for Counters.Counter;
    using EnumerableSet for EnumerableSet.UintSet;

    Counters.Counter private _tokenIdTracker;
    Counters.Counter private _burnedTracker;
    
    // Enumerable set of burnable ids that have minted
    EnumerableSet.UintSet private _burnableMinters;
    
    // burnables Contract
    // mainnet
	address public burnablesAddress = 0xF9b8E02A97780381ea1FbDd41D7706ACD163dd9A;
	// rinkeby
	// address public burnablesAddress = 0x54490f800df2E2A654CfdE8C9eB966C6A55771B1;
    BurnablesInterface burnablesContract = BurnablesInterface(burnablesAddress);
    
    string public baseTokenURI;

    uint256 public constant MAX_ELEMENTS = 4999;    
    uint256 public constant MAX_PUBLIC_ELEMENTS = 4000;    
    uint256 public constant MINT_PRICE = 8 * 10**16;
    

    event CreateBurnable(uint256 indexed id);

    constructor() public ERC721("TheSecondBurn", "BURN2") {}

    modifier saleIsOpen {
        require(_totalSupply() <= MAX_PUBLIC_ELEMENTS, "Sale end");
        _;
    }

    function totalSupply() public view returns (uint256) {
        return  _totalSupply() - _totalBurned();
    }

    function _totalSupply() internal view returns (uint256) {
        return _tokenIdTracker.current();
    }
    
    function _totalBurned() internal view returns (uint256) {
        return _burnedTracker.current();
    }
    
    function totalBurned() public view returns (uint256) {
        return _totalBurned();
    }

    function totalMint() public view returns (uint256) {
        return _totalSupply();
    }
    
    function getBurnablesOwner(uint256 burnablesId) public view returns (address) {
        return burnablesContract.ownerOf(burnablesId);
    }
    
    // Minting reserved for burnables owners
    function mintWithBurnable(uint256 burnablesId) public nonReentrant {
        require(burnablesContract.ownerOf(burnablesId) == msg.sender, "Not the owner of this burnable");
        require(!_burnableMinters.contains(burnablesId), "This burnable already has minted a second burn.");
        
         _burnableMinters.add(burnablesId);

        _mintAnElement(msg.sender);
    }
    
    function mintPublic(uint256 _count) public payable saleIsOpen {
        uint256 total = _totalSupply();
        require(total + _count <= MAX_PUBLIC_ELEMENTS, "Max limit");
        require(total <= MAX_PUBLIC_ELEMENTS, "Sale end");
        require(msg.value >= price(_count), "Value below price");

        for (uint256 i = 0; i < _count; i++) {
            _mintAnElement(msg.sender);
        }
    }
    
    function price(uint256 _count) public pure returns (uint256) {
        return MINT_PRICE.mul(_count);
    }

    function _mintAnElement(address _to) private {
        uint256 id = _totalSupply();
        _tokenIdTracker.increment();
        _safeMint(_to, id);
        emit CreateBurnable(id);
    }
    
    // Send ETH to make the burn value higher
    function sendEther() public payable {
        uint256 supply = totalSupply();
        require(supply > 0);
    }

    function setBaseURI(string memory baseURI) public onlyOwner {
        baseTokenURI = baseURI;
    }

    /**
     * @dev Returns an URI for a given token ID
     */
    function tokenURI(uint256 _tokenId) public view override returns (string memory) {
        return string(abi.encodePacked(baseTokenURI, _tokenId.toString()));
    }
    
    /**
     * @dev Burns and pays the mint price to the token owner.
     * @param _tokenId The token to burn.
     */
    function burn(uint256 _tokenId) public {
        require(ownerOf(_tokenId) == msg.sender);
        uint256 balance = address(this).balance;
        require(balance > 0);

        //Burn token
        _transfer(
            msg.sender,
            0x000000000000000000000000000000000000dEaD,
            _tokenId
        );

        // pay token owner 
        uint256 supply = totalSupply();
        _widthdraw(msg.sender, balance.div(supply));
        
        // increment burn
        _burnedTracker.increment();
    }

    function _widthdraw(address _address, uint256 _amount) private {
        (bool success, ) = _address.call{value: _amount}("");
        require(success, "Transfer failed.");
    }
}
function sendEther() public payable {
    uint256 supply = totalSupply();
    require(supply > 0);
}

// Send ETH to make the burn value higher