from procgraph import Generator, Block, ModelExecutionError, register_model_spec, logger
from procgraph.core.registrar import default_library
from procgraph.testing.utils import PGTestCase


examples = [
    (
        "If coincident, output that",
        """
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
""",
    ),
    (
        "Skip additional",
        """
1 2 3 4 5 6 7 8 9
1*2*3*4*5*6*7*8*9
1 2 3 4 5 6 7 8 9
""",
    ),
    (
        "Wait for the next.",
        """
1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9
""",
    ),
    (
        "Skip first one if necessary",
        """
 1 2 3 4 5 6 7 8 9
* 1 2 3 4 5 6 7 8 9
* 1 2 3 4 5 6 7 8 9
""",
    ),
    (
        "Wait for the next  (multiple delay).",
        """
1   2   3   4   5   6   7   8   9
 1   2   3   4   5   6   7   8   9
  1   2   3   4   5   6   7   8   9
""",
    ),
    (
        "Skip master if signals don't arrive .",
        """
1  *2  *3  **4***5**6   7   8   9
 1   2   3   4   5   6   7   8   9
 1   2   3   4   5   6   7   8   9
""",
    ),
    (
        "Skip slave if other slave doesn't arrive.",
        """
1   2   3   4   5   6   7   8   9
 1*  2   3   4   5   6   7   8   9
  1   2   3   4   5   6   7   8   9
""",
    ),
    (
        "Skip slave if other slave doesn't arrive.",
        """
* 1   2   3   4   9
 1   *   2   3   4   9
   1   2   3   4   9
""",
    ),
]

register_model_spec(
    """
--- model sync_test

|log_sim line=$master| -> master
|log_sim line=$slave1| -> slave1
|log_sim line=$slave2| -> slave2

master, slave1, slave2 -> |sync2| -> |check_sync| --> |output name=value|

"""
)


class LogSim(Generator):
    Block.alias("log_sim")

    Block.output("stream")

    def init(self):
        line = self.config.line

        self.queue = []
        for i, char in enumerate(line):
            t = i + 1
            if char == " ":
                continue
            elif char == "*":
                self.queue.insert(0, (t, -1))
            elif char.isdigit():
                v = int(char)
                self.queue.insert(0, (t, v))
            else:
                raise Exception('Bad input "%s" at char %d of "%s".' % (char, t, line))

    def next_data_status(self):
        if self.queue:
            return (True, self.queue[-1][0])
        else:
            return (False, None)

    def update(self):
        assert self.queue
        timestamp, value = self.queue.pop()
        self.set_output(0, value, timestamp)


class CheckSync(Block):
    Block.alias("check_sync")
    Block.output("value")

    def update(self):

        values = self.get_input_signals_values()
        timestamps = self.get_input_signals_timestamps()

        ok = True
        for v in values:
            if v is None or v != values[0]:
                ok = False
        for t in timestamps:
            if t is None or t != timestamps[0]:
                ok = False

        if not ok:
            msg = "Received bad inputs. Timestamps: %s Values: %s " % (timestamps, values)

            raise ModelExecutionError(msg, self)

        self.output.value = values[0]


class TestSync(PGTestCase):
    def not_working(self):

        for description, example in examples:
            logger.info("Trying with: %s" % description)
            # get non-empty lines
            lines = list([x for x in example.split("\n") if x.strip()])
            logger.info(lines=lines)
            assert len(lines) == 3
            config = {"master": lines[0], "slave1": lines[1], "slave2": lines[2]}

            model = default_library.instance("sync_test", name=None, config=config)

            model.init()
            while model.has_more():
                model.update()
            model.finish()

            final = model.get_output(0)
            if final != 9:
                raise Exception("expected final output to be 9, not %s" % final)
