import subprocess
import datetime
import sys
import os    
import shutil

from engine.DiskLib import *
from engine.CacheLib import *
from engine.StringLib import *

#NODEBUGFUNCTIONS, OGIMG, OG_ERR_CACHESIZE, OG_ERR_NOTCACHE, OG_ERR_NOTWRITE, OG_ERR_FILESYS
#OG_ERR_REPO, OG_ERR_NOTOS, OG_ERR_NOGPT, OG_ERR_OUTOFLIMIT, OG_ERR_IMAGE, OG_ERR_CACHE
#OGLOGSESSION, OGLOGCOMMAND, OGLOGFILE, OG_ERR_LOCKED, OG_ERR_PARTITION, OG_ERR_FORMAT, OG_ERR_NOTEXEC, OG_ERR_NOTFOUND

def ogEcho(*args):
    # Variables locales
    CONT = 1
    LOGS = ""
    LOGLEVEL = ""
    DATETIME = ""

    # Selección de ficheros de registro de incidencias.
    while CONT:
        arg = args.pop(0).lower()
        if arg == "log":
            LOGS += " " + OGLOGFILE
        elif arg == "command":
            LOGS += " " + OGLOGCOMMAND
        elif arg == "session":
            LOGS += " " + OGLOGSESSION
        else:
            CONT = 0

    # Selección del nivel de registro (opcional).
    arg = args.pop(0).lower()
    if arg == "help":
        pass
    elif arg == "info" or arg == "warning" or arg == "error":
        LOGLEVEL = arg

    if LOGLEVEL:
        DATETIME = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        # Registrar mensajes en fichero de log si la depuración no está desactivada.
        if DEBUG.lower() != "no":
            LOGS += " " + OGLOGFILE
        subprocess.call(f"logger -t OpenGnsys {LOGLEVEL} {DATETIME} {' '.join(args)}", shell=True)
    else:
        print(' '.join(args))

def ogExecAndLog(*args):
    # Variables locales
    ISCOMMAND = False
    ISLOG = False
    ISSESSION = False
    COMMAND = ""
    CONTINUE = 1
    FILES = ""
    REDIREC = ""
    FUNCNAME = ogExecAndLog.__name__

    # Si se solicita, mostrar ayuda.
    if len(args) > 0 and args[0] == "help":
        ogHelp(f"{FUNCNAME} str_logfile ... str_command ...",
                f"{FUNCNAME} COMMAND ls -al /")
        return

    # Procesar parámetros.
    while CONTINUE:
        arg = args.pop(0).lower()
        if arg == "command":
            ISCOMMAND = True
            continue
        elif arg == "log":
            ISLOG = True
            continue
        elif arg == "session":
            ISSESSION = True
            continue
        else:
            COMMAND = " ".join(args)
            CONTINUE = 0

    # Error si no se recibe un comando que ejecutar.
    if not COMMAND:
        ogRaiseError(OG_ERR_FORMAT)
        return

    # Componer lista de ficheros de registro.
    if ISCOMMAND:
        FILES = OGLOGCOMMAND
        open(FILES, "w").close()
        REDIREC = "2>&1"
    if ISLOG:
        FILES += " " + OGLOGFILE
    if ISSESSION:
        FILES += " " + OGLOGSESSION

    # Ejecutar comando.
    subprocess.call(f"{COMMAND} {REDIREC} | tee -a {FILES}", shell=True)
    # Salida de error del comando ejecutado.
    return subprocess.PIPESTATUS[0]

def ogGetCaller():
    # Obtener el nombre del programa o del script que ha llamado al proceso actual.
    output = subprocess.check_output(["ps", "hp", str(os.getppid()), "-o", "args"]).decode("utf-8")
    lines = output.split("\n")
    caller = ""
    for line in lines:
        if "bash" in line and line.split()[1] != "":
            caller = line.split()[1]
        else:
            caller = line.split()[0].lstrip("-")
    return os.path.basename(caller)

def ogHelp(*args):
    # Variables locales
    FUNC = ""
    MSG = ""
    FUNCNAME = ogHelp.__name__

    # Mostrar función, descripción y formato.
    FUNC = args[0] if len(args) > 0 else FUNCNAME[-1]
    MSG = f"MSG_HELP_{FUNC}"
    ogEcho("help", f"{MSG_FUNCTION} {FUNC}: {globals()[MSG]}")
    if len(args) > 1:
        ogEcho("help", f"    {MSG_FORMAT}: {args[1]}")

    # Mostrar ejemplos (si existen).
    for example in args[2:]:
        ogEcho("help", f"    {MSG_EXAMPLE}: {example}")

