#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function

import os
import sys
import re
import stat
import optparse
import shutil
import imp
import codecs

try:
    from cStringIO import StringIO
except ImportError:
    from io import StringIO
from glob import glob
import atexit

# We have some warnings because we reimport some libs. We don't want them to be shown at install
import warnings

PY3 = sys.version_info >= (3,)
if PY3:
    basestring = str  # no basestring in python 3


def _disable_warns(*args, **kwargs):
    pass


warnings.showwarning = _disable_warns

# will fail under 2.5 python version, but if really you have such a version in
# prod you are a morron and we can't help you
python_version = sys.version_info
if python_version < (2, 6):
    sys.exit("OpsBro require as a minimum Python 2.6, sorry")
# elif python_version >= (3,):
#    sys.exit("OpsBro is not yet compatible with Python 3.x, sorry")

package_data = ['*.py']

# Is this setup.py call for a pypi interaction? if true, won't hook lot of things
is_pypi_register_upload = ('register' in sys.argv or ('sdist' in sys.argv and 'upload' in sys.argv))
if is_pypi_register_upload:
    print("Pypi specal mode activated, skipping some black magic")
    if '-v' not in sys.argv:
        sys.argv.append('-v')

# Is it a first step installation for pip? (egg_info stuff)
is_pip_first_step = 'egg_info' in sys.argv
# Last step for pip insta an install one (at least in pip 9.0.1)
is_pip_real_install_step = 'bdist_wheel' in sys.argv

# Black magic install:
# * copy /etc
# * look for dependencies from system packages
# * hide setup.py part
# If not black kmagic (like in pip first step, or pypi interaction (upload, etc)
# we do not want any black magic thing, and we try to behave like a standard python package ^^
# By default we love black magic, but if we are in a pip special call or pypi, we disable it
allow_black_magic = not is_pypi_register_upload and not is_pip_first_step

# We will need to allow a debug of the orig_sys_argv
orig_sys_argv = sys.argv[:]


##################################       Utility functions for files
# helper function to read the README file
def read(fname):
    return codecs.open(os.path.join(os.path.dirname(__file__), fname), 'r', 'utf8').read()


