import json
import multiprocessing
import os
import platform
import subprocess
import sys

# Usually comes from third_party
from .third_party import cpuinfo
from .third_party import cpu_cores


def _getBlenderDeviceInfo(ctx):
    PREFIX = "Benchmark Devices: "
    command = [ctx.blender,
               "--background",
               "--factory-startup",
               "-noaudio",
               "--enable-autoexec",
               "--engine", "CYCLES",
               "--python", ctx.configure_script,
               "--",
               "--benchmark-system-info"]
    process = subprocess.Popen(command,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
    stdout, stderr = process.communicate()
    lines = stdout.decode().split("\n")
    for line in lines:
        if line.startswith(PREFIX):
            return json.loads(line[len(PREFIX):])
    return []


def getBlenderVersion(ctx):
    INFO = ("build_date",
            "build_time",
            "build_commit_date",
            "build_commit_time",
            "build_hash")
    command = [ctx.blender, "--background", "--version"]
    process = subprocess.Popen(command,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
    stdout, stderr = process.communicate()
    lines = stdout.decode().split("\n")
    info = {}
    for line in lines:
        line = line.strip()
        if line.startswith("Blender "):
            version = line[8:].strip()
            info['version'] = version
        if not line.startswith("build "):
            continue
        tokens = line.split(":", 1)
        tokens[0] = tokens[0].replace(" ", "_")
        if tokens[0] in INFO:
            info[tokens[0]] = tokens[1].strip()
    return info


def _getWmicCPUInfo(value):
    cmd = ("wmic", "cpu", "get", value, "/value")
    wmic_output = subprocess.check_output(cmd)
    return wmic_output.strip().decode()


def _getCPUTopologyInformation():
    """
    Get topology information (number of sockets, physical and logical threads)
    of the system CPUs.
    """
    import multiprocessing
    topology_info = {}
    topology_info["num_logical_threads"] = multiprocessing.cpu_count()
    if not sys.platform.startswith('win'):
        cores_info = cpu_cores.CPUCoresCounter.factory()
        topology_info["num_sockets"] = \
            cores_info.get_physical_processors_count()
        topology_info["num_physical_cores"] = \
            cores_info.get_physical_cores_count()
    else:
        num_cores = _getWmicCPUInfo("NumberOfCores").split("=")[1]
        # TODO(sergey): Needs implementation.
        topology_info["num_sockets"] = 1
        topology_info["num_physical_cores"] = num_cores
    return topology_info


def gatherSystemInfo(ctx):
    system_info = {}
    system_info['bitness'] = platform.architecture()[0]
    system_info['machine'] = platform.machine()
    system_info['system'] = platform.system()
    if system_info['system'] == "Linux":
        distro = platform.linux_distribution()
        system_info['dist_name'] = distro[0]
        system_info['dist_version'] = distro[1]
        # system_info['libc_version'] = "-".join(platform.libc_ver())
    # TODO(sergey): Make this to work on Windows and macOS
    cpu_info = cpuinfo.get_cpu_info()
    cpu_topology = _getCPUTopologyInformation()
    system_info['cpu_brand'] = cpu_info['brand']
    system_info["num_cpu_threads"] = cpu_topology['num_logical_threads']
    system_info["num_cpu_sockets"] = cpu_topology['num_sockets']
    system_info["num_cpu_cores"] = cpu_topology['num_physical_cores']
    system_info['devices'] = _getBlenderDeviceInfo(ctx)
    return system_info
