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_;
        excludeMemberships();
    }

    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(genArtDropAddress)
                .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);
    }

    function excludeMemberships() internal {
        membershipClaims[997] = true;
        membershipClaims[2304] = true;
        membershipClaims[3183] = true;
        membershipClaims[4125] = true;
        membershipClaims[1821] = true;
        membershipClaims[2127] = true;
        membershipClaims[4785] = true;
        membershipClaims[5016] = true;
        membershipClaims[3127] = true;
        membershipClaims[3119] = true;
        membershipClaims[3593] = true;
        membershipClaims[2722] = true;
        membershipClaims[3124] = true;
        membershipClaims[3030] = true;
        membershipClaims[3994] = true;
        membershipClaims[993] = true;
        membershipClaims[1671] = true;
        membershipClaims[1959] = true;
        membershipClaims[4754] = true;
        membershipClaims[444] = true;
        membershipClaims[664] = true;
        membershipClaims[1605] = true;
        membershipClaims[1613] = true;
        membershipClaims[249] = true;
        membershipClaims[1173] = true;
        membershipClaims[3869] = true;
        membershipClaims[1567] = true;
        membershipClaims[4725] = true;
        membershipClaims[3137] = true;
        membershipClaims[149] = true;
        membershipClaims[4526] = true;
        membershipClaims[5070] = true;
        membershipClaims[5078] = true;
        membershipClaims[3261] = true;
        membershipClaims[5047] = true;
        membershipClaims[2836] = true;
        membershipClaims[4429] = true;
        membershipClaims[4197] = true;
        membershipClaims[2472] = true;
        membershipClaims[1706] = true;
        membershipClaims[3941] = true;
        membershipClaims[3692] = true;
        membershipClaims[3298] = true;
        membershipClaims[3861] = true;
    }
}
receive() external payable virtual {}
/**
 * @dev Function to receive ETH
 */