abstract contract GovernorCountingSimple is Governor {
    /**
     * @dev Supported vote types. Matches Governor Bravo ordering.
     */
    enum VoteType {
        Against,
        For,
        Abstain
    }

    /**
     * @dev See {IGovernor-COUNTING_MODE}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function COUNTING_MODE()
        public
        pure
        virtual
        override
        returns (string memory)
    {
        return "support=bravo&quorum=for,abstain";
    }

    function getVotes(address account, uint256)
        public
        view
        virtual
        override
        returns (uint256)
    {
        return token.balanceOf(account);
    }

    function votesLeft(uint256 proposalId, address account)
        public
        view
        virtual
        returns (uint256 voteAmount)
    {
        uint256 tokenCount = token.balanceOf(account);
        uint256 tokenId;
        ProposalVote storage proposalvote = _proposals[proposalId];
        for (uint256 i = 0; i < tokenCount; i++) {
            tokenId = token.tokenOfOwnerByIndex(account, i);
            if (
                !getTokenHasVoted(proposalId, tokenId) &&
                //proposalvote.tokenHasVoted[tokenId] == 0 &&
                tokenId < proposalvote.maxVotes
            ) {
                voteAmount += 1;
            }
        }
        return voteAmount;
    }

    /**
     * @dev See {IGovernor-hasVoted}.
     */
    function hasVoted(uint256, address)
        public
        view
        virtual
        override
        returns (bool)
    {
        require(false, "Don't use this method");
        return false;
        //return _proposals[proposalId].voteCount[account] > 0;
    }

    /**
     * @dev Accessor to the internal vote counts.
     */
    function proposalVotes(uint256 proposalId)
        public
        view
        virtual
        returns (
            uint256 againstVotes,
            uint256 forVotes,
            uint256 abstainVotes
        )
    {
        ProposalVote storage proposalvote = _proposals[proposalId];
        return (
            proposalvote.againstVotes,
            proposalvote.forVotes,
            proposalvote.abstainVotes
        );
    }

    function getProposal(uint256 proposalId)
        public
        view
        virtual
        returns (
            string memory title,
            string memory currentState,
            uint256 againstVotes,
            uint256 forVotes,
            uint256 abstainVotes,
            uint256 totalVotes,
            uint256 currentQuorum,
            uint256 minQuorum,
            uint256 maxVotes
        )
    {
        string[8] memory proposalStates;
        proposalStates = [
            "Pending",
            "Active",
            "Canceled",
            "Defeated",
            "Succeeded",
            "Queued",
            "Expired",
            "Executed"
        ];

        ProposalVote storage proposalvote = _proposals[proposalId];
        return (
            proposalTitles[proposalId],
            proposalStates[uint256(state(proposalId))],
            proposalvote.againstVotes,
            proposalvote.forVotes,
            proposalvote.abstainVotes,
            proposalvote.againstVotes +
                proposalvote.forVotes +
                proposalvote.abstainVotes,
            proposalvote.forVotes + proposalvote.abstainVotes,
            quorum(proposalvote.maxVotes),
            proposalvote.maxVotes
        );
    }

    /*
    function getProposalAccountVotes(uint256 proposalId, address owner)
        public
        view
        virtual
        returns (uint256 ownerVoteCount)
    {
        ProposalVote storage proposalvote = _proposals[proposalId];
        return (proposalvote.voteCount[owner]);
    }
*/
    function hasTokenVoted(uint256 proposalId, uint256 tokenId)
        public
        view
        virtual
        returns (bool tokenVoted)
    {
        //ProposalVote storage proposalvote = _proposals[proposalId];
        //return (proposalvote.tokenHasVoted[tokenId] == 1);
        return getTokenHasVoted(proposalId, tokenId);
    }

    function _quorumReached(uint256 proposalId)
        internal
        view
        virtual
        override
        returns (bool)
    {
        ProposalVote storage proposalvote = _proposals[proposalId];

        return
            quorum(_proposals[proposalId].maxVotes) <=
            proposalvote.forVotes + proposalvote.abstainVotes;
    }

    function _voteSucceeded(uint256 proposalId)
        internal
        view
        virtual
        override
        returns (bool)
    {
        ProposalVote storage proposalvote = _proposals[proposalId];

        return proposalvote.forVotes > proposalvote.againstVotes;
    }

    function _countVote(
        uint256 proposalId,
        address account,
        uint8 support
    ) internal virtual override returns (uint16 voteAmount) {
        ProposalVote storage proposalvote = _proposals[proposalId];
        uint256[40] storage vState = proposalvote.tokenHasVoted;
        uint256[] memory tokens = token.walletOfOwner(account);
        uint256 tokenId;
        uint256 idxInt;
        uint256 idxBool;
        for (uint256 i = 0; i < tokens.length; i++) {
            tokenId = tokens[i];
            idxInt = tokenId / 40;
            idxBool = tokenId % 256;
            if (
                ((vState[idxInt] >> idxBool) & uint256(1)) != 1 &&
                tokenId < proposalvote.maxVotes
            ) {
                vState[idxInt] = vState[idxInt] | (uint256(1) << idxBool);
                voteAmount += 1;
            }
        }
        require(voteAmount > 0, "GovernorVotingSimple: No votes left");
        if (support == uint8(VoteType.Against)) {
            proposalvote.againstVotes += voteAmount;
        } else if (support == uint8(VoteType.For)) {
            proposalvote.forVotes += voteAmount;
        } else if (support == uint8(VoteType.Abstain)) {
            proposalvote.abstainVotes += voteAmount;
        } else {
            revert("GovernorVotingSimple: invalid value for enum VoteType");
        }
    }
}
function proposalVotes(uint256 proposalId)
    public
    view
    virtual
    returns (
        uint256 againstVotes,
        uint256 forVotes,
        uint256 abstainVotes
    )
{
    ProposalVote storage proposalvote = _proposals[proposalId];
    return (
        proposalvote.againstVotes,
        proposalvote.forVotes,
        proposalvote.abstainVotes
    );
}

/**
 * @dev Accessor to the internal vote counts.
 */