# -*- coding: utf-8 -*-
# Description:
# Author: Pawel Krupa (paulfantom)
# Author: Ilya Mashchenko (l2isbad)

import os

from subprocess import Popen, PIPE
from .SimpleService import SimpleService

PATH = os.getenv('PATH', '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin').split(':')


def find_binary(binary):
    """
    :param binary: <str>
    :return:
    """
    for directory in PATH:
        binary_name = '/'.join([directory, binary])
        if os.path.isfile(binary_name) and os.access(binary_name, os.X_OK):
            return binary_name
    return None

class ExecutableService(SimpleService):
    command = None

    def __init__(self):
        SimpleService.__init__(self)
        # self.command = None
        self.command_list = self.parse_command(self.command)

    def _get_raw_data(self, stderr=False):
        """
        Get raw data from executed command
        :return: <list>
        """
        try:
            p = Popen(self.command_list, stdout=PIPE, stderr=PIPE)
        except Exception as error:
            # self.error('Executing command {command} resulted in error: {error}'.format(command=self.command,
            #                                                                            error=error))
            # return None
            raise
        data = list()
        std = p.stderr if stderr else p.stdout
        for line in std:
            try:
                data.append(line.decode('utf-8'))
            except TypeError:
                continue

        return data or None

    def check(self):
        """
        Parse basic configuration, check if command is whitelisted and is returning values
        :return: <boolean>
        """
        try:
            data = self._get_data()
        except Exception as error:
            # self.error('_get_data() failed. Command: {command}. Error: {error}'.format(command=self.command,
            #                                                                            error=error))
            return False

        if data:
            return True

        # self.error('Command "{command}" returned no data'.format(command=self.command))
        return False

    def parse_command(self, command):
        # "command" must be: 1.not None 2. type <str>
        if not (command and isinstance(command, str)):
            # self.error('Command is not defined or command type is not <str>')
            return False

        # Split "command" into: 1. command <str> 2. options <list>
        command, opts = command.split()[0], command.split()[1:]

        # Check for "bad" symbols in options. No pipes, redirects etc.
        opts_list = ['&', '|', ';', '>', '<']
        bad_opts = set(''.join(opts)) & set(opts_list)
        if bad_opts:
            # self.error("Bad command argument(s): {opts}".format(opts=bad_opts))
            return False

        # Find absolute path ('echo' => '/bin/echo')
        if '/' not in command:
            command = find_binary(command)
            if not command:
                # self.error('Can\'t locate "{command}" binary'.format(command=self.command))
                return False
        # Check if binary exist and executable
        else:
            if not os.access(command, os.X_OK):
                # self.error('"{binary}" is not executable'.format(binary=command))
                return False

        command_list = [command] + opts if opts else [command]

        return command_list