def ogRaiseError(*args):
    # Variables locales
    CONT = 1
    LOGS = ""
    MSG = ""
    CODE = ""
    FUNCS = ""
    FUNCNAME = ogRaiseError.__name__

    # Si se solicita, mostrar ayuda.
    if len(args) > 0 and args[0] == "help":
        ogHelp(f"{FUNCNAME}", f"{FUNCNAME} [str_logfile ...] int_errorcode str_errormessage")
        return

    # Selección de registros de incidencias.
    while CONT:
        arg = args.pop(0).lower()
        if arg == "log" or arg == "command" or arg == "session":
            LOGS += " " + arg
        else:
            CONT = 0

    # Obtener código y mensaje de error.
    CODE = args.pop(0)
    if CODE == OG_ERR_FORMAT:
        MSG = f"{MSG_ERR_FORMAT} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOTFOUND:
        MSG = f"{MSG_ERR_NOTFOUND} \"{args.pop(0)}\""
    elif CODE == OG_ERR_OUTOFLIMIT:
        MSG = f"{MSG_ERR_OUTOFLIMIT} \"{args.pop(0)}\""
    elif CODE == OG_ERR_PARTITION:
        MSG = f"{MSG_ERR_PARTITION} \"{args.pop(0)}\""
    elif CODE == OG_ERR_LOCKED:
        MSG = f"{MSG_ERR_LOCKED} \"{args.pop(0)}\""
    elif CODE == OG_ERR_CACHE:
        MSG = f"{MSG_ERR_CACHE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOGPT:
        MSG = f"{MSG_ERR_NOGPT} \"{args.pop(0)}\""
    elif CODE == OG_ERR_REPO:
        MSG = f"{MSG_ERR_REPO} \"{args.pop(0)}\""
    elif CODE == OG_ERR_FILESYS:
        MSG = f"{MSG_ERR_FILESYS} \"{args.pop(0)}\""
    elif CODE == OG_ERR_IMAGE:
        MSG = f"{MSG_ERR_IMAGE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOTOS:
        MSG = f"{MSG_ERR_NOTOS} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOTEXEC:
        MSG = f"{MSG_ERR_NOTEXEC} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOTWRITE:
        MSG = f"{MSG_ERR_NOTWRITE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOTCACHE:
        MSG = f"{MSG_ERR_NOTCACHE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_CACHESIZE:
        MSG = f"{MSG_ERR_CACHESIZE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_REDUCEFS:
        MSG = f"{MSG_ERR_REDUCEFS} \"{args.pop(0)}\""
    elif CODE == OG_ERR_EXTENDFS:
        MSG = f"{MSG_ERR_EXTENDFS} \"{args.pop(0)}\""
    elif CODE == OG_ERR_IMGSIZEPARTITION:
        MSG = f"{MSG_ERR_IMGSIZEPARTITION} \"{args.pop(0)}\""
    elif CODE == OG_ERR_UPDATECACHE:
        MSG = f"{MSG_ERR_UPDATECACHE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_DONTFORMAT:
        MSG = f"{MSG_ERR_DONTFORMAT} \"{args.pop(0)}\""
    elif CODE == OG_ERR_IMAGEFILE:
        MSG = f"{MSG_ERR_IMAGEFILE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_UCASTSYNTAXT:
        MSG = f"{MSG_ERR_UCASTSYNTAXT} \"{args.pop(0)}\""
    elif CODE == OG_ERR_UCASTSENDPARTITION:
        MSG = f"{MSG_ERR_UCASTSENDPARTITION} \"{args.pop(0)}\""
    elif CODE == OG_ERR_UCASTSENDFILE:
        MSG = f"{MSG_ERR_UCASTSENDFILE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_UCASTRECEIVERPARTITION:
        MSG = f"{MSG_ERR_UCASTRECEIVERPARTITION} \"{args.pop(0)}\""
    elif CODE == OG_ERR_UCASTRECEIVERFILE:
        MSG = f"{MSG_ERR_UCASTRECEIVERFILE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_MCASTSYNTAXT:
        MSG = f"{MSG_ERR_MCASTSYNTAXT} \"{args.pop(0)}\""
    elif CODE == OG_ERR_MCASTSENDFILE:
        MSG = f"{MSG_ERR_MCASTSENDFILE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_MCASTRECEIVERFILE:
        MSG = f"{MSG_ERR_MCASTRECEIVERFILE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_MCASTSENDPARTITION:
        MSG = f"{MSG_ERR_MCASTSENDPARTITION} \"{args.pop(0)}\""
    elif CODE == OG_ERR_MCASTRECEIVERPARTITION:
        MSG = f"{MSG_ERR_MCASTRECEIVERPARTITION} \"{args.pop(0)}\""
    elif CODE == OG_ERR_PROTOCOLJOINMASTER:
        MSG = f"{MSG_ERR_PROTOCOLJOINMASTER} \"{args.pop(0)}\""
    elif CODE == OG_ERR_DONTMOUNT_IMAGE:
        MSG = f"{MSG_ERR_DONTMOUNT_IMAGE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_DONTUNMOUNT_IMAGE:
        MSG = f"{MSG_ERR_DONTUNMOUNT_IMAGE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_DONTSYNC_IMAGE:
        MSG = f"{MSG_ERR_DONTSYNC_IMAGE} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOTDIFFERENT:
        MSG = f"{MSG_ERR_NOTDIFFERENT} \"{args.pop(0)}\""
    elif CODE == OG_ERR_SYNCHRONIZING:
        MSG = f"{MSG_ERR_SYNCHRONIZING} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOTUEFI:
        MSG = f"{MSG_ERR_NOTUEFI} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOMSDOS:
        MSG = f"{MSG_ERR_NOMSDOS} \"{args.pop(0)}\""
    elif CODE == OG_ERR_NOTBIOS:
        MSG = f"{MSG_ERR_NOTBIOS} \"{args.pop(0)}\""
    else:
        MSG = MSG_ERR_GENERIC
        CODE = OG_ERR_GENERIC

    # Obtener lista de funciones afectadas, incluyendo el script que las llama.
    FUNCS = " ".join(FUNCNAME[1:])
    FUNCS = FUNCS.replace("main", os.path.basename(sys.argv[0]))

    # Mostrar mensaje de error si es función depurable y salir con el código indicado.
    if CODE == OG_ERR_FORMAT or ogCheckStringInGroup(FUNCS, NODEBUGFUNCTIONS) or not ogCheckStringInGroup(FUNCS.split()[0], NODEBUGFUNCTIONS):
        ogEcho(LOGS, "error", f"{FUNCS.replace(' ', '<-')}: {MSG}", file=sys.stderr)

    return CODE

