#!/usr/bin/python3
import os
import subprocess
import logging

from pathlib import Path
from typing import Tuple, Optional

from utils.ecplogging import SystemdLogMask

# Pipe definitions
PIPE_PATH = "/tmp/gps-data"
PIPE_UID = 0
PIPE_GID = 1000
PIPE_PERMISSION = 0o660

# Command definitions
QMICLI = "qmicli"
CDC_WDM = "--device=/dev/cdc-wdm0"
PROXY = "--device-open-proxy"
LOC_START = "--loc-start"
LOC_REPORT = "--loc-get-position-report"
EXEC_LOC_START = f"{QMICLI} {CDC_WDM} {PROXY} {LOC_START}"
EXEC_LOC_REPORT = f"{QMICLI} {CDC_WDM} {PROXY} {LOC_REPORT}"
EXEC_COMMAND_CHAIN = [EXEC_LOC_START, EXEC_LOC_REPORT]

# Other definitions
SUCCESS = True
NO_SUCCESS = False
EXIT_SUCCESS = 0
EXIT_NO_SUCCESS = 1
WRITE_MODE = "a"
WWAN_DRIVER = "/dev/cdc-wdm0"
LOG_NAME = "gpsService"

# Expectations
QMI_GPS_GATHERING_STARTED = "Successfully started location tracking"

# Messages
EXCEPTION_OCCURED = "Exception occured, Service failed!"
ERROR_OCCURED = "Error occured, Service failed!"

# Functions
def createPipe(pipePath: str) -> bool:
    """
    Creates a pipe and return the success status.

    :param pipePath: The path where the pipe will live.
    :return: True if succeeded and vice versa.
    """
    logger.debug(f"creating pipe")
    try:
        os.mkfifo(pipePath, PIPE_PERMISSION)
        os.chown(pipePath, PIPE_UID, PIPE_GID)
        return SUCCESS
    except OSError as error:
        logger.debug(f"creating pipe failed: {error}")
        return NO_SUCCESS


def writeIntoPipe(pipePath: str, message: str) -> bool:
    """
    Writes into the pipe the given message.

    :param pipePath: The path where the pipe will live.
    :param message: The message for write into the pipe.
    :return: True if succeeded and vice versa.
    """
    logger.debug("write to pipe")
    try:
        pipe = open(pipePath, WRITE_MODE)
        pipe.write(message)
        pipe.close()
        return SUCCESS
    except FileExistsError or PermissionError as error:
        logger.debug(f"write to pipe failed: {error}")
        return NO_SUCCESS


def executeCommand(command: str) -> Tuple[bool, str]:
    """
    Executes the command in a shell from a subprocess.

    :param command: The command it should be executed.
    :return: True if succeeded and vice versa. Also the message retrieved from the subprocess.
    """
    logger.debug(f"execute command: {command}")
    output, error, returnStatusCode = _executeInSubprocess(command)

    if (error is None or "") and (returnStatusCode == EXIT_SUCCESS):
        message = output
        success = SUCCESS
    else:
        message = error or ERROR_OCCURED
        success = NO_SUCCESS
    logger.debug(f"execution has returned:\noutput: {output}\nerror: {error}\nreturnStatusCode: {returnStatusCode}")
    return success, message


def _executeInSubprocess(command: str) -> Tuple[Optional[str], Optional[str], int]:
    """
    Opens a subprocess and executes the command in a shell.

    :param command: The command it should be executed.
    :return: The output and error from the subshell and returnStatusCode 0 if succeeded and vice versa.
    """
    logger.debug("execute in subprocess")
    try:
        process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, encoding="utf-8")

        (output, error) = process.communicate()
        returnStatusCode = process.wait()

    except OSError as error:
        logger.debug(f"execute in subprocess failed: {error}")
        output = None
        error = EXCEPTION_OCCURED
        returnStatusCode = EXIT_NO_SUCCESS

    return output, error, returnStatusCode


if __name__ == "__main__":
    SystemdLogMask.setSystemdLogClass()
    logger = logging.getLogger(LOG_NAME)
    # Check if WWAN interface is ready
    if not Path(WWAN_DRIVER).exists():
        logger.warning("QMI port not found")
        exit(EXIT_NO_SUCCESS)

    # Check and create fifo pipe
    if not Path(PIPE_PATH).exists():
        statusPipe = createPipe(PIPE_PATH)
    else:
        statusPipe = SUCCESS

    if not statusPipe:
        logger.warning("Pipe could not be created")
        exit(EXIT_NO_SUCCESS)

    # Start command sequence
    for command in EXEC_COMMAND_CHAIN:
        success, message = executeCommand(command)
        successPipeWrite = writeIntoPipe(PIPE_PATH, message)

        if NO_SUCCESS in [success, successPipeWrite]:
            logger.warning(f"A shell command has failed messages: \n{success}\n{message}\n{successPipeWrite}")
            exit(EXIT_NO_SUCCESS)

    exit(EXIT_SUCCESS)
