import os, os.path, subprocess, shutil

try:
    from urllib.request import urlopen
except ImportError:
    from urllib import urlopen

python_versions = ['2.6.8', '2.7.5', '3.1.5', '3.2.5', '3.3.5', '3.4.1']
libcurl_versions = ['7.19.0', '7.46.0']

libcurl_meta = {
    '7.19.0': {
        'patches': [
            'curl-7.19.0-sslv2-c66b0b32fba-modified.patch',
            #'curl-7.19.0-sslv2-2b0e09b0f98.patch',
        ],
    },
}

root = os.path.abspath(os.path.dirname(__file__))

class in_dir:
    def __init__(self, dir):
        self.dir = dir

    def __enter__(self):
        self.oldwd = os.getcwd()
        os.chdir(self.dir)

    def __exit__(self, type, value, traceback):
        os.chdir(self.oldwd)

def subprocess_check_call(cmd, **kwargs):
    try:
        subprocess.check_call(cmd, **kwargs)
    except OSError as exc:
        message = exc.args[0]
        message = '%s while trying to execute %s' % (message, str(cmd))
        args = tuple([message] + exc.args[1:])
        raise type(exc)(args)

def fetch(url, archive=None):
    if archive is None:
        archive = os.path.basename(url)
    if not os.path.exists(archive):
        sys.stdout.write("Fetching %s\n" % url)
        io = urlopen(url)
        with open('.tmp.%s' % archive, 'wb') as f:
            while True:
                chunk = io.read(65536)
                if len(chunk) == 0:
                    break
                f.write(chunk)
        os.rename('.tmp.%s' % archive, archive)

def build(archive, dir, prefix, meta=None):
    if not os.path.exists(dir):
        sys.stdout.write("Building %s\n" % archive)
        subprocess_check_call(['tar', 'xf', archive])
        with in_dir(dir):
            if meta and 'patches' in meta:
                for patch in meta['patches']:
                    patch_path = os.path.join(root, 'matrix', patch)
                    subprocess_check_call(['patch', '-p1', '-i', patch_path])
            subprocess_check_call(['./configure', '--prefix=%s' % prefix])
            if 'post-configure' in meta:
                for cmd in meta['post-configure']:
                    subprocess_check_call(cmd, shell=True)
            subprocess_check_call(['make'])
            subprocess_check_call(['make', 'install'])

def run_matrix(python_versions, libcurl_versions):
    for python_version in python_versions:
        url = 'http://www.python.org/ftp/python/%s/Python-%s.tgz' % (python_version, python_version)
        archive = os.path.basename(url)
        fetch(url, archive)

        dir = archive.replace('.tgz', '')
        prefix = os.path.abspath('i/%s' % dir)
        build(archive, dir, prefix)

    for libcurl_version in libcurl_versions:
        url = 'https://curl.haxx.se/download/curl-%s.tar.gz' % libcurl_version
        archive = os.path.basename(url)
        fetch(url, archive)

        dir = archive.replace('.tar.gz', '')
        prefix = os.path.abspath('i/%s' % dir)
        build(archive, dir, prefix, meta=libcurl_meta.get(libcurl_version))

    fetch('https://raw.github.com/pypa/virtualenv/1.7/virtualenv.py', 'virtualenv-1.7.py')
    fetch('https://raw.github.com/pypa/virtualenv/1.9.1/virtualenv.py', 'virtualenv-1.9.1.py')

    if not os.path.exists('venv'):
        os.mkdir('venv')

    for python_version in python_versions:
        python_version_pieces = [int(piece) for piece in python_version.split('.')[:2]]
        for libcurl_version in libcurl_versions:
            python_prefix = os.path.abspath('i/Python-%s' % python_version)
            libcurl_prefix = os.path.abspath('i/curl-%s' % libcurl_version)
            venv = os.path.abspath('venv/Python-%s-curl-%s' % (python_version, libcurl_version))
            if os.path.exists(venv):
                shutil.rmtree(venv)
            fetch('https://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg')
            fetch('https://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg')
            # I had virtualenv 1.8.2 installed systemwide which
            # did not work with python 3.0:
            # http://stackoverflow.com/questions/1422361/why-am-i-getting-this-error-related-to-pip-and-easy-install-when-trying-to-set
            # so, use known versions everywhere
            # md5=89e68df89faf1966bcbd99a0033fbf8e
            fetch('https://pypi.python.org/packages/source/d/distribute/distribute-0.6.49.tar.gz')
            subprocess_check_call(['python', 'virtualenv-1.9.1.py', venv, '-p', '%s/bin/python%d.%d' % (python_prefix, python_version_pieces[0], python_version_pieces[1]), '--no-site-packages', '--never-download'])
            curl_config_path = os.path.join(libcurl_prefix, 'bin/curl-config')
            curl_lib_path = os.path.join(libcurl_prefix, 'lib')
            with in_dir('pycurl'):
                extra_patches = []
                extra_env = []
                deps_cmd = 'pip install -r requirements-dev.txt'
                extra_patches = ' && '.join(extra_patches)
                extra_env = ' '.join(extra_env)
                cmd = '''
                    make clean &&
                    . %(venv)s/bin/activate &&
                    %(deps_cmd)s && %(extra_patches)s
                    python -V &&
                    LD_LIBRARY_PATH=%(curl_lib_path)s PYCURL_CURL_CONFIG=%(curl_config_path)s %(extra_env)s make test
                ''' % dict(
                    venv=venv,
                    deps_cmd=deps_cmd,
                    extra_patches=extra_patches,
                    curl_lib_path=curl_lib_path,
                    curl_config_path=curl_config_path,
                    extra_env=extra_env
                )
                print(cmd)
                subprocess_check_call(cmd, shell=True)

if __name__ == '__main__':
    import sys

    def main():
        import optparse

        parser = optparse.OptionParser()
        parser.add_option('-p', '--python', help='Specify python version to test against')
        parser.add_option('-c', '--curl', help='Specify libcurl version to test against')
        options, args = parser.parse_args()
        if options.python:
            python_version = options.python
            if python_version in python_versions:
                chosen_python_versions = [python_version]
            else:
                chosen_python_versions = [v for v in python_versions if v.startswith(python_version)]
                if len(chosen_python_versions) != 1:
                    raise Exception('Bogus python version requested: %s' % python_version)
        else:
            chosen_python_versions = python_versions
        if options.curl:
            chosen_libcurl_versions = [options.curl]
        else:
            chosen_libcurl_versions = libcurl_versions
        run_matrix(chosen_python_versions, chosen_libcurl_versions)

    main()
