import platform
import os
import sys
import traceback
import time
import subprocess


def ReadFromFile(path):
    """\
    Return content of file
    """
    import os
    import codecs

    if os.path.exists(path):
        f = codecs.open(path, encoding="utf-8", mode="r")
        text = f.read()  # .decode('utf8')
        f.close()
        return text


def WriteToFile(path, string):
    """\
    Write content to file
    """
    f = open(path, "wb")
    f.write(string.encode())
    f.close()
    return True


def Execute(command):
    """\
    Execute system command, return output.
    """

    import os
    import subprocess

    process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, close_fds=True)
    os.waitpid(process.pid, 0)
    response = process.stdout.read().strip()
    process.stdout.close()
    process.wait()
    return response


def Garbage(length, uppercase=True, lowercase=True, numbers=True, punctuation=False):
    """\
    Return string containing garbage.
    """

    import random

    uppercaseparts = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    lowercaseparts = "abcdefghijklmnopqrstuvwxyz"
    numberparts = "0123456789"
    punctuationparts = ".-_"

    pool = ""
    if uppercase:
        pool += uppercaseparts
    if lowercase:
        pool += lowercaseparts
    if numbers:
        pool += numberparts
    if punctuation:
        pool += punctuationparts

    garbage = ""

    while len(garbage) < length:
        garbage += random.choice(pool)

    return garbage


def get_registry_value(key, subkey, value):
    import winreg

    key = getattr(winreg, key)
    handle = winreg.OpenKey(key, subkey)
    (value, type) = winreg.QueryValueEx(handle, value)
    return value


def install_font(path, data):
    try:
        f = open(path, "wb")
        f.write(data)
        f.close()

        if WIN:
            register_font_in_winreg(path)

        return True, None

    except Exception:
        return False, traceback.format_exc()


def uninstall_font(path):
    if WIN:
        unregister_font_in_winreg(path)

    i = 0
    while os.path.exists(path) and i < 100:
        try:
            os.remove(path)
        except PermissionError:
            if WIN:
                print("delete using secondary procedure")
                subprocess.call(f"del {path}", shell=True)
            time.sleep(0.1)
        i += 1

    # Font still not deleted
    if os.path.exists(path):

        # Put registry entry bacl where it was
        if WIN:
            register_font_in_winreg(path)

        return (
            False,
            "Font couldn’t be removed.",
        )

    return True, None


def register_font_in_winreg(path):
    from fontTools.ttLib import TTFont

    f = TTFont(path)
    name = (f["name"].getName(4, 3, 1, 1033) or f["name"].getName(4, 1, 0, 0)).toUnicode()
    string = f"{name} ({os.path.splitext(path)[-1].split('.')[-1]})"
    f.close()
    import winreg as wreg

    key = wreg.OpenKey(
        wreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", 0, wreg.KEY_SET_VALUE
    )
    wreg.SetValueEx(key, string, 0, wreg.REG_SZ, os.path.basename(path))


def unregister_font_in_winreg(path):
    from fontTools.ttLib import TTFont

    f = TTFont(path)
    name = (f["name"].getName(4, 3, 1, 1033) or f["name"].getName(4, 1, 0, 0)).toUnicode()
    string = f"{name} ({os.path.splitext(path)[-1].split('.')[-1]})"
    f.close()
    import winreg as wreg

    key = wreg.OpenKey(
        wreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", 0, wreg.KEY_ALL_ACCESS
    )
    try:
        wreg.DeleteValue(key, string)
    except FileNotFoundError:
        pass
    key.Close()


# https://www.tenforums.com/tutorials/54452-rebuild-font-cache-windows-10-a.html
def rebuild_font_cache():
    os.system('sc stop "FontCache"')
    os.system('sc config "FontCache" start=disabled')
    os.system('del /A /F /Q "%WinDir%\\ServiceProfiles\\LocalService\\AppData\\Local\\FontCache\\*FontCache*"')
    os.system('del /A /F /Q "%WinDir%\\System32\\FNTCACHE.DAT"')
    os.system('sc config "FontCache" start=auto')
    os.system('sc start "FontCache"')


def MachineName():

    machineModelIdentifier = None
    humanReadableName = None
    specsDescription = None

    if platform.system() == "Windows":

        specsDescription = get_registry_value(
            "HKEY_LOCAL_MACHINE",
            "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
            "ProcessorNameString",
        )
        humanReadableName = get_registry_value(
            "HKEY_LOCAL_MACHINE",
            "HARDWARE\\DESCRIPTION\\System\\BIOS",
            "SystemProductName",
        )

    if platform.system() == "Linux":

        cpu = ""
        itemsUsed = []
        procinfo = Execute("cat /proc/cpuinfo").decode()

        for line in procinfo.split("\n"):
            if ":" in line:
                k, v = line.split(":")[:2]
                if k.strip() == "model name" and k not in itemsUsed:
                    cpu += v.strip()
                    itemsUsed.append(k)

        if os.path.exists("/sys/devices/virtual/dmi/id/sys_vendor") and os.path.exists(
            "/sys/devices/virtual/dmi/id/product_name"
        ):
            humanReadableName = "%s %s" % (
                Execute("cat /sys/devices/virtual/dmi/id/sys_vendor").decode(),
                Execute("cat /sys/devices/virtual/dmi/id/product_name").decode(),
            )
        else:
            humanReadableName = "Google App Engine"  # nocoverage
        specsDescription = cpu

    elif platform.system() == "Darwin":

        name = None

        # Approach 1
        import plistlib

        data = plistlib.loads(Execute("system_profiler -xml SPHardwareDataType"))

        # Approach 2
        if not name:
            name = data[0]["_items"][0]["machine_name"]

        machineModelIdentifier = data[0]["_items"][0]["machine_model"]
        humanReadableName = "%s" % name
        # if not name.startswith('Apple'):
        # 	name = 'Apple ' + name
        specsDescription = []
        # if 'cpu_type' in data[0]['_items'][0]:
        # 	specsDescription.append(data[0]['_items'][0]['cpu_type'])
        if "current_processor_speed" in data[0]["_items"][0]:
            specsDescription.append(data[0]["_items"][0]["current_processor_speed"])
        specsDescription.append("with")
        if "physical_memory" in data[0]["_items"][0]:
            specsDescription.append(data[0]["_items"][0]["physical_memory"])

        specsDescription = " ".join(specsDescription)

    return machineModelIdentifier, humanReadableName, specsDescription


