# Path: pycoinnet/peer/Fetcher.py
# class Fetcher:
#     """
#     Fetching a merkleblock also fetches the transactions that follow, and
#     includes them in the message as the "tx" key.
#     """
#     def __init__(self, peer):
#         self.peer = peer
#         self.request_q = asyncio.Queue()
#         self.futures = weakref.WeakValueDictionary()
# 
#         getdata_loop_future = asyncio.Task(self._getdata_loop())
#         next_message = peer.new_get_next_message_f(
#             filter_f=lambda name, data: name in ["tx", "block", "merkleblock", "notfound"])
#         peer.add_task(self._fetch_loop(next_message, getdata_loop_future))
# 
#     @asyncio.coroutine
#     def fetch(self, inv_item, timeout=None):
#         """
#         Return the fetched object or None if the remote says it doesn't have it, or
#         times out by exceeding `timeout` seconds.
#         """
#         future = self.futures.get(inv_item)
#         if not future:
#             future = asyncio.Future()
#             self.futures[inv_item] = future
#             self.request_q.put_nowait(inv_item)
#         try:
#             return (yield from asyncio.wait_for(future, timeout=timeout))
#         except asyncio.TimeoutError:
#             return None
# 
#     def queue_size(self):
#         pass
#         # ## TODO: finish
# 
#     @asyncio.coroutine
#     def _getdata_loop(self):
#         while True:
#             so_far = []
#             inv_item = yield from self.request_q.get()
#             while True:
#                 so_far.append(inv_item)
#                 if self.request_q.qsize() == 0 or len(so_far) >= 50000:
#                     break
#                 inv_item = yield from self.request_q.get()
#             self.peer.send_msg("getdata", items=so_far)
# 
#     @asyncio.coroutine
#     def _fetch_loop(self, next_message, getdata_loop_future):
#         try:
#             while True:
#                 name, data = yield from next_message()
#                 ITEM_LOOKUP = dict(tx="tx", block="block", merkleblock="header")
#                 if name in ITEM_LOOKUP:
#                     item = data[ITEM_LOOKUP[name]]
#                     the_hash = item.hash()
#                     TYPE_DB = {"tx": ITEM_TYPE_TX,
#                                "block": ITEM_TYPE_BLOCK,
#                                "merkleblock": ITEM_TYPE_MERKLEBLOCK}
#                     the_type = TYPE_DB[name]
#                     inv_item = InvItem(the_type, the_hash)
#                     future = self.futures.get(inv_item)
#                     if name == "merkleblock":
#                         txs = []
#                         for h in data["tx_hashes"]:
#                             name, data = yield from next_message()
#                             if name != "tx":
#                                 logging.error(
#                                     "insufficient tx messages after merkleblock message: missing %s",
#                                     b2h_rev(h))
#                                 del self.futures[inv_item]
#                                 future.set_result(None)
#                                 break
#                             tx = data["tx"]
#                             if tx.hash() != h:
#                                 logging.error(
#                                     "missing tx message after merkleblock message: missing %s", b2h_rev(h))
#                                 del self.futures[inv_item]
#                                 future.set_result(None)
#                                 break
#                             txs.append(tx)
#                         item.txs = txs
#                     if future is not None:
#                         del self.futures[inv_item]
#                         if not future.done():
#                             future.set_result(item)
#                         else:
#                             logging.info("got %s unsolicited", item.id())
#                 if name == "notfound":
#                     for inv_item in data["items"]:
#                         the_hash = inv_item.data
#                         future = self.futures.get(inv_item)
#                         if future:
#                             del self.futures[inv_item]
#                             future.set_result(None)
#         except EOFError:
#             getdata_loop_future.cancel()


import asyncio
import logging
import time
import weakref
from pycoinnet.peer.Fetcher import Fetcher
"""
InvCollector.py

Listen to peers and queue when new InvItem objects are seen.

Allow them to be fetched.

Advertise objects that are fetched (to peers that haven't told us they have it).
"""




class InvCollector:
    def __init__(self):
        self.inv_item_db = {}
        # key: InvItem; value: dictionary of peers to timestamps

        self.fetchers_by_peer = {}
        self.advertise_queues = weakref.WeakSet()
        self.inv_item_queues = weakref.WeakSet()
        self.inv_item_peers_q = {}

    def add_peer(self, peer):
        """
        Add a peer whose inv messages we want to monitor.
        """

