contract FlyPass is ERC721, Ownable, PaymentSplitter {
    // Libraries
    using ECDSA for bytes32;

    bool _stopMint;

    // Define critical mint parameters
    uint256 _walletLimit;
    uint256 _mintPrice;
    uint256 _startingBlock;
    uint256 _maxSupply;

    address _devWallet;

    address _openSea = 0xa5409ec958C83C3f309868babACA7c86DCB077c1;

    address[] foundingFlyz = [
        0xeE24c06ae9469E29Fc0107048C2B4e806970ecdA,
        0x7d6F7FB391CD36BAde7baa7465581d9826a8D97D,
        0x665C5C08465E7c52DCD869a5998E11D8A6CdE361,
        0x9A665fb3fFB5906F47C438716D1E009767F96BBD,
        0x60916B17F8B0B9194baa5eCA43b7E1583b99A714,
        0xDAcb094be451A45D6C54fB18Eed1931D04F5793c,
        0xc2DfCFa2Bf1C871cB5af60b7eFBd95864984129D,
        0x155a3b74c26955Ca5174500A8f83947d7793bDd2,
        0xbB599fbd3BB5ce9326EA50dE38D09D5B946B24C1
    ];

    uint256[] foundingShares = [
        27,
        20,
        15,
        15,
        5,
        5,
        5,
        5,
        3
    ];

    // Constructor
    constructor
    (address devWallet)
    ERC721("FLY Pass", "FFMP")
    PaymentSplitter(foundingFlyz,foundingShares)
    {
        _devWallet = devWallet;
    }

    // Strictly for UI purposes.
    function maxSupply(uint256 startingBlock, uint256 currentBlock) internal view returns (uint256) {
        uint256 currentMaxSupply;
        if (startingBlock > currentBlock || _stopMint) {
            return 0;
        } else {
            uint256 blocksElapsed = currentBlock - startingBlock;
            if (blocksElapsed < 5760) {
                currentMaxSupply = 2858;
            } else if (blocksElapsed < 7200) {
                currentMaxSupply = 8888;
            } else if (blocksElapsed < 8640) {
                currentMaxSupply = 7888;
            } else if (blocksElapsed < 10080) {
                currentMaxSupply = 6888;
            } else if (blocksElapsed < 11520) {
                currentMaxSupply = 5888;
            } else if (blocksElapsed < 12960) {
                currentMaxSupply = 4888;
            } else if (blocksElapsed < 14400) {
                currentMaxSupply = 3888;
            } else if (blocksElapsed < 15840) {
                currentMaxSupply = 2888;
            } else if (blocksElapsed < 17280) {
                currentMaxSupply = 1888;
            } else {
                currentMaxSupply = 0;
            }

            return totalSupply() < currentMaxSupply ? currentMaxSupply : totalSupply();
        }
    }

    function getMaxSupply() public view returns (uint256) {
        return maxSupply(_startingBlock, block.number);
    }

    function contractURI() public pure returns (string memory) {
        return "https://frenlyflyz.io/ffmp-collection-metadata/";
    }

    function tokenURI(uint256 tokenId) public view override(ERC721) returns (string memory) {
        require(_exists(tokenId), "Doesn't exist!");
        return "ipfs://QmQrcFTeEWWMr5QRzGUT7KqGFotLbsHscRV2HV5SEpuE3z";
    }

    // Free OS Listings
    function isApprovedForAll(
        address owner,
        address operator
    )
        public
        view
        override(ERC721)
        returns (bool)
    {
        // Whitelist OpenSea proxy contract for easy trading.
        ProxyRegistry proxyRegistry = ProxyRegistry(_openSea);
        if (address(proxyRegistry.proxies(owner)) == operator) {
            return true;
        }

        return ERC721.isApprovedForAll(owner, operator);
    }

    // To save on gas fees, we write new balances to _minted mapping at mint time.
    // On first transfer from account with non-zero _minted value, we copy _minted
    // to _balances. Likewise on first transfer to a non-zero minted value. We write
    // 10 to the minted count to prevent any further mints, and to signal that
    // balances have been synchronized with minted quantity.
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override (ERC721) {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        if (_minted[from] != 0 && _minted[from] != 10) {
            _balances[from] += _minted[from];
            _minted[from] = 10;
        }

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

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    function balanceOf(address owner) public view virtual override(ERC721) returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        if (_minted[owner] != 10) {
            return _balances[owner] + _minted[owner];
        } else {
            return _balances[owner];
        }
    }

    // Expected starting block is 14149694
    function startMint(uint256 startingBlock) public onlyOwner {
        require(totalSupply() == 0, "Cannot restart mint!");
        // We mint to dev wallet so we don't need to implement safe mint, which
        // causes more problems than not. Tokens will be transferred to gnosis
        // asap.
        _mint(_devWallet, 188, 1, "");
        _startingBlock = startingBlock;
    }

    function stopMint() public onlyOwner {
        _stopMint = true;
    }

    function mint(uint256 rawQuantity, uint256 mintType, bytes memory signature) public payable {
        // No total supply check on FL mint, since supply is artificially limited
        // to ensure FL members can obtain a maximum of 2500 NFTs in total. Supply
        // constraints achieved cryptographically by restricting entries on fly
        // and og lists.
        //
        // Passes acquired on secondary markets will count against total wallet
        // limit. See overridden balanceOf for comments and operation of wallet
        // limiting mechanic.
        //
        // Limit wallets and ensure sale has started.
        require(
            (block.number > _startingBlock) &&
            maxSupply(_startingBlock, block.number) > 0 &&
            (7 > balanceOf(msg.sender) + rawQuantity),
                "Mint Not Allowed!");

        bytes32 hash = keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n32",
            keccak256(abi.encodePacked(msg.sender, mintType, rawQuantity)))
          );

        // Values hard coded to reduce SLOAD.
        require(
            hash.recover(signature) == 0x87A7219276164F5740302c24f5D709830f4Ed91F,
                "Mint not signed!");

        uint256 quantity;

        // Mint type restriction will be imposed by WL administration system. After 24
        // hours, OG and Fly pass do not benefit from reduced price, 5 + 1 benefit, or
        // the supply check bypass.
        require(0 <= mintType && mintType <= 3,
            "Hmm");
        if (mintType == 0) {
            require(msg.value >= rawQuantity * 0.04 ether,
                "Not enough eth");
            quantity = rawQuantity;
            // Strict supply check imposed on public mint.
            require((maxSupply(_startingBlock, block.number) > totalSupply() + quantity),
                "Not enough Supply");
        } else if (mintType == 1) {
            require(msg.value >= rawQuantity * 0.03 ether,
                "Not enough eth");
            // Credit one additional mint if paid for five
            quantity = rawQuantity == 5 ? 6 : rawQuantity;
        } else if (mintType == 2) {
            require(msg.value >= rawQuantity * 0.025 ether,
                "Not enough eth");
            // Credit one additional mint if paid for five
            quantity = rawQuantity == 5 ? 6 : rawQuantity;
        }

        _mint(msg.sender, quantity, 0, "");
    }
}
function isApprovedForAll(
    address owner,
    address operator
)
    public
    view
    override(ERC721)
    returns (bool)
{
    // Whitelist OpenSea proxy contract for easy trading.
    ProxyRegistry proxyRegistry = ProxyRegistry(_openSea);
    if (address(proxyRegistry.proxies(owner)) == operator) {
        return true;
    }

    return ERC721.isApprovedForAll(owner, operator);
}

// Free OS Listings