#!/usr/bin/env python2

# This script downloads the latest version of Compass and installs it on
# non-windows platforms.

import json
import os
import os.path as path
import shutil
import subprocess
import sys
import tempfile
import platform
import time
import urllib


def get_pkg_format():
    """Determine the package manager for this Linux distro."""
    with open(os.devnull, 'w') as fnull:
        try:
            subprocess.call(['apt-get', '--help'], stdout=fnull, stderr=fnull)
            return 'apt'
        except:
            pass

        try:
            subprocess.call(['dnf', '--help'], stdout=fnull, stderr=fnull)
            return 'dnf'
        except:
            pass

        try:
            subprocess.call(['yum', '--help'], stdout=fnull, stderr=fnull)
            return 'yum'
        except:
            pass

    return ''


def download_progress(count, block_size, total_size):
    """Log the download progress."""
    percent = int(count * block_size * 100 / total_size)
    sys.stdout.write("\rDownloading Compass... %d%%" % percent)
    sys.stdout.flush()


def download_pkg(link, pkg_format=''):
    """Download the package from link, logging progress. Returns the filename."""
    suf = ''
    if pkg_format == 'apt':
        suf = '.deb'
    elif pkg_format == 'yum' or pkg_format == 'dnf':
        suf = '.rpm'

    tmpf = tempfile.mkstemp(suffix=suf)
    res = urllib.urlretrieve(link, filename=tmpf[1], reporthook=download_progress)
    # Download progress doesn't end with a newline so add it here.
    print ''
    return res[0]


def install_mac(dmg):
    """Use CLI tools to mount the dmg and extract all .apps into Applications."""
    tmp = tempfile.mkdtemp()
    with open(os.devnull, 'w') as fnull:
        subprocess.check_call(['hdiutil', 'attach', '-nobrowse', '-noautoopen',
                               '-mountpoint', tmp, dmg], stdout=fnull, stderr=fnull)
        try:
            apps = [f for f in os.listdir(tmp) if f.endswith('.app')]
            for app in apps:
                if path.isdir('/Applications/' + app):
                    print 'Old version found removing...'
                    shutil.rmtree('/Applications/' + app)
                print 'Copying %s to /Applications' % app
                shutil.copytree(path.join(tmp, app), '/Applications/' + app)
        # We don't really care about what errors come up here. Just log the failure
        # and use the finally to make sure we always unmount the dmg.
        except Exception as exception:
            print exception
        finally:
            subprocess.check_call(['hdiutil', 'detach', tmp], stdout=fnull, stderr=fnull)

        if path.isdir('/Applications/MongoDB Compass.app'):
            subprocess.check_call(['open', '/Applications/MongoDB Compass.app'])
            return
        if path.isdir('/Applications/MongoDB Compass Community.app'):
            subprocess.check_call(['open', '/Applications/MongoDB Compass Community.app'])
            return


def install_linux(pkg_format, pkg_file):
    """Use the package manager indicated by pkg_format to install pkg_file."""
    if pkg_format == 'yum':
        install = ['yum', 'localinstall', '--assumeyes', pkg_file]
    elif pkg_format == 'apt':
        # dpkg returns an error code when it fails to install dependencies
        # so just run it and let apt-get tell us if something went wrong
        subprocess.call(['dpkg', '--install', pkg_file])
        install = ['apt-get', 'install', '-f', '--yes']
    elif pkg_format == 'dnf':
        install = ['dnf', 'install', '--assumeyes', pkg_file]
    else:
        print 'No available installation methods.'
        sys.exit(1)

    subprocess.check_call(install)


def is_supported_distro():
    distro_name, version_number, extra = platform.linux_distribution()

    if (distro_name == 'Ubuntu' and
        (version_number == '14.04' or
         version_number == '16.04')):
        return True

    if (distro_name == 'Red Hat Enterprise Linux Server' and
        (float(version_number) >= 7.0)):
        return True

    return False


def download_and_install_compass():
    """Download and install compass for this platform."""
    os_type = sys.platform
    pkg_format = get_pkg_format()

    # Sometimes sys.platform gives us 'linux2' and we only want 'linux'
    if os_type.startswith('linux'):
        os_type = 'linux'
        if pkg_format == 'apt':
            os_type += '_deb'
        elif pkg_format == 'yum' or pkg_format == 'dnf':
            os_type += '_rpm'
    elif os_type == 'darwin':
        os_type = 'osx'

    if os_type.startswith('linux') and os.getuid() != 0:
        print 'You must run this script as root.'
        sys.exit(1)

    if os_type.startswith('linux') and not is_supported_distro():
        print 'You are using an unsupported Linux distribution.\n' \
        'Please visit: https://compass.mongodb.com/community-supported-platforms' \
        ' to view available community supported packages.'
        sys.exit(1)

    if platform.machine() != 'x86_64':
        print 'Sorry, MongoDB Compass is only supported on 64 bit platforms.' \
        ' If you believe you\'re seeing this message in error please open a' \
        ' ticket on the SERVER project at https://jira.mongodb.org/'

    link = 'https://compass.mongodb.com/api/v2/download/latest/compass-community/stable/' + os_type
    pkg = download_pkg(link, pkg_format=pkg_format)

    print 'Installing the package...'
    if os_type == 'osx':
        install_mac(pkg)
    elif os_type.startswith('linux'):
        install_linux(pkg_format, pkg)
    else:
        print 'Unrecognized os_type: %s' % os_type

    print 'Cleaning up...'
    os.remove(pkg)
    print 'Done!'


if __name__ == '__main__':
    download_and_install_compass()