def OSName():

    import platform

    if platform.system() == "Darwin":
        return "macOS %s" % platform.mac_ver()[0]

    elif platform.system() == "Windows":

        return (
            get_registry_value(
                "HKEY_LOCAL_MACHINE",
                "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
                "ProductName",
            )
            + ", "
            + str(platform.version())
        )

    elif platform.system() == "Linux":
        return " ".join(platform.platform())


def addAttributeToURL(url, attributes):

    from urllib.parse import urlparse

    o = urlparse(url)

    for attribute in attributes.split("&"):

        key, value = attribute.split("=")

        replaced = False
        queryParts = o.query.split("&")
        if queryParts:
            for i, query in enumerate(queryParts):
                if "=" in query and query.startswith(key + "="):
                    queryParts[i] = attribute
                    replaced = True
                    break
        if not replaced:
            if queryParts[0]:
                queryParts.append(attribute)
            else:
                queryParts[0] = attribute
        o = o._replace(query="&".join(queryParts))

    return o.geturl()


WIN = platform.system() == "Windows"
MAC = platform.system() == "Darwin"
LINUX = platform.system() == "Linux"


# from https://gist.github.com/dgelessus/018681c297dfae22a06a3bc315d9e6e3:

"""Allows logging Python :class:`str`s to the system log using Foundation's ``NSLog``
function. This module is meant to help with debugging the loading process of Rubicon on
systems like iOS, where sometimes only NSLog output (but not stdout/stderr) is available
to the developer."""

if MAC:
    import ctypes

    __all__ = [
        "nslog",
    ]

    # Name of the UTF-16 encoding with the system byte order.
    if sys.byteorder == "little":
        UTF16_NATIVE = "utf-16-le"
    elif sys.byteorder == "big":  # nocoverage
        UTF16_NATIVE = "utf-16-be"  # nocoverage
    else:
        raise AssertionError("Unknown byte order: " + sys.byteorder)  # nocoverage

    # Note: Many of the following definitions are duplicated in other rubicon.objc
    # submodules.
    # However because this module is used to debug the early startup process of Rubicon,
    # we can't use any of the other
    # definitions.

    class CFTypeRef(ctypes.c_void_p):
        pass

    CFIndex = ctypes.c_long
    UniChar = ctypes.c_uint16

    CoreFoundation = ctypes.CDLL("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")
    Foundation = ctypes.CDLL("/System/Library/Frameworks/Foundation.framework/Foundation")

    # void CFRelease(CFTypeRef arg)
    CoreFoundation.CFRelease.restype = None
    CoreFoundation.CFRelease.argtypes = [CFTypeRef]

    # CFStringRef CFStringCreateWithCharacters(CFAllocatorRef alloc, const UniChar
    # *chars, CFIndex numChars)
    CoreFoundation.CFStringCreateWithCharacters.restype = CFTypeRef
    CoreFoundation.CFStringCreateWithCharacters.argtypes = [
        CFTypeRef,
        ctypes.POINTER(UniChar),
        CFIndex,
    ]

    # void NSLog(NSString *format, ...)
    Foundation.NSLog.restype = None
    Foundation.NSLog.argtypes = [CFTypeRef]

    def _cfstr(s):
        """Create a ``CFString`` from the given Python :class:`str`."""

        encoded = s.encode(UTF16_NATIVE)
        assert len(encoded) % 2 == 0
        arr = (UniChar * (len(encoded) // 2)).from_buffer_copy(encoded)
        cfstring = CoreFoundation.CFStringCreateWithCharacters(None, arr, len(arr))
        assert cfstring is not None
        return cfstring

    # Format string for a single object.
    FORMAT = _cfstr("%@")

    def nslog(s):
        """Log the given Python :class:`str` to the system log."""

        cfstring = _cfstr(s)
        Foundation.NSLog(FORMAT, cfstring)
        CoreFoundation.CFRelease(cfstring)

    # class NSLogWriter(io.TextIOBase):
    # 	"""An output-only text stream that writes to the system log."""

    # 	def write(self, s):
    # 		nslog(s[:-1] if s.endswith('\n') else s)
    # 		return len(s)

    # 	@property
    # 	def encoding(self):
    # 		return UTF16_NATIVE
