from CBDMultipleOutput.Source.CBD import BaseBlock
from fsaclasses import Event, RunTimeEvent, State

class FSA_SemanticAdaptation():
    @staticmethod
    def getEventsTriggeredByZeroCrossing(time, fsaBlock, currentState):
        '''
        Returns a list of events scheduled to now that indicate that certain transitions became enabled.
        '''
        events = []
        input_ports = fsaBlock.getLinksIn().keys()
        for t in fsaBlock.fsa.transitions:
            if t.source == currentState and isinstance(t.trigger,WhenVarCrossesThresholdTrigger):
                # check whether the input values presented to the fsaBlock have crossed the thresholds.
                # I don't like short circuiting ands, so I have multiple cascading ifs.
                if t.trigger.varname in input_ports:
                    inputValue = fsaBlock.getInputSignal(None, input_port=t.trigger.varname).value
                    if t.trigger.op == "<":
                        if inputValue < t.trigger.threshold:
                            events.append(RunTimeEvent(t.trigger.eventName,time))
                    elif t.trigger.op == ">":
                        if inputValue > t.trigger.threshold:
                            events.append(RunTimeEvent(t.trigger.eventName,time))
                    else:
                        raise NotImplementedError("Operator not supported.")
        
        if len(events)>1:
            raise NotImplemented("Multiple threshold crossings found. This is non-deterministic: " + events)
        
        print "Threshold crossing events at " + str(time) + " for block " + fsaBlock.getBlockName() + ": " + str(events)
        
        return events
    
    @staticmethod
    def updateOutputFSA(iteration, time, fsaBlock, fsaState):
        '''
        Updates the output port of the fsaBlock with the value representative of the current state.
        '''
        assert isinstance(fsaBlock, FSABlock)
        assert len(fsaBlock.getOutputPorts()) <= 1
        if len(fsaBlock.getOutputPorts()) == 0:
            # Nothing to do
            return 
        
        output_port = fsaBlock.getOutputPorts()[0]
        assert isinstance(fsaState, ValuedState)
        assert len(fsaBlock.getSignal(output_port)) == iteration
        fsaBlock.appendToSignal(fsaState.value, output_port)
        
    
class FSABlock(BaseBlock):
    def __init__(self, name, fsaModel, input_ports, output_ports):
        BaseBlock.__init__(self, name, input_ports, output_ports)
        self.fsa = fsaModel

class WhenVarCrossesThresholdTrigger(Event):
    def __init__(self, varname, threshold, operation):
        self.varname = varname
        self.threshold = threshold
        self.op = operation
        self.eventName = varname+operation+str(threshold)
        Event.__init__(self, self.eventName)

class ValuedState(State):
    def __init__(self, name, value):
        State.__init__(self, name)
        self.value = value
        