contract MREIT is ERC20, Ownable, IMerkleDistributor {
    
    // Defines how to read the TokenInfo ABI, as well as the capabilities of the token
    uint256 public TOKEN_TYPE = 1;
    
    struct TokenInfo {
        uint8 decimals;
        address creator;
    }
    
    TokenInfo public INFO;

    bytes32 public override merkleRoot;
    // address of token i.e MREIT
    address public immutable override token;
    // This is a packed array of booleans for verifying the claims.
    mapping(uint256 => uint256) private claimedBitMap;
    

    constructor(
        string memory _name, 
        string memory _symbol, 
        uint8 _decimals, 
        address _creator, 
        uint256 _totalSupply, 
        bytes32 _merkleRoot
    ) ERC20(_name, _symbol) {
        _mint(msg.sender, _totalSupply);
        INFO = TokenInfo(_decimals, _creator);
        merkleRoot = _merkleRoot;
        token = address(this);
    }
    
    function decimals() public view virtual override returns (uint8) {
        return INFO.decimals;
    }

    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }
    

    ///@dev set claim for the given index
    function _setClaimed(uint256 index) internal {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex);
    }

    ///@dev called by the user to claim the token
    ///@param index sequential index 
    ///@param account account of the claim address
    ///@param amount amount of claim 
    ///@param merkleProof merkleProof for the claim of the account
    function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) external override {
        require(!isClaimed(index), 'MerkleDistributor: Drop already claimed.');

        // Verify the merkle proof.
        bytes32 node = keccak256(abi.encodePacked(index, account, amount));
        require(MerkleProof.verify(merkleProof, merkleRoot, node), 'MerkleDistributor: Invalid proof.');

        // Mark it claimed and send the token.
        _setClaimed(index);
        require(transfer(account, amount), 'MerkleDistributor: Transfer failed.');

        emit Claimed(index, account, amount);
    }

    ///@dev look if already claimed or not for the index
    ///@param index index of the token
    function isClaimed(uint256 index) public view override returns (bool) {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        uint256 claimedWord = claimedBitMap[claimedWordIndex];
        uint256 mask = (1 << claimedBitIndex);
        return claimedWord & mask == mask;
    }


    ///@dev update merkle root 
    ///@param _newMerkleRoot new merkle root for the distribution
    function updateMerkleRoot(bytes32 _newMerkleRoot) external onlyOwner{
        merkleRoot = _newMerkleRoot;
    }

    ///@dev called by the owner of the contract to rescue the token from the contract
    ///@param amount amount of token to withdraw
    function withdrawToken(uint256 amount) external onlyOwner{
        transfer(msg.sender, amount);
    }
    
}
// A fully ERC20 Compliant Non Mintable Token (ENMT)
function updateMerkleRoot(bytes32 _newMerkleRoot) external onlyOwner{
    merkleRoot = _newMerkleRoot;
}

///@dev update merkle root 
///@param _newMerkleRoot new merkle root for the distribution