"""
Handles each individual test/button. Every time a command
is sent to the communication pipe, it should be one
complete, individual test. One test can implement multiple
steps and will determine if one step needs to be left running.

| author     = "Seth Miers"
| copyright  = "Copyright, Xilinx Inc. All rights reserved."
| credits    = ["Seth Miers"]
| license    = "GPL"
| version    = "3.0"
| maintainer = "Seth Miers"
| email      = "sethm@xilinx.com"
| status     = "Development"


"""

##########################################
#               Imports                  #
##########################################

# --------------Packages---------------- #
import threading
import ruamel.yaml

# --------------Local------------------- #
import step
import passfail


##########################################
#              Main Code                 #
##########################################

class Test(passfail.PassFail):
    '''
    The Test class is the entry point to all executor methods.
    This class will parse the YAML that is sent to it by
    the communication pipe and execute a single test by
    running individual steps and determining pass/fail
    criteria.

    Args:
        test_config (ordereddict): The yaml config that defines
            how the test should be run
        steps (dict): A dictionary of all the steps
            being run.
        stop (bool): the stop variable in case the program
            needs to exit.
        test_name (string): the name of the test that is being
            run

    Todo:
        * Basically everything.
    '''
    def __init__(self, test_config):
        passfail.PassFail.__init__(self)
        self.test_config = test_config
        self.stop = False
        self.steps = dict()
        self.test_name = ''

    def run_test(self):
        '''
        Gather all of the information about the test
        that is about to run so we can collect data
        on the test as it runs

        Todo:
            * fill in logger comments
        '''

        if len(self.test_config) != 1:
            return
            # logger error here
        self.test_name = str(list(TESTCONFIG.keys())[0])

        self.run_steps()

    def run_steps(self):
        '''
        Run each step in order as defined by the test_config.
        This step creates the step, manages the thread, and
        waits for the step to exit before moving on to the next
        step.

        Attributes:
            step_type (string): The type of test being run.
            run_step (step): The current step being executed.

        Todo:
            * everytime I do 'get' for a yaml thing, I need to have
              a defult of an exception handle of some kind
            * why is the self.steps dictionary needed?
        '''
        # Every test has a bunch of steps, those are parsed out here
        print(type(self.test_config))
        for index, step_config in enumerate(
                self.test_config.get(self.test_name,
                                     ruamel.yaml.comments.CommentedMap()).get(
                                         'steps', {})):
            if self.stop:
                break
            step_type = step_config.get('type', '').lower()
            pass_conditions = step_config.get('pass condition', '')
            fail_conditions = step_config.get('fail condition', '')
            if index not in self.steps.keys():
                if step_type in ["uart", "serial"]:
                    # creating new step
                    port_regex = step_config.get('port', '')
                    print(str(port_regex))
                    run_step = step.Serial(pass_conditions=pass_conditions,
                                           fail_conditions=fail_conditions,
                                           port_regex=port_regex)
            else:
                # getting existing step
                run_step = self.steps.get(index)
                run_step.reset_step()
            self.steps.update({index: run_step})
            run_step_thread = threading.Thread(target=run_step.run_step,
                                               name='run_step')
            run_step_thread.setDaemon(True)
            run_step_thread.start()
            while run_step_thread.isAlive():
                if self.stop:
                    break
            run_step.stop_step()
            if self.stop:
                break

    def stop_steps(self):
        '''
        Stop everything from running by setting the stop variable.
        '''
        self.stop = True


##########################################
#             Unit Testing               #
##########################################

if __name__ == "__main__":
    TESTYAML = 'Test Yaml:\n  steps:\n  - type: uart\n    ' + \
            'command: Testing\n    port: COM2->COM1\n    pass condition:' +\
            '\n    - pass\n  ui:\n    progress: 40'
    YAML = ruamel.yaml.YAML()
    TESTCONFIG = YAML.load(TESTYAML)
    X = Test(TESTCONFIG)
    X.run_test()
