import uuid
from macholib.MachO import MachO
from macholib.mach_o import uuid_command


CPU_ARCH_MASK = 0xff000000
CPU_ARCH_ABI64 = 0x01000000
CPU_TYPE_ANY = -1
CPU_TYPE_VAX = 1
CPU_TYPE_MC680x0 = 6
CPU_TYPE_X86 = 7
CPU_TYPE_I386 = CPU_TYPE_X86
CPU_TYPE_X86_64 = CPU_TYPE_X86 | CPU_ARCH_ABI64
CPU_TYPE_MC98000 = 10
CPU_TYPE_HPPA = 11
CPU_TYPE_ARM = 12
CPU_TYPE_ARM64 = CPU_TYPE_ARM | CPU_ARCH_ABI64
CPU_TYPE_MC88000 = 13
CPU_TYPE_SPARC = 14
CPU_TYPE_I860 = 15
CPU_TYPE_POWERPC = 18
CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64
CPU_SUBTYPE_MASK = 0xff000000
CPU_SUBTYPE_LIB64 = 0x80000000
CPU_SUBTYPE_MULTIPLE = -1
CPU_SUBTYPE_LITTLE_ENDIAN = 0
CPU_SUBTYPE_BIG_ENDIAN = 1
CPU_THREADTYPE_NONE = 0
CPU_SUBTYPE_VAX_ALL = 0
CPU_SUBTYPE_VAX780 = 1
CPU_SUBTYPE_VAX785 = 2
CPU_SUBTYPE_VAX750 = 3
CPU_SUBTYPE_VAX730 = 4
CPU_SUBTYPE_UVAXI = 5
CPU_SUBTYPE_UVAXII = 6
CPU_SUBTYPE_VAX8200 = 7
CPU_SUBTYPE_VAX8500 = 8
CPU_SUBTYPE_VAX8600 = 9
CPU_SUBTYPE_VAX8650 = 10
CPU_SUBTYPE_VAX8800 = 11
CPU_SUBTYPE_UVAXIII = 12

