# pylint: skip-file
import binascii
from lbrynet.core.StreamDescriptor import StreamMetadata
from lbrynet.cryptstream.client.CryptStreamDownloader import CryptStreamDownloader
from zope.interface import implements
from lbrynet.lbrylive.client.LiveStreamMetadataHandler import LiveStreamMetadataHandler
from lbrynet.lbrylive.client.LiveStreamProgressManager import LiveStreamProgressManager
import os
from lbrynet.lbrylive.StreamDescriptor import save_sd_info
from lbrynet.lbrylive.PaymentRateManager import LiveStreamPaymentRateManager
from twisted.internet import defer, threads  # , process
from lbrynet.interfaces import IStreamDownloaderFactory
from lbrynet.lbrylive.StreamDescriptor import LiveStreamType


class _LiveStreamDownloader(CryptStreamDownloader):

    def __init__(self, stream_hash, peer_finder, rate_limiter, blob_manager, stream_info_manager,
                 payment_rate_manager, wallet, upload_allowed):
        CryptStreamDownloader.__init__(self, peer_finder, rate_limiter, blob_manager,
                                       payment_rate_manager, wallet, upload_allowed)
        self.stream_hash = stream_hash
        self.stream_info_manager = stream_info_manager
        self.public_key = None

    def set_stream_info(self):
        if self.public_key is None and self.key is None:

            d = self.stream_info_manager.get_stream_info(self.stream_hash)

            def set_stream_info(stream_info):
                public_key, key, stream_name = stream_info
                self.public_key = public_key
                self.key = binascii.unhexlify(key)
                self.stream_name = binascii.unhexlify(stream_name)

            d.addCallback(set_stream_info)
            return d
        else:
            return defer.succeed(True)


class LiveStreamDownloader(_LiveStreamDownloader):
    def __init__(self, stream_hash, peer_finder, rate_limiter, blob_manager, stream_info_manager,
                 payment_rate_manager, wallet, upload_allowed):
        _LiveStreamDownloader.__init__(self, stream_hash, peer_finder, rate_limiter, blob_manager,
                                      stream_info_manager, payment_rate_manager, wallet, upload_allowed)


    def _get_metadata_handler(self, download_manager):
        return LiveStreamMetadataHandler(self.stream_hash, self.stream_info_manager,
                                         self.peer_finder, self.public_key, False,
                                         self.payment_rate_manager, self.wallet, download_manager, 10)

    def _get_progress_manager(self, download_manager):
        return LiveStreamProgressManager(self._finished_downloading, self.blob_manager, download_manager,
                                         delete_blob_after_finished=True, download_whole=False,
                                         max_before_skip_ahead=10)

    def _get_write_func(self):
        def write_func(data):
            if self.stopped is False:
                pass
        return write_func


class FullLiveStreamDownloader(_LiveStreamDownloader):
    def __init__(self, stream_hash, peer_finder, rate_limiter, blob_manager, stream_info_manager,
                 payment_rate_manager, wallet, upload_allowed):
        _LiveStreamDownloader.__init__(self, stream_hash, peer_finder, rate_limiter,
                                      blob_manager, stream_info_manager, payment_rate_manager,
                                      wallet, upload_allowed)
        self.file_handle = None
        self.file_name = None

    def set_stream_info(self):
        d = _LiveStreamDownloader.set_stream_info(self)

        def set_file_name_if_unset():
            if not self.file_name:
                if not self.stream_name:
                    self.stream_name = "_"
                self.file_name = os.path.basename(self.stream_name)

        d.addCallback(lambda _: set_file_name_if_unset())
        return d

    def stop(self, err=None):
        d = self._close_file()
        d.addBoth(lambda _: _LiveStreamDownloader.stop(self, err))
        return d

    def _start(self):
        if self.file_handle is None:
            d = self._open_file()
        else:
            d = defer.succeed(True)
        d.addCallback(lambda _: _LiveStreamDownloader._start(self))
        return d

    def _open_file(self):
        def open_file():
            self.file_handle = open(self.file_name, 'wb')
        return threads.deferToThread(open_file)

    def _get_metadata_handler(self, download_manager):
        return LiveStreamMetadataHandler(self.stream_hash, self.stream_info_manager,
                                         self.peer_finder, self.public_key, True,
                                         self.payment_rate_manager, self.wallet, download_manager)

    def _get_primary_request_creators(self, download_manager):
        return [download_manager.blob_requester, download_manager.blob_info_finder]

    def _get_write_func(self):
        def write_func(data):
            if self.stopped is False:
                self.file_handle.write(data)
        return write_func

    def _close_file(self):
        def close_file():
            if self.file_handle is not None:
                self.file_handle.close()
                self.file_handle = None
        return threads.deferToThread(close_file)


class FullLiveStreamDownloaderFactory(object):

    implements(IStreamDownloaderFactory)

    def __init__(self, peer_finder, rate_limiter, blob_manager, stream_info_manager, wallet,
                 default_payment_rate_manager):
        self.peer_finder = peer_finder
        self.rate_limiter = rate_limiter
        self.blob_manager = blob_manager
        self.stream_info_manager = stream_info_manager
        self.wallet = wallet
        self.default_payment_rate_manager = default_payment_rate_manager

    def can_download(self, sd_validator):
        return True

    def make_downloader(self, metadata, options, payment_rate_manager):
        # TODO: check options for payment rate manager parameters
        prm = LiveStreamPaymentRateManager(self.default_payment_rate_manager,
                                                            payment_rate_manager)

        def save_source_if_blob(stream_hash):
            if metadata.metadata_source == StreamMetadata.FROM_BLOB:
                d = self.stream_info_manager.save_sd_blob_hash_to_stream(stream_hash, metadata.source_blob_hash)
            else:
                d = defer.succeed(True)
            d.addCallback(lambda _: stream_hash)
            return d

        d = save_sd_info(self.stream_info_manager, metadata.validator.raw_info)
        d.addCallback(save_source_if_blob)

        def create_downloader(stream_hash):
            stream_downloader = FullLiveStreamDownloader(stream_hash, self.peer_finder, self.rate_limiter,
                                                         self.blob_manager, self.stream_info_manager,
                                                         prm, self.wallet, True)
            # TODO: change upload_allowed=True above to something better
            d = stream_downloader.set_stream_info()
            d.addCallback(lambda _: stream_downloader)
            return d

        d.addCallback(create_downloader)
        return d


def add_full_live_stream_downloader_to_sd_identifier(session, stream_info_manager, sd_identifier,
                                                     base_live_stream_payment_rate_manager):
    downloader_factory = FullLiveStreamDownloaderFactory(session.peer_finder,
                                                         session.rate_limiter,
                                                         session.blob_manager,
                                                         stream_info_manager,
                                                         session.wallet,
                                                         base_live_stream_payment_rate_manager)
    sd_identifier.add_stream_downloader_factory(LiveStreamType, downloader_factory)