from pkg_resources import require
require("altgraph")

from altgraph.compat import *
from modulegraph.util import *
import os
import sys
import stat
import operator
import struct
import shutil
from macholib.mach_o import MH_MAGIC, FAT_MAGIC, MH_CIGAM, MH_MAGIC_64, MH_CIGAM_64
MAGIC = [struct.pack('!L', _) for _ in [MH_MAGIC, FAT_MAGIC, MH_CIGAM, MH_MAGIC_64, MH_CIGAM_64]]
MAGIC_LEN = 4
STRIPCMD = ['/usr/bin/strip', '-x', '-S', '-']

def fsencoding(s, encoding=sys.getfilesystemencoding()):
    if isinstance(s, unicode):
        s = s.encode(encoding)
    return s

def move(src, dst):
    shutil.move(fsencoding(src), fsencoding(dst))

def copy2(src, dst):
    shutil.copy2(fsencoding(src), fsencoding(dst))

def flipwritable(fn, mode=None):
    if os.access(fn, os.W_OK):
        return None
    old_mode = os.stat(fn).st_mode
    os.chmod(fn, stat.S_IWRITE | old_mode)
    return old_mode

class writablefile(file):
    def __init__(self, fn, *args, **kwargs):
        self._old_mode = flipwritable(fn)
        file.__init__(self, fn, *args, **kwargs)

    def close(self):
        file.close(self)
        flipwritable(self.name, self._old_mode)

def mergecopy(src, dest):
    if os.path.exists(dest) and os.stat(dest).st_mtime >= os.stat(src).st_mtime:
        return
    copy2(src, dest)

def mergetree(src, dst, condition=None, copyfn=mergecopy):
    """Recursively merge a directory tree using mergecopy()."""
    # XXX - symlinks
    src = fsencoding(src)
    dst = fsencoding(dst)
    names = os.listdir(src)
    try:
        os.makedirs(dst)
    except OSError:
        pass
    errors = []
    for name in names:
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        if condition is not None and not condition(srcname):
            continue
        try:
            if os.path.isdir(srcname):
                mergetree(srcname, dstname, condition=condition)
            else:
                copyfn(srcname, dstname)
        except (IOError, os.error), why:
            errors.append((srcname, dstname, why))
    if errors:
        raise IOError, errors

def sdk_normalize(filename):
    if filename.startswith('/Developer/SDKs/'):
        pathcomp = filename.split('/')
        del pathcomp[1:4]
        filename = '/'.join(pathcomp)
    return filename

def in_system_path(filename):
    """
    Return True if the file is in a system path
    """
    fn = sdk_normalize(os.path.realpath(filename))
    if fn.startswith('/usr/local/'):
        return False
    elif fn.startswith('/System/') or fn.startswith('/usr/'):
        return True
    else:
        return False

def has_filename_filter(module):
    """
    Return False if the module does not have a filename attribute
    """
    return getattr(module, 'filename', None) is not None

def get_magic():
    return MAGIC

def is_platform_file(path):
    if not os.path.exists(path) or os.path.islink(path):
        return False
    bytes = file(path).read(MAGIC_LEN)
    for magic in MAGIC:
        if bytes == magic:
            return True
    return False

def iter_platform_files(dst):
    for root, dirs, files in os.walk(dst):
        for fn in files:
            fn = os.path.join(root, fn)
            if is_platform_file(fn):
                yield fn

def strip_files(files, argv_max=(256 * 1024)):
    tostrip = [(fn, flipwritable(fn)) for fn in files]
    while tostrip:
        cmd = list(STRIPCMD)
        flips = []
        pathlen = reduce(operator.add, [len(s)+1 for s in cmd])
        while pathlen < argv_max:
            if not tostrip:
                break
            added, flip = tostrip.pop()
            pathlen += len(added) + 1
            cmd.append(added)
            flips.append((added, flip))
        else:
            cmd.pop()
            tostrip.append(flips.pop())
        os.spawnv(os.P_WAIT, cmd[0], cmd)
        for args in flips:
            flipwritable(*args)