CPU_SUBTYPE_MC680x0_ALL = 1
CPU_SUBTYPE_MC68030 = 1
CPU_SUBTYPE_MC68040 = 2
CPU_SUBTYPE_MC68030_ONLY = 3
CPU_SUBTYPE_INTEL = lambda f, m: (f) + ((m) << 4)
CPU_SUBTYPE_I386_ALL = CPU_SUBTYPE_INTEL(3, 0)
CPU_SUBTYPE_386 = CPU_SUBTYPE_INTEL(3, 0)
CPU_SUBTYPE_486 = CPU_SUBTYPE_INTEL(4, 0)
CPU_SUBTYPE_486SX = CPU_SUBTYPE_INTEL(4, 8)
CPU_SUBTYPE_586 = CPU_SUBTYPE_INTEL(5, 0)
CPU_SUBTYPE_PENT = CPU_SUBTYPE_INTEL(5, 0)
CPU_SUBTYPE_PENTPRO = CPU_SUBTYPE_INTEL(6, 1)
CPU_SUBTYPE_PENTII_M3 = CPU_SUBTYPE_INTEL(6, 3)
CPU_SUBTYPE_PENTII_M5 = CPU_SUBTYPE_INTEL(6, 5)
CPU_SUBTYPE_CELERON = CPU_SUBTYPE_INTEL(7, 6)
CPU_SUBTYPE_CELERON_MOBILE = CPU_SUBTYPE_INTEL(7, 7)
CPU_SUBTYPE_PENTIUM_3 = CPU_SUBTYPE_INTEL(8, 0)
CPU_SUBTYPE_PENTIUM_3_M = CPU_SUBTYPE_INTEL(8, 1)
CPU_SUBTYPE_PENTIUM_3_XEON = CPU_SUBTYPE_INTEL(8, 2)
CPU_SUBTYPE_PENTIUM_M = CPU_SUBTYPE_INTEL(9, 0)
CPU_SUBTYPE_PENTIUM_4 = CPU_SUBTYPE_INTEL(10, 0)
CPU_SUBTYPE_PENTIUM_4_M = CPU_SUBTYPE_INTEL(10, 1)
CPU_SUBTYPE_ITANIUM = CPU_SUBTYPE_INTEL(11, 0)
CPU_SUBTYPE_ITANIUM_2 = CPU_SUBTYPE_INTEL(11, 1)
CPU_SUBTYPE_XEON = CPU_SUBTYPE_INTEL(12, 0)
CPU_SUBTYPE_XEON_MP = CPU_SUBTYPE_INTEL(12, 1)
CPU_SUBTYPE_INTEL_FAMILY = lambda x: (x) & 15
CPU_SUBTYPE_INTEL_FAMILY_MAX = 15
CPU_SUBTYPE_INTEL_MODEL = lambda x: (x) >> 4
CPU_SUBTYPE_INTEL_MODEL_ALL = 0
CPU_SUBTYPE_X86_ALL = 3
CPU_SUBTYPE_X86_64_ALL = 3
CPU_SUBTYPE_X86_ARCH1 = 4
CPU_THREADTYPE_INTEL_HTT = 1
CPU_SUBTYPE_MIPS_ALL = 0
CPU_SUBTYPE_MIPS_R2300 = 1
CPU_SUBTYPE_MIPS_R2600 = 2
CPU_SUBTYPE_MIPS_R2800 = 3
CPU_SUBTYPE_MIPS_R2000a = 4
CPU_SUBTYPE_MIPS_R2000 = 5
CPU_SUBTYPE_MIPS_R3000a = 6
CPU_SUBTYPE_MIPS_R3000 = 7
CPU_SUBTYPE_MC98000_ALL = 0
CPU_SUBTYPE_MC98601 = 1
CPU_SUBTYPE_HPPA_ALL = 0
CPU_SUBTYPE_HPPA_7100 = 0
CPU_SUBTYPE_HPPA_7100LC = 1
CPU_SUBTYPE_POWERPC_ALL = 0
CPU_SUBTYPE_POWERPC64_ALL = 0
CPU_SUBTYPE_POWERPC_601 = 1
CPU_SUBTYPE_POWERPC_602 = 2
CPU_SUBTYPE_POWERPC_603 = 3
CPU_SUBTYPE_POWERPC_603e = 4
CPU_SUBTYPE_POWERPC_603ev = 5
CPU_SUBTYPE_POWERPC_604 = 6
CPU_SUBTYPE_POWERPC_604e = 7
CPU_SUBTYPE_POWERPC_620 = 8
CPU_SUBTYPE_POWERPC_750 = 9
CPU_SUBTYPE_POWERPC_7400 = 10
CPU_SUBTYPE_POWERPC_7450 = 11
CPU_SUBTYPE_POWERPC_970 = 100
CPU_SUBTYPE_ARM_ALL = 0
CPU_SUBTYPE_ARM_V4T = 5
CPU_SUBTYPE_ARM_V6 = 6
CPU_SUBTYPE_ARM_V5TEJ = 7
CPU_SUBTYPE_ARM_XSCALE = 8
CPU_SUBTYPE_ARM_V7 = 9
CPU_SUBTYPE_ARM_V7F = 10
CPU_SUBTYPE_ARM_V7S = 11
CPU_SUBTYPE_ARM_V7K = 12
CPU_SUBTYPE_ARM_V6M = 14
CPU_SUBTYPE_ARM_V7M = 15
CPU_SUBTYPE_ARM_V7EM = 16
CPU_SUBTYPE_ARM_V8 = 13
CPU_SUBTYPE_ARM64_ALL = 0
CPU_SUBTYPE_ARM64_V8 = 1
CPUFAMILY_UNKNOWN = 0
CPUFAMILY_POWERPC_G3 = 0xcee41549
CPUFAMILY_POWERPC_G4 = 0x77c184ae
CPUFAMILY_POWERPC_G5 = 0xed76d8aa
CPUFAMILY_INTEL_6_13 = 0xaa33392b
CPUFAMILY_INTEL_6_14 = 0x73d67300
CPUFAMILY_INTEL_6_15 = 0x426f69ef
CPUFAMILY_INTEL_6_23 = 0x78ea4fbc
CPUFAMILY_INTEL_6_26 = 0x6b5a4cd2
CPUFAMILY_ARM_9 = 0xe73283ae
CPUFAMILY_ARM_11 = 0x8ff620d8
CPUFAMILY_ARM_XSCALE = 0x53b005f5
CPUFAMILY_ARM_13 = 0x0cc90e64
CPUFAMILY_INTEL_YONAH = CPUFAMILY_INTEL_6_14
CPUFAMILY_INTEL_MEROM = CPUFAMILY_INTEL_6_15
CPUFAMILY_INTEL_PENRYN = CPUFAMILY_INTEL_6_23
CPUFAMILY_INTEL_NEHALEM = CPUFAMILY_INTEL_6_26
CPUFAMILY_INTEL_CORE = CPUFAMILY_INTEL_6_14
CPUFAMILY_INTEL_CORE2 = CPUFAMILY_INTEL_6_15


