package freenet.support.CPUInformation;

/**
 *  Moved out of CPUID.java
 *
 *  Ref: https://software.intel.com/en-us/articles/intel-architecture-and-processor-identification-with-cpuid-model-and-family-numbers
 *  Ref: http://en.wikipedia.org/wiki/List_of_Intel_CPU_microarchitectures
 *
 *  @since 0.8.7
 */
class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
{
    private static boolean isPentiumCompatible;
    private static boolean isPentiumMMXCompatible;
    private static boolean isPentium2Compatible;
    private static boolean isPentium3Compatible;
    private static boolean isPentium4Compatible;
    private static boolean isPentiumMCompatible;
    private static boolean isAtomCompatible;
    private static boolean isCore2Compatible;
    private static boolean isCoreiCompatible;
    private static boolean isSandyCompatible;
    private static boolean isIvyCompatible;
    private static boolean isHaswellCompatible;
    private static boolean isBroadwellCompatible;
    
    // If modelString != null, the cpu is considered correctly identified.
    private static final String smodel = identifyCPU();
    public boolean IsPentiumCompatible(){ return isPentiumCompatible; }
    public boolean IsPentiumMMXCompatible(){ return isPentiumMMXCompatible; }
    public boolean IsPentium2Compatible(){ return isPentium2Compatible; }
    public boolean IsPentium3Compatible(){ return isPentium3Compatible; }
    public boolean IsPentium4Compatible(){ return isPentium4Compatible; }
    public boolean IsPentiumMCompatible(){ return isPentiumMCompatible; }
    public boolean IsAtomCompatible(){ return isAtomCompatible; }
    public boolean IsCore2Compatible(){ return isCore2Compatible; }
    public boolean IsCoreiCompatible(){ return isCoreiCompatible; }
    public boolean IsSandyCompatible(){ return isSandyCompatible; }
    public boolean IsIvyCompatible(){ return isIvyCompatible; }
    public boolean IsHaswellCompatible(){ return isHaswellCompatible; }
    public boolean IsBroadwellCompatible(){ return isBroadwellCompatible; }

    public String getCPUModelString() throws UnknownCPUException
    {
        if (smodel != null)
            return smodel;
        throw new UnknownCPUException("Unknown Intel CPU; Family="+CPUID.getCPUFamily() + '/' + CPUID.getCPUExtendedFamily()+
                                      ", Model="+CPUID.getCPUModel() + '/' + CPUID.getCPUExtendedModel());    	// 

    }
    
