# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial

from typing import List, Optional

from PySide6.QtCore import QProcess, Signal, Slot
from PySide6.QtWidgets import QPlainTextEdit, QWidget

from utils import qprocess_output


class ProcessView(QPlainTextEdit):
    """A QPlainTextEdit logging QProcess output. It can execute a batch of
       commands like qmake/make."""

    finished = Signal(int, QProcess.ExitStatus)

    def __init__(self, parent: Optional[QWidget] = None):
        super(ProcessView, self).__init__(parent)
        self.setReadOnly(True)
        self._process = QProcess(self)
        self._process.setProcessChannelMode(QProcess.MergedChannels)
        self._process.readyReadStandardOutput.connect(self._stdOut)
        self._process.finished.connect(self._finished)

    def is_running(self) -> bool:
        return self._process.state() != QProcess.NotRunning

    def start(self, commands: List[List[str]], work_dir: str) -> bool:
        """Start a batch (list of argument lists) of commands)"""
        if self.is_running():
            return False
        self.setPlainText('')
        if work_dir:
            self._process.setWorkingDirectory(work_dir)
        self._commands = commands
        return self._start_next()

    def _start_next(self) -> bool:
        if not self._commands:
            return False
        command = self._commands[0]
        self._commands = self._commands[1:]
        binary = command[0]
        arguments = command[1:]
        command_string = binary + ' ' + ' '.join(arguments)
        self.appendPlainText(f'Running: {command_string}\n\n')
        self._process.start(binary, arguments)
        if not self._process.waitForStarted():
            error = self._process.errorString()
            self.appendPlainText(f'Error starting {command_string}: {error}')
            return False
        return True

    @Slot()
    def _stdOut(self) -> None:
        o = self._process.readAllStandardOutput()
        self.appendPlainText(qprocess_output(o))

    @Slot()
    def _finished(self, code: int, state: QProcess.ExitStatus) -> None:
        binary = self._process.program()
        crashed = state != QProcess.NormalExit
        failed = code != 0
        if crashed:
            self.appendPlainText(f'{binary} crashed\n')
        elif failed:
            self.appendPlainText(f'{binary} returned {code}\n')
        else:
            self.appendPlainText(f'{binary} succeeded\n')
        if crashed or failed or not self._start_next():
            self.finished.emit(code, state)
