#
#   Copyright 2016 RIFT.IO Inc
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# Author(s): Varun Prasad
# Creation Date: 09/25/2016
#

import abc
import enum
import os
import uuid
import time


class InvalidDestinationError(Exception):
    pass


class DownloaderProtocol:
    """Listener of this class can implement the following method to get a
    callback
    """
    def on_download_started(self, job):
        """Called when the download starts

        Args:
            job (DownloadJob): Yang Model

        """
        pass

    def on_download_progress(self, job):
        """Called after each chunk is downloaded

        Args:
            job (DownloadJob): Yang Model

        """
        pass

    def on_download_succeeded(self, job):
        """Called when the download is completed successfully

        Args:
            job (DownloadJob): Yang Model

        """
        pass

    def on_download_failed(self, job):
        """Called when the download fails

        Args:
            job (DownloadJob): Yang Model

        """
        pass

    def on_download_cancelled(self, job):
        """Called when the download is canceled

        Args:
            job (DownloadJob): Yang Model

        """
        pass

    def on_download_finished(self, job):
        """Called when the download finishes regardless of the status of the
        download (success, failed or canceled)

        Args:
            job (DownloadJob): Yang Model

        """
        pass


class DownloadStatus(enum.Enum):
    STARTED = 1
    IN_PROGRESS = 2
    COMPLETED = 3
    FAILED = 4
    CANCELLED = 5


class DownloadMeta:
    """Model data used by the downloader.
    """
    def __init__(self, url, dest_file):
        self.url = url
        self.filepath = dest_file
        self.download_id = str(uuid.uuid4())
        self.bytes_total = 0
        self.progress_percent = 0
        self.bytes_downloaded = 0
        self.bytes_per_second = 0


        self.start_time = 0
        self.stop_time = 0
        self.detail = ""

    @property
    def filename(self):
        return os.path.basename(self.filepath)

    def start_download(self):
        self.start_time = time.time()

    def end_download(self):
        self.end_time = time.time()

    def set_state(self, state):
        self.status = state

    def update_with_data(self, downloaded_chunk):
        self.bytes_downloaded += len(downloaded_chunk)

        if self.bytes_total != 0:
            self.progress_percent = \
                int((self.bytes_downloaded / self.bytes_total) * 100)

        # compute bps
        seconds_elapsed = time.time() - self.start_time
        self.bytes_per_second = self.bytes_downloaded // seconds_elapsed

    def update_data_with_head(self, headers):
        """Update the model from the header of HEAD request

        Args:
            headers (dict): headers from HEAD response
        """
        if 'Content-Length' in headers:
            self.bytes_total = int(headers['Content-Length'])

    def as_dict(self):
        return self.__dict__


class AbstractDownloader:

    def __init__(self):
        self._delegate = None

    @property
    def delegate(self):
        return self._delegate

    @delegate.setter
    def delegate(self, delegate):
        self._delegate = delegate

    @abc.abstractproperty
    def download_id(self):
        pass

    @abc.abstractmethod
    def cancel_download(self):
        pass

    @abc.abstractmethod
    def close(self):
        pass

    @abc.abstractmethod
    def download(self):
        pass