    private static String identifyCPU()
    {
        // http://en.wikipedia.org/wiki/Cpuid
        // http://web.archive.org/web/20110307080258/http://www.intel.com/Assets/PDF/appnote/241618.pdf
        // http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf
    	// #include "llvm/Support/Host.h", http://llvm.org/docs/doxygen/html/Host_8cpp_source.html
        String modelString = null;
        int family = CPUID.getCPUFamily();
        int model = CPUID.getCPUModel();
        if (family == 15 || family == 6) {
            // Intel uses extended model for family = 15 or family = 6,
            // which is not what wikipedia says
            model += CPUID.getCPUExtendedModel() << 4;
        }
        if (family == 15) {
            family += CPUID.getCPUExtendedFamily();
        }

        switch (family) {
            case 4: {
                switch (model) {
                    case 0:
                        modelString = "486 DX-25/33";
                        break;
                    case 1:
                        modelString = "486 DX-50";
                        break;
                    case 2:
                        modelString = "486 SX";
                        break;
                    case 3:
                        modelString = "486 DX/2";
                        break;
                    case 4:
                        modelString = "486 SL";
                        break;
                    case 5:
                        modelString = "486 SX/2";
                        break;
                    case 7:
                        modelString = "486 DX/2-WB";
                        break;
                    case 8:
                        modelString = "486 DX/4";
                        break;
                    case 9:
                        modelString = "486 DX/4-WB";
                        break;
                    default:
                        modelString = "Intel 486/586 model " + model;
                        break;
                }
            }
            break;

            // P5
            case 5: {
                isPentiumCompatible = true;
                switch (model) {
                    case 0:
                        modelString = "Pentium 60/66 A-step";
                        break;
                    case 1:
                        modelString = "Pentium 60/66";
                        break;
                    case 2:
                        modelString = "Pentium 75 - 200";
                        break;
                    case 3:
                        modelString = "OverDrive PODP5V83";
                        break;
                    case 4:
                        isPentiumMMXCompatible = true;
                        modelString = "Pentium MMX";
                        break;
                    case 7:
                        modelString = "Mobile Pentium 75 - 200";
                        break;
                    case 8:
                        isPentiumMMXCompatible = true;
                        modelString = "Mobile Pentium MMX";
                        break;
                    default:
                        modelString = "Intel Pentium model " + model;
                        break;
                }
            }
            break;

            // P6
            case 6: {
                isPentiumCompatible = true;
                isPentiumMMXCompatible = true;
                int extmodel = model >> 4;
                if (extmodel >= 1) {
                    isPentium2Compatible = true;
                    isPentium3Compatible = true;
                    isPentium4Compatible = true;
                    isPentiumMCompatible = true;
                    isCore2Compatible = true;
                    if (extmodel >= 2)
                        isCoreiCompatible = true;
                }
                switch (model) {
                    case 0:
                        modelString = "Pentium Pro A-step";
                        break;
                    case 1:
                        modelString = "Pentium Pro";
                        break;
                    // Spoofed Nehalem by qemu-kvm
                    // Not in any CPUID charts
                    // KVM bug?
                    // # cat /usr/share/kvm/cpus-x86_64.conf | grep 'name = "Nehalem"' -B 1 -A 12
                    // [cpudef]
                    //    name = "Nehalem"
                    //    level = "2"
                    //    vendor = "GenuineIntel"
                    //    family = "6"
                    //    model = "2"
                    //    stepping = "3"
                    //    feature_edx = "sse2 sse fxsr mmx clflush pse36 pat cmov mca pge mtrr sep apic cx8 mce pae msr tsc pse de fpu"
                    //    feature_ecx = "popcnt sse4.2 sse4.1 cx16 ssse3 sse3"
                    //    extfeature_edx = "i64 syscall xd"
                    //    extfeature_ecx = "lahf_lm"
                    //    xlevel = "0x8000000A"
                    //    model_id = "Intel Core i7 9xx (Nehalem Class Core i7)"
                    //case 2:
                        // ...
                    case 3:
                        isPentium2Compatible = true;
                        modelString = "Pentium II (Klamath)";
                        break;
                    case 5:
                        isPentium2Compatible = true;
                        modelString = "Pentium II (Deschutes), Celeron (Covington), Mobile Pentium II (Dixon)";
                        break;
                    case 6:
                        isPentium2Compatible = true;
                        modelString = "Mobile Pentium II, Celeron (Mendocino)";
                        break;
                    case 7:
                        isPentium2Compatible = true;
                        isPentium3Compatible = true;
                        modelString = "Pentium III (Katmai)";
                        break;
                    case 8:
                        isPentium2Compatible = true;
                        isPentium3Compatible = true;
                        modelString = "Pentium III (Coppermine), Celeron w/SSE";
                        break;
                    case 9:
                        isPentium2Compatible = true;
                        isPentium3Compatible = true;
                        isPentiumMCompatible = true;
                        modelString = "Pentium M (Banias)";
                        break;
                    case 10:
                        isPentium2Compatible = true;
                        isPentium3Compatible = true;
                        modelString = "Pentium III Xeon (Cascades)";
                        break;
                    case 11:
                        isPentium2Compatible = true;
                        isPentium3Compatible = true;
                        modelString = "Pentium III (130 nm)";
                        break;
                    case 13:
                        isPentium2Compatible = true;
                        isPentium3Compatible = true;
                        isPentiumMCompatible = true;
                        modelString = "Core (Yonah)";
                        break;
                    case 14:
                    case 15:
                        isPentium2Compatible = true;
                        isPentium3Compatible = true;
                        isPentiumMCompatible = true;
                        isCore2Compatible = true;
                        modelString = "Penryn";
                        break;

                // following are for extended model == 1
                // most flags are set above

                    // Celeron 65 nm
                    case 0x16:
                        modelString = "Merom";
                        break;
                    // Penryn 45 nm
                    case 0x17:
                        modelString = "Penryn";
                        break;
                    // Nehalem 45 nm
                    case 0x1a:
                        isCoreiCompatible = true;
                        modelString = "Nehalem";
                        break;
                    // Atom Pineview / Silverthorne 45 nm
                    case 0x1c:
                        isAtomCompatible = true;
                        // Some support SSE3? true for Pineview? TBD...
                        isCore2Compatible = false;
                        isPentium4Compatible = false;
                        modelString = "Atom";
                        break;
                    // Penryn 45 nm
                    case 0x1d:
                        isCoreiCompatible = true;
                        modelString = "Penryn";
                        break;
                    // Nehalem 45 nm
                    case 0x1e:
                        isCoreiCompatible = true;
                        modelString = "Nehalem";
                        break;

                // following are for extended model == 2
                // most flags are set above
                // isCoreiCompatible = true is the default

                    // Westmere 32 nm
                    case 0x25:
                        modelString = "Westmere";
                        break;
                    // Atom Lincroft 45 nm
                    case 0x26:
                        isAtomCompatible = true;
                        // Supports SSE 3
                        isCoreiCompatible = false;
                        modelString = "Atom";
                        break;
                    // Sandy bridge 32 nm
                    // 1, 2, or 4 cores
                    // ref: https://en.wikipedia.org/wiki/Sandy_Bridge_%28microarchitecture%29
                    case 0x2a:
                        isSandyCompatible = true;
                        modelString = "Sandy Bridge";
                        break;
                    // Details unknown, please add a proper model string if 0x2B model is found 
                    case 0x2b:
                        modelString = "Core i7/i5 (32nm)";
                        break;
                    // Westmere
                    case 0x2c:
                        modelString = "Westmere";
                        break;
                    // Sandy Bridge 32 nm
                    // Sandy Bridge-E up to 8 cores
                    // ref: https://en.wikipedia.org/wiki/Sandy_Bridge_%28microarchitecture%29
                    case 0x2d:
                        isSandyCompatible = true;
                        modelString = "Sandy Bridge";
                        break;
                    // Nehalem 45 nm
                    case 0x2e:
                        modelString = "Nehalem";
                        break;
                    // Westmere 32 nm
                    case 0x2f:
                        modelString = "Westemere";
                        break;

                // following are for extended model == 3
                // most flags are set above
                // isCoreiCompatible = true is the default

                    // Atom Cedarview 32 nm
                    case 0x36:
                        isAtomCompatible = true;
                        // Supports SSE 3
                        isCore2Compatible = false;
                        isCoreiCompatible = false;
                        modelString = "Atom";
                        break;
                    // Silvermont 22 nm Celeron
                    case 0x37:
                        isAtomCompatible = true;
                        isCore2Compatible = false;
                        isCoreiCompatible = false;
                        modelString = "Atom";
                        break;
                    // Ivy Bridge 22 nm
                    // ref: https://en.wikipedia.org/wiki/Sandy_Bridge_%28microarchitecture%29
                    case 0x3a:
                        isSandyCompatible = true;
                        isIvyCompatible = true;
                        modelString = "Ivy Bridge";
                        break;

                    // case 0x3c: See below

                    // Broadwell 14 nm
                    // See Haswell notes below
                    case 0x3d: {
                        CPUIDCPUInfo c = new CPUIDCPUInfo();
                        if (c.hasAVX2() && c.hasBMI1()  && c.hasBMI2() &&
                            c.hasFMA3() && c.hasMOVBE() && c.hasABM()) {
                            isSandyCompatible = true;
                            isIvyCompatible = true;
                            isHaswellCompatible = true;
                            if (c.hasADX())
                                isBroadwellCompatible = true;
                            modelString = "Broadwell Core i3/i5/i7";
                        } else {
                            // This processor is "corei" compatible, as we define it,
                            // i.e. SSE4.2 but not necessarily AVX.
                            if (c.hasAVX()) {
                                isSandyCompatible = true;
                                isIvyCompatible = true;
                                modelString = "Broadwell Celeron/Pentium w/ AVX";
                            } else {
                                modelString = "Broadwell Celeron/Pentium";
                            }
                        }
                        break;
                    }

                    // Ivy Bridge 22 nm
                    case 0x3e:
                        isSandyCompatible = true;
                        isIvyCompatible = true;
                        modelString = "Ivy Bridge";
                        break;

                    // case 0x3f: See below

                // following are for extended model == 4
                // most flags are set above
                // isCoreiCompatible = true is the default

                    // Haswell 22 nm
                    // Pentium and Celeron Haswells do not support new Haswell instructions,
                    // only Corei ones do, but we can't tell that from the model alone.
                    //
                    // We know for sure that GMP coreihwl uses the MULX instruction from BMI2,
                    // unsure about the others, but let's be safe and check all 6 feature bits, as
                    // the Intel app note suggests.
                    //
                    // ref: https://en.wikipedia.org/wiki/Haswell_%28microarchitecture%29
                    // ref: https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
                    case 0x3c:
                    case 0x3f:
                    case 0x45:
                    case 0x46:
                    {
                        CPUIDCPUInfo c = new CPUIDCPUInfo();
                        if (c.hasAVX2() && c.hasBMI1()  && c.hasBMI2() &&
                            c.hasFMA3() && c.hasMOVBE() && c.hasABM()) {
                            isSandyCompatible = true;
                            isIvyCompatible = true;
                            isHaswellCompatible = true;
                            modelString = "Haswell Core i3/i5/i7 model " + model;
                        } else {
                            // This processor is "corei" compatible, as we define it,
                            // i.e. SSE4.2 but not necessarily AVX.
                            if (c.hasAVX()) {
                                isSandyCompatible = true;
                                isIvyCompatible = true;
                                modelString = "Haswell Celeron/Pentium w/ AVX model " + model;
                            } else {
                                modelString = "Haswell Celeron/Pentium model " + model;
                            }
                        }
                        break;
                    }

                    // Quark 32nm
                    case 0x4a:
                        isCore2Compatible = false;
                        isCoreiCompatible = false;
                        modelString = "Quark";
                        break;                
                    // Silvermont 22 nm
                    // Supports SSE 4.2
                    case 0x4d:
                        isAtomCompatible = true;
                        modelString = "Atom";
                        break;

                // following are for extended model == 5
                // most flags are set above
                // isCoreiCompatible = true is the default

                    // Skylake 14 nm
                    // See reference link errata #SKD052 re: BMI
                    // ref: http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/desktop-6th-gen-core-family-spec-update.pdf
                    // See Haswell notes above
                    case 0x5e: {
                        CPUIDCPUInfo c = new CPUIDCPUInfo();
                        if (c.hasAVX2() && c.hasBMI1()  && c.hasBMI2() &&
                            c.hasFMA3() && c.hasMOVBE() && c.hasABM()) {
                            isSandyCompatible = true;
                            isIvyCompatible = true;
                            isHaswellCompatible = true;
                            if (c.hasADX())
                                isBroadwellCompatible = true;
                            modelString = "Skylake Core i3/i5/i7";
                        } else {
                            // This processor is "corei" compatible, as we define it,
                            // i.e. SSE4.2 but not necessarily AVX.
                            if (c.hasAVX()) {
                                isSandyCompatible = true;
                                isIvyCompatible = true;
                                modelString = "Skylake Celeron/Pentium w/ AVX";
                            } else {
                                modelString = "Skylake Celeron/Pentium";
                            }
                        }
                        break;
                    }

                // following are for extended model == 8 or 9
                // most flags are set above
                // isCoreiCompatible = true is the default

                    // Kaby Lake
                    // ref: https://github.com/InstLatx64/InstLatx64/commit/9d2ea1a9eb727868dc514900da9e2f175710f9bf
                    // See Haswell notes above
                    case 0x8e:
                    case 0x9e: {
                        CPUIDCPUInfo c = new CPUIDCPUInfo();
                        if (c.hasAVX2() && c.hasBMI1()  && c.hasBMI2() &&
                            c.hasFMA3() && c.hasMOVBE() && c.hasABM()) {
                            isSandyCompatible = true;
                            isIvyCompatible = true;
                            isHaswellCompatible = true;
                            if (c.hasADX())
                                isBroadwellCompatible = true;
                            modelString = "Kaby Lake Core i3/i5/i7";
                        } else {
                            // This processor is "corei" compatible, as we define it,
                            // i.e. SSE4.2 but not necessarily AVX.
                            if (c.hasAVX()) {
                                isSandyCompatible = true;
                                isIvyCompatible = true;
                                modelString = "Kaby Lake Celeron/Pentium w/ AVX";
                            } else {
                                modelString = "Kaby Lake Celeron/Pentium";
                            }
                        }
                        break;
                    }

                    // others
                    default:
                        modelString = "Intel model " + model;
                        break;

                } // switch model
            } // case 6
            break;

            case 7: {
                modelString = "Intel Itanium model " + model;
            }
            break;

            case 15: {
                isPentiumCompatible = true;
                isPentiumMMXCompatible = true;
                isPentium2Compatible = true;
                isPentium3Compatible = true;
                isPentium4Compatible = true;
                switch (model) {
                    case 0:
                    case 1:
                        modelString = "Pentium IV (180 nm)";
                        break;
                    case 2:
                        modelString = "Pentium IV (130 nm)";
                        break;
                    case 3:
                        modelString = "Pentium IV (90 nm)";
                        break;
                    case 4:
                        modelString = "Pentium IV (90 nm)";
                        break;
                    case 6:
                        modelString = "Pentium IV (65 nm)";
                        break;
                    default:
                        modelString = "Intel Pentium IV model " + model;
                        break;
                }
            }
            break;

            case 16: {
                modelString = "Intel Itanium II model " + model;
            }
        }
        return modelString;
    }
}
