# -*- coding: utf-8 -*-
"""Background worker that collects the daily preview thumbnails."""

from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.request import urlopen

from qgis.PyQt.QtCore import QThread, pyqtSignal

from .s2_cloud_service import daily_preview_url, list_daily_scenes, preview_region

THUMB_TIMEOUT = 60
# Each thumbnail is a separate Earth Engine request that spends nearly all of
# its time waiting, so they are fetched in parallel. Kept modest to stay well
# inside the Earth Engine request quota.
MAX_PARALLEL_THUMBS = 8


class PreviewLoadWorker(QThread):
    """
    Two phase loader.

    The scene list is one Earth Engine round trip, so it arrives quickly and the
    gallery can be drawn straight away. The thumbnails are then fetched by a
    small pool of threads and streamed back as they arrive, in whatever order
    they complete.
    """

    scenes_ready = pyqtSignal(list, bool)
    scenes_failed = pyqtSignal(str)
    thumb_ready = pyqtSignal(int, object)
    thumb_failed = pyqtSignal(int, str)
    all_done = pyqtSignal()

    def __init__(self, geometry, start_date, end_date, thumb_size=320, parent=None):
        super(PreviewLoadWorker, self).__init__(parent)
        self._geometry = geometry
        self._start_date = start_date
        self._end_date = end_date
        self._thumb_size = thumb_size

    def run(self):
        try:
            # Resolved once into plain coordinates, so none of the requests
            # below carries a geometry that Earth Engine has to compute.
            region = preview_region(self._geometry)
            scenes, truncated = list_daily_scenes(
                self._start_date, self._end_date, self._geometry, region=region,
            )
        except Exception as error:
            self.scenes_failed.emit(str(error))
            return

        self.scenes_ready.emit(scenes, truncated)
        if not scenes:
            # The dialog already reports the empty range, so there is nothing
            # left to finish.
            return

        self._fetch_thumbnails(scenes, region)

    def _fetch_thumbnails(self, scenes, region):
        workers = min(MAX_PARALLEL_THUMBS, len(scenes))
        pool = ThreadPoolExecutor(max_workers=workers)
        try:
            pending = dict(
                (pool.submit(self._thumbnail, scene['day'], region), index)
                for index, scene in enumerate(scenes)
            )
            for future in as_completed(pending):
                if self.isInterruptionRequested():
                    return
                index = pending[future]
                try:
                    self.thumb_ready.emit(index, future.result())
                except Exception as error:
                    self.thumb_failed.emit(index, str(error))
        finally:
            # Not waiting keeps closing the dialog instant: any request still in
            # flight finishes unnoticed, since only this thread emits signals.
            pool.shutdown(wait=False)

        self.all_done.emit()

    def _thumbnail(self, day, region):
        url = daily_preview_url(
            day, self._geometry, size=self._thumb_size, region=region,
        )
        # Earth Engine answers with an https url. Refusing anything else keeps
        # urlopen from reaching for a local file or another scheme.
        if not url.startswith('https://'):
            raise ValueError('Preview url is not https: {}'.format(url))
        return urlopen(url, timeout=THUMB_TIMEOUT).read()  # nosec B310