CPU_NAMES = {
    (CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL): 'x86_64',
    (CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL): 'i386',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_ALL): 'arm',
    (CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL): 'arm64',
    (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL): 'ppc',
    (CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC64_ALL): 'ppc64',
    (CPU_TYPE_MC680x0, CPU_SUBTYPE_MC680x0_ALL): 'm68k',
    (CPU_TYPE_HPPA, CPU_SUBTYPE_HPPA_ALL): 'hppa',
    (CPU_TYPE_I386, CPU_SUBTYPE_486): 'i486',
    (CPU_TYPE_I386, CPU_SUBTYPE_486SX): 'i486SX',
    (CPU_TYPE_I386, CPU_SUBTYPE_PENT): 'pentium',
    (CPU_TYPE_I386, CPU_SUBTYPE_586): 'i586',
    (CPU_TYPE_I386, CPU_SUBTYPE_PENTPRO): 'pentpro',
    (CPU_TYPE_I386, CPU_SUBTYPE_PENTPRO): 'i686',
    (CPU_TYPE_I386, CPU_SUBTYPE_PENTII_M3): 'pentIIm3',
    (CPU_TYPE_I386, CPU_SUBTYPE_PENTII_M5): 'pentIIm5',
    (CPU_TYPE_I386, CPU_SUBTYPE_PENTIUM_4): 'pentium4',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T): 'armv4t',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ): 'armv5',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_XSCALE): 'xscale',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6): 'armv6',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6M): 'armv6m',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7): 'armv7',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7F): 'armv7f',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S): 'armv7s',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K): 'armv7k',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7M): 'armv7m',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7EM): 'armv7em',
    (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V8): 'armv8',
    (CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8): 'arm64',
    (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_601): 'ppc601',
    (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603): 'ppc603',
    (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604): 'ppc604',
    (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604e): 'ppc604e',
    (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_750): 'ppc750',
    (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400): 'ppc7400',
    (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7450): 'ppc7450',
    (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_970): 'ppc970',
    (CPU_TYPE_MC680x0, CPU_SUBTYPE_MC68030_ONLY): 'm68030',
    (CPU_TYPE_MC680x0, CPU_SUBTYPE_MC68040): 'm68040',
    (CPU_TYPE_HPPA, CPU_SUBTYPE_HPPA_7100LC): 'hppa7100LC',
}

VALID_CPU_NAMES = frozenset(CPU_NAMES.values())


def get_cpu_name(type, subtype):
    """Get a known CPU name from the given maco cpu type and subtype.
    If this is not known `None` is returned.  This only covers things we
    actually expect in OS X/iOS projects.
    """
    return CPU_NAMES.get((type, subtype))


def is_valid_cpu_name(name):
    return name in VALID_CPU_NAMES


def get_macho_uuids(filename):
    """Given the filename to a mach-o file this returns the UUIDs for the
    binaries within it.
    """
    rv = []
    bin = MachO(filename)
    for header in bin.headers:
        cpu = get_cpu_name(header.header.cputype,
                           header.header.cpusubtype)
        if cpu is None:
            continue
        for cmd in header.commands:
            if type(cmd[1]) is uuid_command:
                rv.append((cpu, str(uuid.UUID(bytes=cmd[1].uuid))))
    return rv
