contract GenArtTokenAirdrop is Ownable {
    address genArtTokenAddress;
    address genArtMembershipAddress;

    uint256 tokensPerMint = 213 * 1e18;
    uint256 endBlock;
    address genArtAirdropAddress;
    address genArtDropAddress;

    uint256[] airdropCollections = [1, 2];
    uint256[] dropCollectionGroups = [1, 2, 3, 4, 5, 6, 7, 8, 9];

    mapping(uint256 => bool) membershipClaims;
    event Claimed(address account, uint256 membershipId, uint256 amount);

    constructor(
        address genArtMembershipAddress_,
        address genArtTokenAddress_,
        address genArtAirdropAddress_,
        address genArtDropAddress_,
        uint256 endBlock_
    ) {
        genArtTokenAddress = genArtTokenAddress_;
        genArtMembershipAddress = genArtMembershipAddress_;
        genArtAirdropAddress = genArtAirdropAddress_;
        genArtDropAddress = genArtDropAddress_;
        endBlock = endBlock_;
    }

    function claimAllTokens() public {
        require(
            block.number < endBlock,
            "GenArtTokenAirdrop: token claiming window has ended"
        );
        uint256[] memory memberships = IGenArt(genArtMembershipAddress)
            .getTokensByOwner(msg.sender);
        require(
            memberships.length > 0,
            "GenArtTokenAirdrop: sender does not own memberships"
        );
        uint256 airdropTokenAmount = 0;
        for (uint256 i = 0; i < memberships.length; i++) {
            airdropTokenAmount += getAirdropTokenAmount(memberships[i]);
            membershipClaims[memberships[i]] = true;
            emit Claimed(msg.sender, memberships[i], airdropTokenAmount);
        }
        require(
            airdropTokenAmount > 0,
            "GenArtTokenAirdrop: no tokens to claim"
        );
        IERC20(genArtTokenAddress).transfer(msg.sender, airdropTokenAmount);
    }

    function claimTokens(uint256 membershipId) public {
        require(
            !membershipClaims[membershipId],
            "GenArtTokenAirdrop: tokens already claimed"
        );
        require(
            block.number < endBlock,
            "GenArtTokenAirdrop: token claiming window has ended"
        );
        require(
            IGenArt(genArtMembershipAddress).ownerOf(membershipId) ==
                msg.sender,
            "GenArtTokenAirdrop: sender is not owner of membership"
        );

        uint256 airdropTokenAmount = getAirdropTokenAmount(membershipId);

        require(
            airdropTokenAmount > 0,
            "GenArtTokenAirdrop: no tokens to claim"
        );
        IERC20(genArtTokenAddress).transfer(msg.sender, airdropTokenAmount);
        emit Claimed(msg.sender, membershipId, airdropTokenAmount);
        membershipClaims[membershipId] = true;
    }

    function getAirdropTokenAmountAccount(address account)
        public
        view
        returns (uint256)
    {
        uint256[] memory memberships = IGenArt(genArtMembershipAddress)
            .getTokensByOwner(account);
        uint256 airdropTokenAmount = 0;
        for (uint256 i = 0; i < memberships.length; i++) {
            airdropTokenAmount += getAirdropTokenAmount(memberships[i]);
        }

        return airdropTokenAmount;
    }

    function getAirdropTokenAmount(uint256 membershipId)
        public
        view
        returns (uint256)
    {
        if (membershipClaims[membershipId]) {
            return 0;
        }

        bool isGoldToken = IGenArt(genArtMembershipAddress).isGoldToken(
            membershipId
        );
        uint256 tokenAmount = 0;
        for (uint256 i = 0; i < airdropCollections.length; i++) {
            uint256 remainingMints = IGenArtAirdrop(genArtAirdropAddress)
                .getAllowedMintForMembership(
                    airdropCollections[i],
                    membershipId
                );

            uint256 mints = (isGoldToken ? 5 : 1) - remainingMints;
            tokenAmount = tokenAmount + (mints * tokensPerMint);
        }

        for (uint256 i = 0; i < dropCollectionGroups.length; i++) {
            uint256 remainingMints = IGenArtDrop(genArtAirdropAddress)
                .getAllowedMintForMembership(
                    dropCollectionGroups[i],
                    membershipId
                );

            uint256 mints = (isGoldToken ? 5 : 1) - remainingMints;
            tokenAmount = tokenAmount + (mints * tokensPerMint);
        }

        return tokenAmount;
    }

    /**
     * @dev Function to receive ETH
     */
    receive() external payable virtual {}

    function withdrawTokens(uint256 _amount, address _to) public onlyOwner {
        IERC20(genArtTokenAddress).transfer(_to, _amount);
    }

    function withdraw(uint256 value) public onlyOwner {
        address _owner = owner();
        payable(_owner).transfer(value);
    }
}
/**
 * @dev Function to receive ETH
 */