import logging
import asyncio

from ..abc.source import Source

L = logging.getLogger(__name__)


class SubProcessSource(Source):
    """
    Sub-Process Source is capable of calling any command and fetch result from stdin as event

    Can be useful with commands like `tail -f`, `tshark -l -n -T ek -i wlan0` or others
    """

    ConfigDefaults = {
        "command": "",
        "line_len_limit": 2**20,
        "ok_return_codes": "0",  # Multiple codes must be separated by commas, e.g. 'ok_return_codes': '0,1,2',
    }

    def __init__(self, app, pipeline, *, id=None, config=None):
        super().__init__(app, pipeline, id=id, config=config)

        self.Command = str(self.Config["command"])
        assert self.Command, "`command` not set on " + self.__class__.__name__
        self._process = None
        # Get the list of OK return codes
        self.OKReturnCodes = [
            int(i.strip()) for i in self.Config["ok_return_codes"].split(",")
        ]

    async def main(self):
        self._process = await asyncio.create_subprocess_shell(
            self.Command,
            shell=True,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.DEVNULL,
            limit=self.Config.get("line_len_limit"),
        )
        # Check if process is running
        while self._process.returncode is None:
            await self.Pipeline.ready()
            event = await self._process.stdout.readline()
            await self.process(event)
        # Error message, when process has been terminated
        if (
            self._process.returncode not in self.OKReturnCodes
            and self._process.returncode is not None
        ):
            # Print error, wait a bit and retry again
            L.error(
                "Command {} has exited with return code: {}".format(
                    self.Command, self._process.returncode
                )
            )
            await asyncio.sleep(5)
