# -*- coding: utf-8 -*-

#-------------------------------------------------------------------------
# drawElements Quality Program utilities
# --------------------------------------
#
# Copyright 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#-------------------------------------------------------------------------

import os
import sys
import copy
import multiprocessing

from . common import which, HostInfo, DEQP_DIR

try:
    if sys.version_info < (3, 0):
        import _winreg
    else:
        import winreg
        _winreg = winreg
except:
    _winreg = None

class BuildConfig:
    def __init__ (self, buildDir, buildType, args, srcPath = DEQP_DIR):
        self.srcPath = srcPath
        self.buildDir = buildDir
        self.buildType = buildType
        self.args = copy.copy(args)
        self.cmakePath = BuildConfig.findCMake()

    def getSrcPath (self):
        return self.srcPath

    def getBuildDir (self):
        return self.buildDir

    def getBuildType (self):
        return self.buildType

    def getArgs (self):
        return self.args

    def getCMakePath (self):
        return self.cmakePath

    @staticmethod
    def findCMake ():
        if which("cmake") == None:
            possiblePaths = [
                "/Applications/CMake.app/Contents/bin/cmake"
            ]
            for path in possiblePaths:
                if os.path.exists(path):
                    return path
            raise FileNotFoundError("cmake executable file is not avaliable on the platform. It may not have been installed or added to PATH environment variable")
        return "cmake"

class CMakeGenerator:
    def __init__ (self, name, isMultiConfig = False, extraBuildArgs = [], platform = None):
        self.name = name
        self.isMultiConfig = isMultiConfig
        self.extraBuildArgs = copy.copy(extraBuildArgs)
        self.platform = platform

    def getName (self):
        return self.name

    def getGenerateArgs (self, buildType):
        args = ['-G', self.name]
        if not self.isMultiConfig:
            args.append('-DCMAKE_BUILD_TYPE=%s' % buildType)
        if self.platform:
            # this is supported since CMake 3.1, needed for VS2019+
            args.append('-A')
            args.append(self.platform)
        return args

    def getBuildArgs (self, buildType):
        args = []
        if self.isMultiConfig:
            args += ['--config', buildType]
        if len(self.extraBuildArgs) > 0:
            args += ['--'] + self.extraBuildArgs
        return args

    def getBinaryPath (self, buildType, basePath):
        return basePath

class UnixMakefileGenerator(CMakeGenerator):
    def __init__(self):
        CMakeGenerator.__init__(self, "Unix Makefiles", extraBuildArgs = ["-j%d" % multiprocessing.cpu_count()])

    def isAvailable (self):
        return which('make') != None

class NMakeGenerator(CMakeGenerator):
    def __init__(self):
        CMakeGenerator.__init__(self, "NMake Makefiles")

    def isAvailable (self):
        return which('nmake') != None

class NinjaGenerator(CMakeGenerator):
    def __init__(self):
        CMakeGenerator.__init__(self, "Ninja")

    def isAvailable (self):
        return which('ninja') != None

class VSProjectGenerator(CMakeGenerator):
    ARCH_32BIT = 0
    ARCH_64BIT = 1

    def __init__(self, version, arch):
        name = "Visual Studio %d" % version

        platform = None

        if version >= 16:
            # From VS2019 onwards, the architecture is given by -A <platform-name> switch
            if arch == self.ARCH_64BIT:
                platform = "x64"
            elif arch == self.ARCH_32BIT:
                platform = "Win32"
        else:
            if arch == self.ARCH_64BIT:
                name += " Win64"

        CMakeGenerator.__init__(self, name, isMultiConfig = True, extraBuildArgs = ['/m'], platform = platform)
        self.version = version
        self.arch = arch

    def getBinaryPath (self, buildType, basePath):
        return os.path.join(os.path.dirname(basePath), buildType, os.path.basename(basePath) + ".exe")

    @staticmethod
    def getNativeArch ():
        bits = HostInfo.getArchBits()

        if bits == 32:
            return VSProjectGenerator.ARCH_32BIT
        elif bits == 64:
            return VSProjectGenerator.ARCH_64BIT
        else:
            raise Exception("Unhandled bits '%s'" % bits)

    @staticmethod
    def registryKeyAvailable (root, arch, name):
        try:
            key = _winreg.OpenKey(root, name, 0, _winreg.KEY_READ | arch)
            _winreg.CloseKey(key)
            return True
        except:
            return False

    def isAvailable (self):
        if sys.platform == 'win32' and _winreg != None:
            nativeArch = VSProjectGenerator.getNativeArch()
            if nativeArch == self.ARCH_32BIT and self.arch == self.ARCH_64BIT:
                return False

            arch = _winreg.KEY_WOW64_32KEY if nativeArch == self.ARCH_64BIT else 0
            keyMap = {
                10: [(_winreg.HKEY_CLASSES_ROOT, "VisualStudio.DTE.10.0"), (_winreg.HKEY_LOCAL_MACHINE, "Software\\Microsoft\\VCExpress\\10.0")],
                11: [(_winreg.HKEY_CLASSES_ROOT, "VisualStudio.DTE.11.0"), (_winreg.HKEY_LOCAL_MACHINE, "Software\\Microsoft\\VCExpress\\11.0")],
                12: [(_winreg.HKEY_CLASSES_ROOT, "VisualStudio.DTE.12.0"), (_winreg.HKEY_LOCAL_MACHINE, "Software\\Microsoft\\VCExpress\\12.0")],
                14: [(_winreg.HKEY_CLASSES_ROOT, "VisualStudio.DTE.14.0"), (_winreg.HKEY_LOCAL_MACHINE, "Software\\Microsoft\\VCExpress\\14.0")],
                15: [(_winreg.HKEY_CLASSES_ROOT, "VisualStudio.DTE.15.0"), (_winreg.HKEY_LOCAL_MACHINE, "Software\\Microsoft\\VCExpress\\15.0")],
                16: [(_winreg.HKEY_CLASSES_ROOT, "VisualStudio.DTE.16.0"), (_winreg.HKEY_LOCAL_MACHINE, "Software\\Microsoft\\VCExpress\\16.0")],
                17: [(_winreg.HKEY_CLASSES_ROOT, "VisualStudio.DTE.17.0"), (_winreg.HKEY_LOCAL_MACHINE, "Software\\Microsoft\\VCExpress\\17.0")]
            }

            if not self.version in keyMap:
                raise Exception("Unsupported VS version %d" % self.version)

            keys = keyMap[self.version]
            for root, name in keys:
                if VSProjectGenerator.registryKeyAvailable(root, arch, name):
                    return True
            return False
        else:
            return False

