#!/usr/bin/env python3

import os
import argparse
import platform
import shutil


def file_exists(file):
    """
    Check if file exists. Used for parsing file inputs from command line.

    :param file: Path to a file, including extension.
    :type file: str
    """
    if not os.path.exists(file):
        raise argparse.ArgumentError("{0} does not exist.".format(file))
    return file


def positive_integer(inputint):
    """
    Check if number > 0. Used for parsing integer inputs from command line.

    :param inputint: Integer to check.
    :type inputint: int
    """
    if int(inputint) <= 0:
        raise argparse.ArgumentError("{0} is too low.".format(str(inputint)))
    return str(inputint)


def escreens_pin(pin):
    """
    Check if given PIN is valid (8 character hexadecimal)

    :param pin: PIN to check.
    :type pin: str
    """
    if len(pin) == 8:
        try:
            int(pin, 16)  # hexadecimal-ness
        except ValueError:
            raise argparse.ArgumentError("Invalid PIN.")
        else:
            return pin.lower()
    else:
        raise argparse.ArgumentError("Invalid PIN.")


def escreens_duration(duration):
    """
    Check if escreens duration is valid.

    :param duration: Duration to check.
    :type duration: int
    """
    if int(duration) in (1, 3, 6, 15, 30):
        return int(duration)
    else:
        raise argparse.ArgumentError("Invalid duration.")


def str2bool(v):
    """
    Parse bool from string input.

    :param v: String to check if it means True or False.
    :type v: str
    """
    return str(v).lower() in ("yes", "true", "t", "1", "y")


def is_amd64():
    """
    Returns true if script is running on an AMD64 system
    """
    amd64 = platform.machine().endswith("64")
    return amd64


def is_windows():
    """
    Returns true if script is running on Windows.
    """
    windows = platform.system() == "Windows"
    return windows


def is_mac():
    """
    Returns true if script is running on OSX.
    """
    mac = platform.system() == "Darwin"
    return mac


def is_linux():
    """
    Returns true if script is running on Linux.
    """
    linux = platform.system() == "Linux"
    return linux


def get_seven_zip(talkative=False):
    """
    Return name of 7-Zip executable.
    On POSIX, it MUST be 7za.
    On Windows, it can be installed or supplied with the script.
    :func:`win_seven_zip` is used to determine if it's installed.

    :param talkative: Whether to output to screen. False by default.
    :type talkative: bool
    """
    if is_windows():
        smeg = win_seven_zip(talkative)
        return smeg
    else:
        return "7za"


def win_seven_zip(talkative=False):
    """
    For Windows, checks where 7-Zip is.
    Consults registry first for any installed instances of 7-Zip.
    If it's not there, it falls back onto the supplied executables.
    If *those* aren't there, return "error".

    :param talkative: Whether to output to screen. False by default.
    :type talkative: bool
    """
    if talkative:
        print("CHECKING INSTALLED FILES...")
    try:
        import winreg  # windows registry
        hk7z = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\7-Zip")
        path = winreg.QueryValueEx(hk7z, "Path")
    except Exception as e:
        if talkative:
            print("SOMETHING WENT WRONG")
            print(str(e))
            print("TRYING LOCAL FILES...")
        listdir = os.listdir(os.getcwd())
        filecount = 0
        for i in listdir:
            if i == "7za.exe" or i == "7za64.exe":
                filecount += 1
        if filecount == 2:
            if talkative:
                print("7ZIP USING LOCAL FILES")
            if is_amd64():
                return "7za64.exe"
            else:
                return "7za.exe"
        else:
            if talkative:
                print("NO LOCAL FILES")
            return "error"
    else:
        if talkative:
            print("7ZIP USING INSTALLED FILES")
        return '"' + os.path.join(path[0], "7z.exe") + '"'


def get_core_count():
    """
    Find out how many CPU cores this system has.
    Good for multicore compression.
    """
    cores = str(os.cpu_count())  # thank you Python 3.4
    if os.cpu_count() is None:
        cores = str(1)
    return cores


def prep_seven_zip():
    """
    Check for presence of 7-Zip.
    On POSIX, checks for p7zip.
    On Windows, checks for 7-Zip.
    Returns False if not found, True if found.
    """
    if is_mac():
        path = shutil.which("7za")
        if path is None:
            print("NO 7ZIP")
            print("PLEASE INSTALL p7zip FROM SOURCE/HOMEBREW/MACPORTS")
            return False
        else:
            print("7ZIP FOUND AT", path)
            return True
    elif is_linux():
        path = shutil.which("7za")
        if path is None:
            print("NO 7ZIP")
            print("PLEASE INSTALL p7zip AND ANY RELATED PACKAGES")
            print("CONSULT YOUR PACKAGE MANAGER, OR GOOGLE IT")
            return False
        else:
            print("7ZIP FOUND AT", path)
            return True
    elif is_windows():
        smeg = get_seven_zip(True)
        if smeg == "error":
            return False
        else:
            return True


def return_model(index):
    """
    Return device model from selected HWID/variant index.
    Lists found in bbconstants module.

    :param index: The index to look up.
    :type index: int
    """
    if 0 <= index <= 3:
        return(0)  # Z10
    elif 4 <= index <= 5:
        return(1)  # P9982
    elif 6 <= index <= 10:
        return(2)  # Q10
    elif 11 <= index <= 13:
        return(3)  # Q5
    elif 14 <= index <= 15:
        return(4)  # P9983
    elif 16 <= index <= 21:
        return(5)  # Z30
    elif 22 <= index <= 26:
        return(6)  # Classic
    elif 27 <= index <= 28:
        return(7)  # Leap
    elif 29 <= index <= 30:
        return(8)  # Z3
    elif 31 <= index <= 33:
        return(9)  # Passport


def return_family(index):
    """
    Return device family from selected HWID.
    Lists found in bbconstants module.

    :param index: The index to look up.
    :type index: int
    """
    if index == 0:  # STL100-1
        return(0)
    elif 1 <= index <= 5:  # STL100-2/3/4, P9982
        return(1)
    elif 6 <= index <= 15:  # Q10, Q5, P9983
        return(2)
    elif 16 <= index <= 28:  # Z30, Classic, Leap
        return(3)
    elif 29 <= index <= 30:  # Z3
        return(4)
    elif 31 <= index <= 33:  # Passport
        return(5)
