#!/usr/bin/python3
#Marco Mambelli - first version 10/8/2012 

import sys
import os
import platform
import optparse

from typing import Dict

SUPPORTED_OSX = ['10.13', '10.14', '10.15']

# Mapping for distro name in /etc/os-release to Bosco naming scheme
DISTRO_MAPPING = {'redhat': 'RH',
                  'centos': 'RH',
                  'debian': 'DEB',
                  'ubuntu': 'UBUNTU',
                  'linuxmint': 'UBUNTU'}

MINT_TO_UBUNTU_VERSION = {'18': '16',
                          '19': '18',
                          '20': '20'}

# download URLs for the different platforms
URL_DICT={
  "DEB9": "https://research.cs.wisc.edu/htcondor/tarball/9.0/current/condor-current-x86_64_Debian9-stripped.tar.gz",
  "DEB10": "https://research.cs.wisc.edu/htcondor/tarball/9.0/current/condor-current-x86_64_Debian10-stripped.tar.gz",
  "MAC": "https://research.cs.wisc.edu/htcondor/tarball/9.0/current/condor-current-x86_64_MacOSX-stripped.tar.gz",
  "RH7": "https://research.cs.wisc.edu/htcondor/tarball/9.0/current/condor-current-x86_64_CentOS7-stripped.tar.gz",
  "RH8": "https://research.cs.wisc.edu/htcondor/tarball/9.0/current/condor-current-x86_64_CentOS8-stripped.tar.gz",
  "UBUNTU18": "https://research.cs.wisc.edu/htcondor/tarball/9.0/current/condor-current-x86_64_Ubuntu18-stripped.tar.gz",
  "UBUNTU20": "https://research.cs.wisc.edu/htcondor/tarball/9.0/current/condor-current-x86_64_Ubuntu20-stripped.tar.gz",
}

BIT32="32bit"
BIT64="64bit"

INSTALLER_NAME="boscoinstaller"
SYSINSTALLER_NAME="boscomuinstaller"

def findversion_mac(detail=False):
  # system_profiler -detailLevel mini SPSoftwareDataType | grep "System Version"
  #      System Version: Mac OS X 10.6.8 (10K549)
  #
  import subprocess
  ec, out = subprocess.getstatusoutput('system_profiler -detailLevel mini SPSoftwareDataType | grep "System Version"')
  retv = out.strip()[len("System Version:"):].strip()
  if detail and ec==0:
    return retv
  if retv.startswith('Mac OS X 10.'):
    version = [i.strip() for i in retv.strip()[len("Mac OS X "):].split('.')]
    if '.'.join(version[:2]) in SUPPORTED_OSX:
      return "MAC"
    return "UNSUPPORTED"
  return "UNSUPPORTED"


def get_distro_info() -> Dict[str, str]:
    """
    Return a dict containing the contents of /etc/os-release

    NAME="Ubuntu"
    VERSION="20.04.1 LTS (Focal Fossa)"
    ID=ubuntu
    ID_LIKE=debian
    PRETTY_NAME="Ubuntu 20.04.1 LTS"
    VERSION_ID="20.04"
    HOME_URL="https://www.ubuntu.com/"
    SUPPORT_URL="https://help.ubuntu.com/"
    BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
    PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
    VERSION_CODENAME=focal
    UBUNTU_CODENAME=focal
    """
    with open('/etc/os-release') as os_file:
        os_release = os_file.readlines()

    os_info = dict()
    for line in os_release:
        key, val = line.split('=', maxsplit=1)
        os_info[key] = val.replace('"', '').strip()

    return os_info


def findversion() -> str:
  if not os.name=='posix':
    return "UNSUPPORTED"
  if sys.platform=='darwin':
    myver = platform.mac_ver()
    if myver[0]:
      if '.'.join(myver[0].split('.')[:2]) in SUPPORTED_OSX:
        return "MAC"
    return findversion_mac()
  elif sys.platform.startswith('linux'):
    # only 64 bit supported
    if not platform.architecture()[0]=='64bit':
      return "UNSUPPORTED"

    try:
      distro_info = get_distro_info()
    except FileNotFoundError:
      return "UNKNOWN"

    distro = distro_info['ID']
    try:
        bosco_distro = DISTRO_MAPPING[distro]
    except KeyError:
        return "UNKNOWN"
    major_ver = distro_info['VERSION_ID'].split('.')[0]

    if distro == 'linuxmint':
      try:
        major_ver = MINT_TO_UBUNTU_VERSION[major_ver]
      except KeyError:
        return "UNKNOWN"

    return f'{bosco_distro}{major_ver}'

  return "UNKNOWN"

def findversion_detail():
  if sys.platform=='darwin':
    return findversion_mac(True)
  elif sys.platform.startswith('linux'):
    if os.path.isfile('/etc/redhat-release'):
      try:
        return get_distro_info()["PRETTY_NAME"]
      except (KeyError, FileNotFoundError):
        pass
  return "UNKNOWN"