# Pre-defined generators

MAKEFILE_GENERATOR = UnixMakefileGenerator()
NMAKE_GENERATOR = NMakeGenerator()
NINJA_GENERATOR = NinjaGenerator()
VS2010_X32_GENERATOR = VSProjectGenerator(10, VSProjectGenerator.ARCH_32BIT)
VS2010_X64_GENERATOR = VSProjectGenerator(10, VSProjectGenerator.ARCH_64BIT)
VS2012_X32_GENERATOR = VSProjectGenerator(11, VSProjectGenerator.ARCH_32BIT)
VS2012_X64_GENERATOR = VSProjectGenerator(11, VSProjectGenerator.ARCH_64BIT)
VS2013_X32_GENERATOR = VSProjectGenerator(12, VSProjectGenerator.ARCH_32BIT)
VS2013_X64_GENERATOR = VSProjectGenerator(12, VSProjectGenerator.ARCH_64BIT)
VS2015_X32_GENERATOR = VSProjectGenerator(14, VSProjectGenerator.ARCH_32BIT)
VS2015_X64_GENERATOR = VSProjectGenerator(14, VSProjectGenerator.ARCH_64BIT)
VS2017_X32_GENERATOR = VSProjectGenerator(15, VSProjectGenerator.ARCH_32BIT)
VS2017_X64_GENERATOR = VSProjectGenerator(15, VSProjectGenerator.ARCH_64BIT)
VS2019_X32_GENERATOR = VSProjectGenerator(16, VSProjectGenerator.ARCH_32BIT)
VS2019_X64_GENERATOR = VSProjectGenerator(16, VSProjectGenerator.ARCH_64BIT)
VS2022_X32_GENERATOR = VSProjectGenerator(17, VSProjectGenerator.ARCH_32BIT)
VS2022_X64_GENERATOR = VSProjectGenerator(17, VSProjectGenerator.ARCH_64BIT)

def selectFirstAvailableGenerator (generators):
    for generator in generators:
        if generator.isAvailable():
            return generator
    return None

ANY_VS_X32_GENERATOR = selectFirstAvailableGenerator([
                                VS2022_X32_GENERATOR,
                                VS2019_X32_GENERATOR,
                                VS2017_X32_GENERATOR,
                                VS2015_X32_GENERATOR,
                                VS2013_X32_GENERATOR,
                                VS2012_X32_GENERATOR,
                                VS2010_X32_GENERATOR,
                            ])
ANY_VS_X64_GENERATOR = selectFirstAvailableGenerator([
                                VS2022_X64_GENERATOR,
                                VS2019_X64_GENERATOR,
                                VS2017_X64_GENERATOR,
                                VS2015_X64_GENERATOR,
                                VS2013_X64_GENERATOR,
                                VS2012_X64_GENERATOR,
                                VS2010_X64_GENERATOR,
                            ])
ANY_UNIX_GENERATOR = selectFirstAvailableGenerator([
                                NINJA_GENERATOR,
                                MAKEFILE_GENERATOR,
                                NMAKE_GENERATOR,
                            ])
ANY_GENERATOR = selectFirstAvailableGenerator([
                                VS2022_X64_GENERATOR,
                                VS2022_X32_GENERATOR,
                                VS2019_X64_GENERATOR,
                                VS2019_X32_GENERATOR,
                                VS2017_X64_GENERATOR,
                                VS2017_X32_GENERATOR,
                                VS2015_X64_GENERATOR,
                                VS2015_X32_GENERATOR,
                                VS2013_X64_GENERATOR,
                                VS2012_X64_GENERATOR,
                                VS2010_X64_GENERATOR,
                                VS2013_X32_GENERATOR,
                                VS2012_X32_GENERATOR,
                                VS2010_X32_GENERATOR,
                                NINJA_GENERATOR,
                                MAKEFILE_GENERATOR,
                                NMAKE_GENERATOR,
                            ])
