contract NFTVotesToken is ERC721, ERC721Votes {
    uint256 tokenCount;
    mapping(uint256 => uint256) tokenIdToVotingPower;

    function initialize() public payable initializer {
        __ERC721_init("NFT Votes Token", "NVT");
    }

    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721, ERC721Votes) {
        _transferVotingUnits(from, to, LibVotesPower.votesStorage().tokenIdToVotingPower[tokenId]);
        super._afterTokenTransfer(from, to, tokenId);
    }

    function mint(address to, uint256 tokenPower) public {
        require(to != address(0), "ERC721: mint to the zero address");
        require(tokenPower != 0, "ERC721: power must be > 0");

        tokenCount++;
        uint256 tokenId = tokenCount;

        _beforeTokenTransfer(address(0), to, tokenId);

        tokenIdToVotingPower[tokenId] = tokenPower;
        _balances[to] += tokenPower;
        _owners[tokenId] = to;

        _mint(to, tokenId);
    }

    function _mint(address to, uint256 tokenId) internal override(ERC721) {
        emit Transfer(address(0), to, tokenId);
        _afterTokenTransfer(address(0), to, tokenId);
    }

    function transfer(
        address from,
        address to,
        uint256 tokenId
    ) public {
        _transfer(from, to, tokenId);
    }

    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721) {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        uint256 tokenPower = tokenIdToVotingPower[tokenId];
        _balances[from] -= tokenPower;
        _balances[to] += tokenPower;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    function burn(uint256 tokenId) public {
        _burn(tokenId);
    }

    function _burn(uint256 tokenId) internal override(ERC721) {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        ERC721Storage.layout()._balances[owner] -= tokenIdToVotingPower[tokenId];
        delete tokenIdToVotingPower[tokenId];
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }
}