def install(platform, multiuser=False, options=None):
  """Install BOSCO for the platform"""
  import tempfile
  import urllib.request, urllib.error, urllib.parse
  import shutil
  import tarfile
  import subprocess
  # make tmp dir
  if not platform in sorted(URL_DICT.keys()):
      print("Your system is not supported. Aborting the installation.")
      sys.exit(1)
  try:
    tmp_dir = tempfile.mkdtemp() # create dir
    # download bosco and save directly to file
    condor_dir = ""
    try:
      print("Downloading BOSCO from %s" % URL_DICT[platform])
      response = urllib.request.urlopen(URL_DICT[platform])
      fname = os.path.join(tmp_dir, 'bosco-download.tar.gz')
      f = open(fname, 'wb')
      shutil.copyfileobj(response, f)
      f.close()
      response.close() 
      tar = tarfile.open(fname, "r")
      for tarname in tar.getnames():
        tar.extract(tarname, tmp_dir)
      tar.close()
      condor_dir_list = [i for i in os.listdir(tmp_dir) if i.startswith('condor')]
    except:
      (etype, evalue, etraceback) = sys.exc_info()
      if evalue:
        print(evalue)
      condor_dir_list = []
    if not condor_dir_list:
      print("The download and extraction of BOSCO failed. Aborting the installation.")
      sys.exit(1)
    # run bosco_install (pass parameters)
    options_list = ""
    if options:
      options_list =  " ".join(options)
    if not options_list:
      if multiuser:
        options_list = "--prefix=/opt/bosco"
        print("Installing BOSCO in /opt/bosco")
      else:
        print("Installing BOSCO in ~/bosco")
    else:
      print("Installing BOSCO with: ./bosco_install %s" % options_list)
    cmd_list = [#"curl -s -o %s/bosco-download.tar.gz %s" % (tmp_dir, URL_DICT[platform]),
                #"tar xzf %s/bosco-download.tar.gz -C %s" % (tmp_dir, tmp_dir),
                "cd %s/%s; ./bosco_install %s" % (tmp_dir, condor_dir_list[0], options_list)
               ]
    for cmd in cmd_list:
      ec, out = subprocess.getstatusoutput(cmd)
      if ec != 0:
        print("Error installing BOSCO")
        print("Command %s failed:\n%s" % (cmd, out))
        print("Aborting BOSCO installation")
        sys.exit(1)
      else:
        print(out)
  finally:
    try:
      shutil.rmtree(tmp_dir) # delete directory
    except OSError as e:
      if e.errno != 2: # code 2 - no such file or directory
        raise
  # rm itself and exit or not?  
  print("Congratulations, you installed BOSCO succesfully!")
  return
   
if __name__ == "__main__":
  # invoked as installer (INSTALLER_NAME [install options])
  tmp_basename = os.path.basename(sys.argv[0])
  if tmp_basename==INSTALLER_NAME or tmp_basename==SYSINSTALLER_NAME:
    res = findversion()
    if tmp_basename==SYSINSTALLER_NAME:
      install(res, True, sys.argv[1:])
    else:
      install(res, False, sys.argv[1:])
    sys.exit(0)
  # install parameter separation
  my_par = sys.argv[1:]
  install_par = None 
  for i in range(1, len(sys.argv)):
    if sys.argv[i]=='--':
      my_par = sys.argv[1:i]
      install_par = sys.argv[i+1:]
  usage = '\n'.join(["Usage: %prog [options]",
                     "       %prog --install [-- install options]",
                     "       %s [install options]" % INSTALLER_NAME
                    ]) 
  parser = optparse.OptionParser(usage=usage)
  parser.add_option('-u', '--url', help='return the download URL for the current platform', dest='url',
                    default=False, action='store_true')
  parser.add_option('-b', '--bit', help='return if the architecture is 32bit or 64bit', dest='bit',
                    default=False, action='store_true')
  parser.add_option('-f', '--full', help='return the full version string form the OS', dest='details',
                    default=False, action='store_true')
  parser.add_option('--force', help='force a specific platform %s' % sorted(URL_DICT.keys()), dest='force',
                    )
  parser.add_option('-i', '--install', help='basic bosco installation for the current platform', dest='install',
                    default=False, action='store_true')
  (opts, args) = parser.parse_args(my_par)
  # options consistency
  if opts.force and not opts.force in sorted(URL_DICT.keys()):
    print("The platform %s is not supported.\n" % opts.force)
    parser.print_help()
    sys.exit(-1)
  if opts.details and opts.bit:
    print("Option full and bit are mutually exclusive\n")
    parser.print_help()
    sys.exit(-1)
  # run
  if opts.bit:
    if platform.architecture()[0]=='64bit':
      print(BIT64)
    else:
      print(BIT32)
    sys.exit(0)
  elif opts.details:
    print(findversion_detail())
  else:
    if opts.force:
      res = opts.force
    else:
      res = findversion()
    if opts.install:
      install(res, False, install_par)
      sys.exit(0)
    elif opts.url:
      print(URL_DICT[res])
    else:
      print(res)
