#!/usr/bin/python3
# coding=utf-8

# Copyright (C) 2020, Siemens Healthcare Diagnostics Products GmbH
# Licensed under the Siemens Inner Source License 1.2, see LICENSE.md.

import subprocess

from typing import Optional, List

import bbutil

__all__ = [
    "Execute"
]


class Execute(object):

    def __init__(self, **kwargs):
        self.name: str = ""
        self.desc: str = ""
        self.commands: List[str] = []
        self.messages: List[str] = []
        self.returncode: int = 0
        self.errors: Optional[List[str]] = None

        self.stdout: Optional[subprocess.PIPE] = subprocess.PIPE
        self.stderr: Optional[subprocess.PIPE] = subprocess.PIPE
        self.stdin: Optional[subprocess.PIPE] = None
        self.call_stdout = None
        self.call_stderr = None

        self.setup(**kwargs)
        return

    def show_command(self):
        line = ""

        for _item in self.commands:
            if line == "":
                line = _item
            else:
                line = "{0:s} {1:s}".format(line, _item)

        bbutil.log.debug1(self.name, line)
        return

    @staticmethod
    def _convert_line(data) -> str:
        try:
            new = data.decode(encoding='utf-8')
        except UnicodeDecodeError:
            return ""
        new = new.replace("\r\n", "")
        new = new.replace("\n", "")
        return new

    def setup(self, **kwargs):
        item = kwargs.get("commands", None)
        if item is not None:
            self.commands = item

        item = kwargs.get("name", None)
        if item is not None:
            self.name = item

        item = kwargs.get("desc", None)
        if item is not None:
            self.desc = item

        item = kwargs.get("stdout", None)
        if item is not None:
            self.stdout = item

        item = kwargs.get("stderr", None)
        if item is not None:
            self.stderr = item

        item = kwargs.get("stdin", None)
        if item is not None:
            self.stdin = item

        item = kwargs.get("call_stdout", None)
        if item is not None:
            self.call_stdout = item

        item = kwargs.get("call_stderr", None)
        if item is not None:
            self.call_stderr = item
        return

    def execute(self) -> bool:
        self.show_command()

        self.messages = []

        if self.stdin is not None:
            p = subprocess.Popen(self.commands, stdout=self.stdout, stderr=self.stderr, stdin=self.stdin)
        else:
            p = subprocess.Popen(self.commands, stdout=self.stdout, stderr=self.stderr)

        #  parse output and wait for end
        while True:
            if p.stdout is not None:
                for line in p.stdout:
                    if line is None:
                        continue
                    data = self._convert_line(line)
                    self.messages.append(data)

                    if self.call_stdout is not None:
                        self.call_stdout(data)

            if p.stderr is not None:
                for line in p.stderr:
                    if line is None:
                        continue
                    data = self._convert_line(line)

                    if self.errors is None:
                        self.errors = []

                    self.errors.append(data)

                    if self.call_stderr is not None:
                        self.call_stderr(data)

            _poll = p.poll()
            if _poll is not None:
                break

        self.returncode = p.returncode

        if p.returncode != 0:
            bbutil.log.clear()
            bbutil.log.error("{0:s} failed!".format(self.desc))
            bbutil.log.error("Process did end with error {0:d}".format(p.returncode))
            if self.errors is not None:
                for _line in self.errors:
                    bbutil.log.error(_line)
            else:
                for _line in self.messages:
                    bbutil.log.error(_line)
            return False

        return True
