'''
TestCPU
=======

Tested platforms:

* Windows
* Linux - nproc
'''

import sys
import unittest
from os import environ
from os.path import join
from mock import patch, Mock
from textwrap import dedent

from plyer.tests.common import PlatformTest, platform_import, splitpath


class MockedKernelCPU(object):
    def __init__(self, *args, **kwargs):
        self.fname = args[0] if args else ''
        self.cpu_path = join('/sys', 'devices', 'system', 'cpu')
        self.cores = 16
        self.indicies = 4

    def __enter__(self, *args):
        file_value = None
        cpu_path = self.cpu_path
        spath = splitpath(self.fname)

        if self.fname == join(cpu_path, 'present'):
            file_value = Mock()
            file_value.read.return_value = self.present
        elif spath[5] == 'cache' and spath[7] == 'level':
            file_value = Mock()
            # force bytes, because reading files as bytes
            file_value.read.return_value = str(
                self.index_types[spath[4]][spath[6]][spath[7]]
            ).encode('utf-8')
        return file_value

    def __exit__(self, *args):
        pass

    @property
    def present(self):
        rng = list(range(self.cores))
        start = str(rng[0])
        end = str(rng[-1])
        if start == end:  # cores == 1 --> b'0'
            value = str(start)
        else:  # cores > 1 --> b'0-n'
            value = str('-'.join([start, end]))
        return value.encode('utf-8')

    @property
    def listdir(self):
        return ['index{}'.format(i) for i in range(self.indicies)]

    @property
    def index_types(self):
        # assign L1 to index0-1, L2 to 2, L3 to 3
        types = {0: 1, 1: 1, 2: 2, 3: 3}

        return {
            'cpu{}'.format(c): {
                'index{}'.format(i): {
                    'level': types[i]
                }
                for i in range(self.indicies)
            }
            for c in range(self.cores)
        }


class MockedNProc(object):
    '''
    Mocked object used instead of 'nproc' binary in the Linux specific API
    plyer.platforms.linux.cpu. The same output structure is tested for
    the range of <min_version, max_version>.

    .. note:: Extend the object with another data sample if it does not match.
    '''

    min_version = '8.21'
    max_version = '8.21'
    logical_cores = 99

    def __init__(self, *args, **kwargs):
        # only to ignore all args, kwargs
        pass

    @staticmethod
    def communicate():
        '''
        Mock Popen.communicate, so that 'nproc' isn't used.
        '''
        return (str(MockedNProc.logical_cores).encode('utf-8'), )

    @staticmethod
    def whereis_exe(binary):
        '''
        Mock whereis_exe, so that it looks like
        Linux NProc binary is present on the system.
        '''
        return binary == 'nproc'

    @staticmethod
    def logical():
        '''
        Return percentage from mocked data.
        '''
        return int(MockedNProc.logical_cores)


class MockedProcinfo(object):
    # docs:
    # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    # /tree/arch/x86/kernel/cpu/proc.c
    sockets = 1  # physical id
    physical = 2  # core id
    threads_per_core = 2  # Intel specs document for i7-4500U
    logical = physical * threads_per_core  # processor

    def __init__(self, *args, **kwargs):
        self.fname = args[0] if args else ''

        self.output = []
        __step = 0  # 0,1,0,1 -> 0,0,1,1
        for soc in range(self.sockets):
            for log in range(self.logical):
                if log != 0 and not log % self.physical:
                    __step += 1
                self.output.append((dedent(
                    '''\
                    processor\t: {logical}
                    vendor_id\t: GenuineIntel
                    cpu family\t: 6
                    model\t\t: 69
                    model name\t: Intel(R) Core(TM) i7-4500U CPU @ 1.80GHz
                    stepping\t: 1
                    microcode\t: 0x17
                    cpu MHz\t\t: 774.000
                    cache size\t: 4096 KB
                    physical id\t: {socket}
                    siblings\t: 4
                    core id\t\t: {physical}
                    cpu cores\t: {threads_per_core}
                    apicid\t\t: {logical}
                    initial apicid\t: 0
                    fpu\t\t: yes
                    fpu_exception\t: yes
                    cpuid level\t: 13
                    wp\t\t: yes
                    flags\t\t: fpu vme de pse tsc msr pae mce cx8 ...
                    bogomips\t: 3591.40