# Do a chmod -R +x
def _chmodplusx(d):
    if not os.path.exists(d):
        return
    if os.path.isdir(d):
        for item in os.listdir(d):
            p = os.path.join(d, item)
            if os.path.isdir(p):
                _chmodplusx(p)
            else:
                st = os.stat(p)
                os.chmod(p, st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
    else:
        st = os.stat(d)
        os.chmod(d, st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)


##################################       Hook the ugly python setup() call that can output gcc warnings.
# NOTE: yes, there is os.dup and file descriptor things. Deal with it (r).
# Keep a trace of the old stdout, because setup() is just toooooooooo verbose when succeed
stdout_orig = sys.stdout
stderr_orig = sys.stderr
stdout_catched = StringIO()
stderr_redirect_path = '/tmp/stderr.opsbro.tmp' if os.name != 'nt' else r'c:\stderr.opsbro.tmp'
stderr_redirect = None
stderr_orig_bkp = None


def hook_stdout():
    global stderr_redirect
    # Do not hook if we are uploading to pypi
    if not allow_black_magic:
        return
    sys.stdout = stdout_catched
    sys.stderr = stdout_catched
    # Also hook raw stderr
    stderr_redirect = open(stderr_redirect_path, 'w')
    os.dup2(stderr_redirect.fileno(), 2)


# Unhook stdout, put back fd=0
def unhook_stdout():
    global stderr_redirect
    # For pypi, we did not hook it
    if not allow_black_magic:
        return
    # If we have something in the file descriptor 2, reinject into stderr
    stderr_redirect.close()
    # with open(stderr_redirect_path, 'r') as f:
    #    stdout_catched.write(f.read())
    sys.stdout = stdout_orig
    sys.stderr = stderr_orig


##################################       Parse arguments, especially for pip install arg catched
parser = optparse.OptionParser("%prog [options]", version="%prog ")
parser.add_option('--root', dest="proot", metavar="ROOT", help='Root dir to install, usefull only for packagers')
parser.add_option('--upgrade', '--update', dest="upgrade", action='store_true', help='Only upgrade')
parser.add_option('--install-scripts', dest="install_scripts", help='Path to install the opsbro binary')
parser.add_option('--skip-build', dest="skip_build", action='store_true', help='skipping build')
parser.add_option('-O', type="int", dest="optimize", help='skipping build')
parser.add_option('--record', dest="record", help='File to save writing files. Used by pip install only')
parser.add_option('--single-version-externally-managed', dest="single_version", action='store_true', help='This option is for pip only')

old_error = parser.error


def _error(msg):
    pass


parser.error = _error
opts, args = parser.parse_args()
# reenable the errors for later use
parser.error = old_error

root = opts.proot or ''

##################################       Detect install or Update
prev_version = None
prev_path = ''
# We try to see if we are in a full install or an update process
is_update = False
# Try to import opsbro but not the local one. If available, we are in 
# and upgrade phase, not a classic install
try:
    if '.' in sys.path:
        sys.path.remove('.')
    if os.path.abspath('.') in sys.path:
        sys.path.remove(os.path.abspath('.'))
    if '' in sys.path:
        sys.path.remove('')
    import opsbro as opsbro_test_import
    
    is_update = True
    # Try to guess version
    from opsbro.info import VERSION as prev_version
    
    prev_path = os.path.dirname(opsbro_test_import.__file__)
    del opsbro_test_import
    # But to be sure future opsbro import will load the new one, we need to
    # first hard unload the opsbro modules from python
    # NOTE: this is only ok because we are in the setup.py, don't do this outside this scope!
    all_modules = list(sys.modules.keys())
    for modname in all_modules:
        if modname == 'opsbro' or modname.startswith('opsbro.'):
            del sys.modules[modname]
except ImportError as exp:  # great, first install so
    pass

# Now look at loading the local opsbro lib for version and banner
my_dir = os.path.dirname(os.path.abspath(__file__))
opsbro = imp.load_module('opsbro', *imp.find_module('opsbro', [os.path.realpath(my_dir)]))
from opsbro.info import VERSION, BANNER, TXT_BANNER
from opsbro.log import cprint, is_tty, sprintf, core_logger
from opsbro.misc.bro_quotes import get_quote
from opsbro.systempacketmanager import get_systepacketmgr
from opsbro.cli_display import print_h1
from opsbro.characters import CHARACTERS

systepacketmgr = get_systepacketmgr()

##################################       Only root as it's a global system tool.
if os.name != 'nt' and os.getuid() != 0:
    cprint('Setup must be launched as root.', color='red')
    sys.exit(2)

# By default logger should not print anything
core_logger.setLevel('ERROR')
# By maybe we are in verbose more?
if '-v' in sys.argv or os.environ.get('DEBUG_INSTALL', '0') == '1':
    core_logger.setLevel('DEBUG')

core_logger.debug('SCRIPT: install/update script was call with arguments: %s' % orig_sys_argv)

what = 'Installing' if not is_update else 'Updating'
title = sprintf('%s' % what, color='magenta', end='') + sprintf(' OpsBro to version ', end='') + sprintf('%s' % VERSION, color='magenta', end='')

if allow_black_magic:
    print_h1(title, raw_title=False)

##################################       Start to print to the user
if allow_black_magic:
    # If we have a real tty, we can print the delicious banner with lot of BRO
    if is_tty():
        cprint(BANNER)
    else:  # ok you are poor, just got some ascii art then
        cprint(TXT_BANNER)
    
    # Also print a Bro quote
    quote, from_film = get_quote()
    cprint('  >> %s  (%s)\n' % (quote, from_film), color='grey')
if allow_black_magic:
    if is_update:
        cprint('  Previous OpsBro lib detected on this system:')
        cprint('    * location: ', end='')
        cprint(prev_path, color='blue')
        cprint('    * version : ', end='')
        cprint('%s' % prev_version, color='blue')
        cprint('    * Using the ', end='')
        cprint('update process', color='magenta')
    
    print('')

if '--update' in args or opts.upgrade or '--upgrade' in args:
    if 'update' in args:
        sys.argv.remove('update')
        sys.argv.insert(1, 'install')
    if '--update' in args:
        sys.argv.remove('--update')
    if '--upgrade' in args:
        sys.argv.remove('--upgrade')
    is_update = True

# install: if we are with setupy.py install, or maybe with pip launch (last step)
is_install = False
if not is_update and 'install' in args or is_pip_real_install_step:
    is_install = True

install_scripts = opts.install_scripts or ''

# setup() will warn about unknown parameter we already managed
# to delete them
deleting_args = ['--skip-build']
to_del = []
for a in deleting_args:
    for av in sys.argv:
        if av.startswith(a):
            idx = sys.argv.index(av)
            to_del.append(idx)
            if '=' not in av: