"""Remove nodes whose output is not used."""

from __future__ import unicode_literals, absolute_import, print_function
from iotile.core.exceptions import ArgumentError
from iotile.sg.node import TrueTrigger, FalseTrigger, InputTrigger
from iotile.sg import DataStreamSelector
from iotile.sg.parser.stream_allocator import StreamAllocator


class RemoveDeadCodePass(object):
    """Remove nodes whose operation is discarded and has no side effects.

    An operation has no side effects if it is not a call_rpc operation
    (currently) since no one outside the sensorgraph engine knows if
    the operation was run or not.  It's output is discarded if its stream
    is autogenerated by the compiler and it is not connected to anything.

    Args:
        sensor_graph (SensorGraph): The sensor graph to run
            the optimization pass on
    """

    def __init__(self):
        pass

    def run(self, sensor_graph, model):
        """Run this optimization pass on the sensor graph

        If necessary, information on the device model being targeted
        can be found in the associated model argument.

        Args:
            sensor_graph (SensorGraph): The sensor graph to optimize
            model (DeviceModel): The device model we're using
        """

        # We can only eliminate a node if the following checks are true
        # 1. It has no other nodes connected to it
        # 2. Its stream is not an output of the entire sensor graph
        # 3. Its stream is autogenerated by the compiler
        # 4. Its operation has no side effects
        # 5. Its stream is not buffered so the value will not be accessible

        for node, inputs, outputs in sensor_graph.iterate_bfs():
            can_remove = False

            # Check 1
            if len(outputs) != 0:
                continue

            # Check 2
            if sensor_graph.is_output(node.stream):
                continue

            # Check 3
            if node.stream.stream_id < StreamAllocator.StartingID:
                continue

            # Check 4
            if node.func_name == u'call_rpc':
                continue

            # Check 5
            if node.stream.buffered:
                # FIXME: Add a warning here if the stream is buffered since
                # its weird for the user to be saving useless data to flash
                continue

            # If all of the checks above have passed, we have found a useless
            # node, let's remove it and return True so we run the pass again
            # and look for additional nodes that are now made useles because
            # of the removal of this one.
            for input_node in inputs:
                input_node.outputs.remove(node)

            if node in sensor_graph.roots:
                sensor_graph.roots.remove(node)

            sensor_graph.nodes.remove(node)

            # FIXME: Check if we need to destroy any walkers here

            return True

        return False
