# -*- coding: utf-8 -*-

import time
import mimetypes
import libtorrent as lt
import logging

from threading import Thread

from .utils import convb

logger = logging.getLogger(__name__)


class TorrentSession:
    """Torrent session"""

    ###############################################
    #               CLASS CONSTANTS               #
    ###############################################

    ROUTER_ADDR = ["router.bittorrent.com", "dht.transmissionbt.com"]
    ROUTER_PORT = 6881

    ###############################################
    #               Special Methods               #
    ###############################################

    def __init__(self, proxy_type=None, proxy_hostname=None, proxy_username=None, proxy_password=None):
        self._magnet = None
        self._handle = None
        self._temp_dir = str()
        self._download_limit = int()
        self._upload_limit = int()
        self._num_pieces = int()
        self._session = lt.session()

        pe = self._session.get_pe_settings()
        pe.out_enc_policy = lt.enc_policy.forced
        pe.in_enc_policy = lt.enc_policy.forced
        pe.allowed_enc_level = lt.enc_level.rc4
        pe.prefer_rc4 = True
        self._session.set_pe_settings(pe)

        ss = self._session.get_settings()
        # ss['anonymous_mode'] = True  # <- FIXME : seems to block download

        if proxy_type is not None:
            ps = lt.proxy_settings()
            if proxy_type == "none":
                ps.type = lt.proxy_type.none
            elif proxy_type == "socks4":
                ps.type = lt.proxy_type.socks4
            elif proxy_type == "socks5":
                ps.type = lt.proxy_type.socks5
            elif proxy_type == "socks5_pw":
                ps.type = lt.proxy_type.socks5_pw
            elif proxy_type == "http":
                ps.type = lt.proxy_type.http
            elif proxy_type == "http_pw":
                ps.type = lt.proxy_type.http_pw
            elif proxy_type == "i2p_proxy":
                ps.type = lt.proxy_type.i2p_proxy

            if proxy_type != "none":
                ss['force_proxy'] = True

            if proxy_hostname is not None:
                ps.hostname = proxy_hostname
            if proxy_username is not None:
                ps.username = proxy_username
            if proxy_password is not None:
                ps.password = proxy_password

            self._session.set_proxy(ps)

        self._session.set_settings(ss)

        self._session.listen_on(6881, 6891)
        self._session.start_dht()
        for addr in self.ROUTER_ADDR:
            self._session.add_dht_router(addr, self.ROUTER_PORT)
        self._session.start_upnp()

    ###############################################
    #               Private Methods               #
    ###############################################

    def _setup_magnet(self):
        """Add magnet (variable magnet is a magnet uri)"""

        params = {'save_path': self._temp_dir}
        self._handle = lt.add_magnet_uri(self._session, self._magnet, params)

        if self._upload_limit != -1:
            self._upload_limit *= 1000
        if self._download_limit != -1:
            self._download_limit *= 1000

        self._handle.set_upload_limit(int(self._upload_limit))
        self._handle.set_download_limit(int(self._download_limit))

        # To stream, we need the last piece
        while self.get_state() == "downloading metadata":
            logger.info('Waiting for metadata')
            time.sleep(1)

        logger.info('Set end and beginning a priority to download')
        torinfo = self._handle.get_torrent_info()
        self._num_pieces = torinfo.num_pieces()
        # First and last piece
        self._handle.piece_priority(0, 7)
        self._handle.piece_priority(self._num_pieces - 1, 7)

        while not self.ready2watch():
            time.sleep(1)

        # Sequential download
        logger.info('Set sequential download')
        self._handle.set_sequential_download(True)

    ##############################################
    #               Public Methods               #
    ##############################################

    def ready2watch(self):
        if self.get_state() == "downloading metadata":
            return False
        if self._handle.have_piece(0) and self._handle.have_piece(self._num_pieces - 1):
            return True
        else:
            return False

    def download(self, magnet, directory, up_limit=-1, down_limit=-1):
        self._magnet = magnet
        self._download_limit = int(down_limit)
        self._upload_limit = int(up_limit)
        self._temp_dir = directory

        t = Thread(target=self._setup_magnet)
        t.start()

    def get_progress(self):
        s = self._handle.status()
        return int(s.progress * 100)

    def get_peers(self):
        s = self._handle.status()
        return s.num_peers

    def get_downrate(self):
        s = self._handle.status()
        return convb(s.download_rate)

    def get_state(self):
        state_str = ['queued', 'checking', 'downloading metadata', 'downloading',
                     'finished', 'seeding', 'allocating', 'checking fastresume']
        s = self._handle.status()
        return state_str[s.state]

    def get_file_name(self):
        if self.get_progress() > 0:
            info = self._handle.get_torrent_info()
            files = info.files()

            # Try to find video file path
            for f in files:
                if "video" in mimetypes.guess_type(f.path)[0]:
                    # The video file must not be a sample of the real video
                    if f.path.find("sample") == -1 and f.path.find("Sample") == -1:
                        return f.path
        else:
            return ''

    def get_torrent_name(self):
        return self._handle.name()

    #######################################
    #               Getters               #
    #######################################

    @property
    def session(self):
        return self._session

    @property
    def magnet(self):
        return self._magnet

    @property
    def handle(self):
        try:
            return self._handle
        except AttributeError:
            return False

# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
