# Copyright (C) 2010-2018  Vincent Pelletier <plr.vincent@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
from __future__ import print_function
from setuptools import setup
from setuptools import Command
import setuptools.command.install
from codecs import open
import errno
import hashlib
import os
import subprocess
import sys
import versioneer
try:
    from urllib.request import urlopen
    from urllib.parse import urlsplit
    from html.parser import HTMLParser
except ImportError:
    # BBB: python 2.7
    from urllib2 import urlopen
    from urlparse import urlsplit
    from HTMLParser import HTMLParser

if os.getenv('I_KNOW_HOW_TO_RELEASE_PYTHON_LIBUSB1') != '1' and any(
    x in sys.argv for x in ('sdist', 'upload')
):
    print('Use setup.sh to build')
    sys.exit(1)

CURRENT_WINDOWS_7Z_SHA256 = (
    'd3087e7d09ec4e463f5f4b394dcfec0b90e835545318af1a75575de59d2dfaac'
)

cmdclass = versioneer.get_cmdclass()
class install(setuptools.command.install.install):
    def run(self):
        # XXX: setuptools.command.install.install is an old-style class on
        # python2.7 :(
        setuptools.command.install.install.run(self)
        if os.getenv('LIBUSB_BINARY'):
            self.copy_file(
                os.getenv('LIBUSB_BINARY'),
                os.path.join(self.install_lib, 'usb1'),
            )
cmdclass['install'] = install

class upload(Command):
    def run(self):
        print('This project uses signed releases. See KEYS for instructions.')
        print('Hint:')
        print('  twine upload dist/<release file> dist/<release file>.asc')
        sys.exit(1)
cmdclass['upload'] = upload

class update_libusb(Command):
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    class WindowsBinariesArchiveLinkFinder(HTMLParser):
        found = None
        __a_href = None
        def handle_starttag(self, tag, attrs):
            if tag == 'a':
                assert self.__a_href is None, repr(self.__a_href)
                self.__a_href = dict(attrs).get('href')

        def handle_endtag(self, tag):
            if tag == 'a':
                self.__a_href = None

        def handle_data(self, data):
            if self.__a_href is not None and data == 'Latest Windows Binaries':
                assert self.found is None, repr(self.found)
                self.found = self.__a_href

    def run(self):
        finder = self.WindowsBinariesArchiveLinkFinder()
        finder.feed(urlopen('https://libusb.info/').read().decode('utf-8'))
        finder.close()
        url = finder.found
        if url is None:
            raise ValueError('Failed to locate current windows binary release')
        build_dir = os.path.join(os.path.dirname(__file__), 'build')
        download_cache_path = os.path.join(build_dir, 'download-cache')
        archive_path = os.path.join(
            download_cache_path,
            urlsplit(url).path.rsplit('/', 1)[-1],
        )
        if not os.path.exists(archive_path):
            os.makedirs(download_cache_path)
            with open(archive_path, 'wb') as archive_file:
                archive_file.write(urlopen(url).read())
        with open(archive_path, 'rb') as archive_file:
            archive_sha256 = hashlib.sha256(archive_file.read()).hexdigest()
        if archive_sha256 != CURRENT_WINDOWS_7Z_SHA256:
            raise ValueError(
                'Windows release sha56 mismatch: %r fetched with a sha256 of %r' % (
                    url,
                    archive_sha256,
                )
            )
        # py2 does not have subprocess.DEVNULL.
        with open(os.devnull, 'wb') as devnull:
            for arch_path, out_dir in (
                ('MS32/dll/libusb-1.0.dll', os.path.join(build_dir, 'win32')),
                ('MS64/dll/libusb-1.0.dll', os.path.join(build_dir, 'win_amd64')),
            ):
                subprocess.check_call(
                    [
                        '7z', 'e', '-aoa',
                        '-o' + out_dir,
                        archive_path,
                        arch_path,
                    ],
                    # 7z will not shut its pie hole.
                    stdout=devnull,
                    close_fds=True,
                )
cmdclass['update_libusb'] = update_libusb

long_description = open(
    os.path.join(os.path.dirname(__file__), 'README.rst'),
    encoding='utf8',
).read()

setup(
    name='libusb1',
    description=next(x for x in long_description.splitlines() if x.strip()),
    long_description='.. contents::\n\n' + long_description,
    keywords='usb libusb',
    version=versioneer.get_version(),
    cmdclass=cmdclass,
    author='Vincent Pelletier',
    author_email='plr.vincent@gmail.com',
    url='http://github.com/vpelletier/python-libusb1',
    license='LGPLv2.1+',
    platforms=['any'],
    py_modules=['libusb1'],
    packages=['usb1'],
    classifiers=[
        'Intended Audience :: Developers',
        'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
        'Operating System :: OS Independent',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Programming Language :: Python :: Implementation :: CPython',
        'Programming Language :: Python :: Implementation :: PyPy',
        'Topic :: Software Development :: Libraries',
        'Topic :: System :: Hardware :: Hardware Drivers',
    ],
    setup_requires=[
        'wheel',
    ],
    use_2to3=True,
    test_suite='usb1.testUSB1',
)
