# -*- coding: utf-8 -*-
import base64
import hashlib
import os
import os.path
import subprocess


def subprocess_args(include_stdout=True):
    if hasattr(subprocess, 'STARTUPINFO'):
        si = subprocess.STARTUPINFO()
        si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        env = os.environ
    else:
        si = None
        env = None

    if include_stdout:
        ret = {'stdout': subprocess.PIPE}
    else:
        ret = {}

    ret.update({'stdin': subprocess.PIPE,
                'stderr': subprocess.PIPE,
                'startupinfo': si,
                'env': env})
    return ret


class HelperMethods:

    @staticmethod
    def get_SHA256(string):
        """
        Compute the SHA256 signature of a string.
        """
        return hashlib.sha256(string.encode("utf-8")).hexdigest()

    @staticmethod
    def I2OSP(x, xLen):
        if x > (1 << (8 * xLen)):
            return None
        Xrev = []
        for _ in range(0, xLen):
            x, m = divmod(x, 256)
            Xrev.append(m)
        return bytes(reversed(Xrev))

    @staticmethod
    def OS2IP(X):
        import binascii
        h = binascii.hexlify(X)
        return int(h, 16)

    @staticmethod
    def RSAVP1(pair, s):
        n, e = pair
        if s < 0 or n - 1 < s:
            return None
        return pow(s, e, n)

    @staticmethod
    def EMSA_PKCS1_V15_ENCODE(M, emLen):
        import hashlib
        h = hashlib.sha256()
        h.update(M)
        H = h.digest()

        T = bytes(
            [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04,
             0x20]) + H
        tLen = len(T)
        if emLen < tLen + 11:
            return None
        PS = bytes([0xff for _ in range(emLen - tLen - 3)])
        return b"".join([b"\x00\x01", PS, b"\x00", T])

    @staticmethod
    def RSAASSA_PKCS1_V15_VERIFY(pair, M, S):
        n, e = pair
        s = HelperMethods.OS2IP(S)
        m = HelperMethods.RSAVP1((n, e), s)
        if m is None: return False
        EM = HelperMethods.I2OSP(m, 256)
        if EM is None: return False
        EM2 = HelperMethods.EMSA_PKCS1_V15_ENCODE(M, 256)  # Can return None, but it's OK since EM is not None
        return EM == EM2

    @staticmethod
    def verify_signature(response, rsaPublicKey):
        """
        Verifies a signature from .NET RSACryptoServiceProvider.
        """

        n = HelperMethods.OS2IP(base64.b64decode(rsaPublicKey.modulus))
        e = HelperMethods.OS2IP(base64.b64decode(rsaPublicKey.exponent))

        m = base64.b64decode(response.license_key)
        r = base64.b64decode(response.signature)

        return HelperMethods.RSAASSA_PKCS1_V15_VERIFY((n, e), m, r)

    @staticmethod
    def int2base64(num):
        return base64.b64encode(int.to_bytes(num), byteorder='big')

    @staticmethod
    def base642int(string):
        return int.from_bytes(base64.b64decode((string)), byteorder='big')

    @staticmethod
    def start_process(command, v=1):

        output = subprocess.check_output(command,
                                         **subprocess_args(False))

        if v == 1:
            return output.decode('utf-8')
        elif v == 2:
            rawOutput = output.decode('utf-8')
            return rawOutput[rawOutput.index("UUID") + 4:].strip()
        else:
            raise ValueError("Version can be either 1 or 2.")

    @staticmethod
    def get_dbus_machine_id():
        try:
            with open("/etc/machine-id") as f:
                return f.read().strip()
        except:
            pass
        try:
            with open("/var/lib/dbus/machine-id") as f:
                return f.read().strip()
        except:
            pass
        return ""

    @staticmethod
    def get_inodes():
        import os
        files = ["/bin", "/etc", "/lib", "/root", "/sbin", "/usr", "/var"]
        inodes = []
        for file in files:
            try:
                inodes.append(os.stat(file).st_ino)
            except:
                pass
        return "".join([str(x) for x in inodes])

    @staticmethod
    def compute_machine_code():
        return HelperMethods.get_dbus_machine_id() + HelperMethods.get_inodes()
