from __future__ import absolute_import

import logging
import os
from hashlib import sha1

import libtorrent
from libtorrent import bencode

logger = logging.getLogger(__name__)


def commonprefix(l):
    # this unlike the os.path.commonprefix version always returns path prefixes as it compares
    # path component wise.
    cp = []
    ls = [p.split('/') for p in l]
    ml = min(len(p) for p in ls)

    for i in range(ml):

        s = set(p[i] for p in ls)
        if len(s) != 1:
            break

        cp.append(s.pop())

    return os.path.sep.join(cp)


def create_torrent_file(file_path_list, params, torrent_filepath=None):
    fs = libtorrent.file_storage()

    # filter all non-files
    file_path_list_filtered = []
    for path in file_path_list:
        if not os.path.exists(path):
            raise IOError('Path does not exist: %s' % path)
        elif os.path.isfile(path):
            file_path_list_filtered.append(path)

    # get the directory where these files are in. If there are multiple files, take the common directory they are in
    if len(file_path_list_filtered) == 1:
        base_path = os.path.split(file_path_list_filtered[0])[0]
    else:
        base_path = os.path.abspath(commonprefix(file_path_list_filtered))

    # the base_dir directory is the parent directory of the base_path and is passed to the set_piece_hash method
    base_dir = os.path.split(base_path)[0]

    if len(file_path_list_filtered) == 1:
        filename = os.path.basename(file_path_list_filtered[0])
        fs.add_file(filename, os.path.getsize(file_path_list_filtered[0]))
    else:
        for full_file_path in file_path_list_filtered:
            filename = os.path.join(base_path[len(base_dir) + 1:], full_file_path[len(base_dir):])[1:]
            fs.add_file(filename, os.path.getsize(full_file_path))

    if params.get(b'piece length'):
        piece_size = params[b'piece length']
    else:
        piece_size = 0

    flags = libtorrent.create_torrent_flags_t.optimize

    # This flag doesn't exist anymore in libtorrent V1.1.0
    if hasattr(libtorrent.create_torrent_flags_t, 'calculate_file_hashes'):
        flags |= libtorrent.create_torrent_flags_t.calculate_file_hashes

    from six import PY3
    params = {k: (v.decode('utf-8') if PY3 and isinstance(v, bytes) else v) for k, v in params.items()}

    torrent = libtorrent.create_torrent(fs, piece_size=piece_size, flags=flags)
    # Python2 wants binary, python3 want unicode
    if params.get(b'comment'):
        torrent.set_comment(params[b'comment'])
    if params.get(b'created by'):
        torrent.set_creator(params[b'created by'])
    # main tracker
    if params.get(b'announce'):
        torrent.add_tracker(params[b'announce'])
    # tracker list
    if params.get(b'announce-list'):
        tier = 1
        for tracker in params[b'announce-list']:
            torrent.add_tracker(tracker, tier=tier)
            tier += 1
    # DHT nodes
    # http://www.bittorrent.org/beps/bep_0005.html
    if params.get(b'nodes'):
        for node in params[b'nodes']:
            torrent.add_node(*node)
    # HTTP seeding
    # http://www.bittorrent.org/beps/bep_0017.html
    if params.get(b'httpseeds'):
        torrent.add_http_seed(params[b'httpseeds'])

    # Web seeding
    # http://www.bittorrent.org/beps/bep_0019.html
    if len(file_path_list) == 1:
        if params.get(b'urllist', False):
            torrent.add_url_seed(params[b'urllist'])

    # read the files and calculate the hashes
    if len(file_path_list) == 1:
        libtorrent.set_piece_hashes(torrent, base_path)
    else:
        libtorrent.set_piece_hashes(torrent, base_dir)

    t1 = torrent.generate()
    torrent = libtorrent.bencode(t1)

    if torrent_filepath:
        with open(torrent_filepath, 'wb') as f:
            f.write(torrent)

    return {
        'success': True,
        'base_path': base_path,
        'base_dir': base_dir,
        'torrent_file_path': torrent_filepath,
        'metainfo': torrent,
        'infohash': sha1(bencode(t1[b'info'])).digest()
    }


def get_info_from_handle(handle):
    # In libtorrent 0.16.18, the torrent_handle.torrent_file method is not available.
    # this method checks whether the torrent_file method is available on a given handle.
    # If not, fall back on the deprecated get_torrent_info
    try:
        if hasattr(handle, 'torrent_file'):
            return handle.torrent_file()
        return handle.get_torrent_info()
    except AttributeError as ae:
        logger.warning("No torrent info found from handle: %s", str(ae))
        return None
    except RuntimeError as e:  # This can happen when the torrent handle is invalid.
        logger.warning("Got exception when fetching info from handle: %s", str(e))
        return None
