#!/usr/bin/python -tt
#
# Copyright (c) 2009, 2010, 2011 Intel, Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; version 2 of the License
#
# This program 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 General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., 59
# Temple Place - Suite 330, Boston, MA 02111-1307, USA.

from __future__ import with_statement
import os
import sys
import glob
import re
import shutil
import subprocess

from mic import bootstrap, msger
from mic.conf import configmgr
from mic.utils import errors, proxy
from mic.utils.fs_related import find_binary_path, makedirs
from mic.chroot import setup_chrootenv, cleanup_chrootenv

expath = lambda p: os.path.abspath(os.path.expanduser(p))

def bootstrap_mic(argv=None):


    def mychroot():
        os.chroot(rootdir)
        os.chdir(cwd)

    # by default, sys.argv is used to run mic in bootstrap
    if not argv:
        argv = sys.argv
    if argv[0] not in ('/usr/bin/mic', 'mic'):
        argv[0] = '/usr/bin/mic'

    cropts = configmgr.create
    bsopts = configmgr.bootstrap
    distro = bsopts['distro_name'].lower()

    rootdir = bsopts['rootdir']
    pkglist = bsopts['packages']
    cwd = os.getcwd()

    # create bootstrap and run mic in bootstrap
    bsenv = bootstrap.Bootstrap(rootdir, distro, cropts['arch'])
    bsenv.logfile = cropts['logfile']
    # rootdir is regenerated as a temp dir
    rootdir = bsenv.rootdir

    if 'optional' in bsopts:
        optlist = bsopts['optional']
    else:
        optlist = []

    try:
        msger.info("Creating %s bootstrap ..." % distro)
        bsenv.create(cropts['repomd'], pkglist, optlist)

        # bootstrap is relocated under "bootstrap"
        if os.path.exists(os.path.join(rootdir, "bootstrap")):
            rootdir = os.path.join(rootdir, "bootstrap")

        bsenv.dirsetup(rootdir)
        sync_mic(rootdir)

        #FIXME: sync the ks file to bootstrap
        if "/" == os.path.dirname(os.path.abspath(configmgr._ksconf)):
            safecopy(configmgr._ksconf, rootdir)

        msger.info("Start mic in bootstrap: %s\n" % rootdir)
        bindmounts = get_bindmounts(cropts)
        ret = bsenv.run(argv, cwd, rootdir, bindmounts)

    except errors.BootstrapError, err:
        msger.warning('\n%s' % err)
        if msger.ask("Switch to native mode and continue?"):
            return
        raise
    except RuntimeError, err:
        #change exception type but keep the trace back
        value, tb = sys.exc_info()[1:]
        raise errors.BootstrapError, value, tb
    else:
        sys.exit(ret)
    finally:
        bsenv.cleanup()

def get_bindmounts(cropts):
    binddirs =  [
                  os.getcwd(),
                  cropts['tmpdir'],
                  cropts['cachedir'],
                  cropts['outdir'],
                  cropts['local_pkgs_path'],
                ]
    bindfiles = [
                  cropts['logfile'],
                  configmgr._ksconf,
                ]

    for lrepo in cropts['localrepos']:
        binddirs.append(lrepo)

    bindlist = map(expath, filter(None, binddirs))
    bindlist += map(os.path.dirname, map(expath, filter(None, bindfiles)))
    bindlist = sorted(set(bindlist))
    bindmounts = ';'.join(bindlist)
    return bindmounts


def get_mic_binpath():
    fp = None
    try:
        import pkg_resources # depends on 'setuptools'
    except ImportError:
        pass
    else:
        dist = pkg_resources.get_distribution('mic')
        # the real script is under EGG_INFO/scripts
        if dist.has_metadata('scripts/mic'):
            fp = os.path.join(dist.egg_info, "scripts/mic")

    if fp:
        return fp

    # not found script if 'flat' egg installed
    try:
        return find_binary_path('mic')
    except errors.CreatorError:
        raise errors.BootstrapError("Can't find mic binary in host OS")


def get_mic_modpath():
    try:
        import mic
    except ImportError:
        raise errors.BootstrapError("Can't find mic module in host OS")
    path = os.path.abspath(mic.__file__)
    return os.path.dirname(path)

def get_mic_libpath():
    # TBD: so far mic lib path is hard coded
    return "/usr/lib/mic"

# the hard code path is prepared for bootstrap
def sync_mic(bootstrap, binpth = '/usr/bin/mic',
             libpth='/usr/lib',
             pylib = '/usr/lib/python2.7/site-packages',
             conf = '/etc/mic/mic.conf'):
    _path = lambda p: os.path.join(bootstrap, p.lstrip('/'))

    micpaths = {
                 'binpth': get_mic_binpath(),
                 'libpth': get_mic_libpath(),
                 'pylib': get_mic_modpath(),
                 'conf': '/etc/mic/mic.conf',
               }

    if not os.path.exists(_path(pylib)):
        pyptn = '/usr/lib/python?.?/site-packages'
        pylibs = glob.glob(_path(pyptn))
        if pylibs:
            pylib = pylibs[0].replace(bootstrap, '')
        else:
            raise errors.BootstrapError("Can't find python site dir in: %s" %
                                        bootstrap)

    for key, value in micpaths.items():
        try:
            safecopy(value, _path(eval(key)), False, ["*.pyc", "*.pyo"])
        except (OSError, IOError), err:
            raise errors.BootstrapError(err)

    # auto select backend
    conf_str = file(_path(conf)).read()
    conf_str = re.sub("pkgmgr\s*=\s*.*", "pkgmgr=auto", conf_str)
    with open(_path(conf), 'w') as wf:
        wf.write(conf_str)

    # chmod +x /usr/bin/mic
    os.chmod(_path(binpth), 0777)

    # correct python interpreter
    mic_cont = file(_path(binpth)).read()
    mic_cont = "#!/usr/bin/python\n" + mic_cont
    with open(_path(binpth), 'w') as wf:
        wf.write(mic_cont)


def safecopy(src, dst, symlinks=False, ignore_ptns=()):
    if os.path.isdir(src):
        if os.path.isdir(dst):
            dst = os.path.join(dst, os.path.basename(src))
        if os.path.exists(dst):
            shutil.rmtree(dst, ignore_errors=True)

        src = src.rstrip('/')
        # check common prefix to ignore copying itself
        if dst.startswith(src + '/'):
            ignore_ptns = list(ignore_ptns) + [ os.path.basename(src) ]

        ignores = shutil.ignore_patterns(*ignore_ptns)
        try:
            shutil.copytree(src, dst, symlinks, ignores)
        except (OSError, IOError):
            shutil.rmtree(dst, ignore_errors=True)
            raise
    else:
        if not os.path.isdir(dst):
            makedirs(os.path.dirname(dst))

        shutil.copy2(src, dst)