# Path: pycoinnet/peer/Fetcher.py
# class Fetcher:
#     """
#     Fetching a merkleblock also fetches the transactions that follow, and
#     includes them in the message as the "tx" key.
#     """
#     def __init__(self, peer):
#         self.peer = peer
#         self.request_q = asyncio.Queue()
#         self.futures = weakref.WeakValueDictionary()
# 
#         getdata_loop_future = asyncio.Task(self._getdata_loop())
#         next_message = peer.new_get_next_message_f(
#             filter_f=lambda name, data: name in ["tx", "block", "merkleblock", "notfound"])
#         peer.add_task(self._fetch_loop(next_message, getdata_loop_future))
# 
#     @asyncio.coroutine
#     def fetch(self, inv_item, timeout=None):
#         """
#         Return the fetched object or None if the remote says it doesn't have it, or
#         times out by exceeding `timeout` seconds.
#         """
#         future = self.futures.get(inv_item)
#         if not future:
#             future = asyncio.Future()
#             self.futures[inv_item] = future
#             self.request_q.put_nowait(inv_item)
#         try:
#             return (yield from asyncio.wait_for(future, timeout=timeout))
#         except asyncio.TimeoutError:
#             return None
# 
#     def queue_size(self):
#         pass
#         # ## TODO: finish
# 
#     @asyncio.coroutine
#     def _getdata_loop(self):
#         while True:
#             so_far = []
#             inv_item = yield from self.request_q.get()
#             while True:
#                 so_far.append(inv_item)
#                 if self.request_q.qsize() == 0 or len(so_far) >= 50000:
#                     break
#                 inv_item = yield from self.request_q.get()
#             self.peer.send_msg("getdata", items=so_far)
# 
#     @asyncio.coroutine
#     def _fetch_loop(self, next_message, getdata_loop_future):
#         try:
#             while True:
#                 name, data = yield from next_message()
#                 ITEM_LOOKUP = dict(tx="tx", block="block", merkleblock="header")
#                 if name in ITEM_LOOKUP:
#                     item = data[ITEM_LOOKUP[name]]
#                     the_hash = item.hash()
#                     TYPE_DB = {"tx": ITEM_TYPE_TX,
#                                "block": ITEM_TYPE_BLOCK,
#                                "merkleblock": ITEM_TYPE_MERKLEBLOCK}
#                     the_type = TYPE_DB[name]
#                     inv_item = InvItem(the_type, the_hash)
#                     future = self.futures.get(inv_item)
#                     if name == "merkleblock":
#                         txs = []
#                         for h in data["tx_hashes"]:
#                             name, data = yield from next_message()
#                             if name != "tx":
#                                 logging.error(
#                                     "insufficient tx messages after merkleblock message: missing %s",
#                                     b2h_rev(h))
#                                 del self.futures[inv_item]
#                                 future.set_result(None)
#                                 break
#                             tx = data["tx"]
#                             if tx.hash() != h:
#                                 logging.error(
#                                     "missing tx message after merkleblock message: missing %s", b2h_rev(h))
#                                 del self.futures[inv_item]
#                                 future.set_result(None)
#                                 break
#                             txs.append(tx)
#                         item.txs = txs
#                     if future is not None:
#                         del self.futures[inv_item]
#                         if not future.done():
#                             future.set_result(item)
#                         else:
#                             logging.info("got %s unsolicited", item.id())
#                 if name == "notfound":
#                     for inv_item in data["items"]:
#                         the_hash = inv_item.data
#                         future = self.futures.get(inv_item)
#                         if future:
#                             del self.futures[inv_item]
#                             future.set_result(None)
#         except EOFError:
#             getdata_loop_future.cancel()


# Path: pycoinnet/peergroup/InvCollector.py
import asyncio
import logging
import time
import weakref
from pycoinnet.peer.Fetcher import Fetcher

"""
InvCollector.py

Listen to peers and queue when new InvItem objects are seen.

Allow them to be fetched.

Advertise objects that are fetched (to peers that haven't told us they have it).
"""




class InvCollector:
    def __init__(self):
        self.inv_item_db = {}
        # key: InvItem; value: dictionary of peers to timestamps

        self.fetchers_by_peer = {}
        self.advertise_queues = weakref.WeakSet()
        self.inv_item_queues = weakref.WeakSet()
        self.inv_item_peers_q = {}

    def add_peer(self, peer):
        """
        Add a peer whose inv messages we want to monitor.
        """

        self.fetchers_by_peer[peer] = Fetcher(peer)