def ogIsRepoLocked():
    # Variables locales
    FILES = ""
    FUNCNAME = ogIsRepoLocked.__name__

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(f"{FUNCNAME}", f"{FUNCNAME}", f"if {FUNCNAME}(): ...")

    # No hacer nada, si no está definido el punto de montaje del repositorio.
    if not OGIMG:
        return 1

    # Comprobar si alguno de los ficheros abiertos por los procesos activos está en el
    # punto de montaje del repositorio de imágenes.
    FILES = subprocess.check_output(["find", "/proc", "-maxdepth", "2", "-type", "f", "-lname", f"{OGIMG}/*"]).decode("utf-8")
    return bool(FILES)

def ogCheckProgram(*args):
    FUNCNAME = ogCheckProgram.__name__
    # Si se solicita, mostrar ayuda.
    if len(args) > 0 and args[0] == "help":
        ogHelp(f"{FUNCNAME} \"str_program ...\"",
                f"{FUNCNAME} \"partimage partclone mbuffer\"")
        return

    # Error si no se recibe 1 parámetro.
    if len(args) != 1:
        ogRaiseError(OG_ERR_FORMAT)
        return

    PERROR = 0
    PLOG = " "
    for i in args[0].split():
        if not shutil.which(i):
            PERROR = 1
            PLOG += f" {i}"
    
    if PERROR == 1:
        ogRaiseError(OG_ERR_NOTEXEC, PLOG)
        return
    else:
        return 0
    
def ogIsVirtualMachine():
    output = subprocess.check_output(["dmidecode", "-s", "system-product-name"]).decode("utf-8")
    if "KVM" in output or "VirtualBox" in output:
        return 1
    else:
        return 0