AtomicDEVSBlock Initial {
    name = "Initial"

    initialState = """
        self.state, self.elapsed = True, 0.0
    """

    timeAdvance = """
        if self.state:
            return 0.0
        else:
            return float('inf')
    """

    outputFnc = """
        return {self.my_ports['control_out']: {}}
    """

    intTransition = """
        return False
    """

    extTransition = """
        return None
    """

}

OutputPort init_co {
    name = "control_out"
}
DEVSBlockToPort (Initial, init_co) {}

AtomicDEVSBlock Finish {
    name = "Finish"

    initialState = """
        self.state, self.elapsed = None, 0.0
    """

    timeAdvance = """
        return float('inf')
    """

    outputFnc = """
        return {}
    """

    intTransition = """
        return None
    """

    extTransition = """
        return self.elapsed
    """

}

InputPort finish_ci {
    name = "control_in"
}
DEVSBlockToPort (Finish, finish_ci) {}

AtomicDEVSBlock ResourceHandler {
    name = "ResourceHandler"

    initialState = """
        self.state, self.elapsed = {'resources': self.parameters[0], 'queue': []}, 0.0
    """

    timeAdvance = """
        if self.state['queue'] and self.state['resources']:
            # Can grant a resource
            return 0.0
        else:
            # No request queued, or no available resources to handle the request
            return float('inf')
    """

    outputFnc = """
        return {self.my_ports['resource_out']: {'id': self.state['queue'][0]}}
    """

    intTransition = """
        # Processed a request that could be processed
        self.state['resources'] -= 1
        del self.state['queue'][0]
        return self.state
    """

    extTransition = """
        for inp in inputs[self.my_ports['resource_in']]:
            if inp['type'] == 'request':
                # Queue the request
                self.state['queue'].append(inp['id'])
            elif inp['type'] == 'release':
                # Increment the number of available resources
                self.state['resources'] += 1

        return self.state
    """
}

InputPort rh_ri {
    name = "resource_in"
}

OutputPort rh_ro {
    name = "resource_out"
}

DEVSBlockToPort (ResourceHandler, rh_ri) {}
DEVSBlockToPort (ResourceHandler, rh_ro) {}

AtomicDEVSBlock Activity {
    name = "Activity"
    initialState = """
        class ActivityState(object):
            def __init__(self, name, distribution):
                self.timer = float('inf')
                self.mode = 'inactive'
                self.counter = 0
                self.name = name
                self.distribution = distribution

            def __str__(self):
                return str(vars(self))

            def random_sample(self):
                return self.distribution(self.counter)

        self.state, self.elapsed = ActivityState(self.parameters[0], self.parameters[1]), 0.0
        """
    
    timeAdvance = """
        return self.state.timer
        """

    outputFnc = """
        if self.state.mode == 'active':
            # Output the control token to the next model in line, and release the resources
            return {self.my_ports['control_out']: {}, self.my_ports['resource_out']: [{'type': 'release'}]}
        elif self.state.mode == 'request_resource':
            # Output a request for resources with a specified ID (used to find out whether this was our request)
            return {self.my_ports['resource_out']: [{'type': 'request', 'id': '%s-%s' % (self.state.name, self.state.counter)}]}
        else:
            return {}
        """

    intTransition = """
        self.state.timer -= self.timeAdvance()
        if self.state.mode == 'request_resource':
            # Go and request the required resource
            self.state.mode = 'wait_resource'
            self.state.timer = float('inf')
        elif self.state.mode == 'active':
            # Finished execution, so release resources
            self.state.mode = 'inactive'
            self.state.timer = 0.0
            self.state.timer = float('inf')
            self.state.counter += 1
        return self.state
        """

    extTransition = """
        self.state.timer -= self.elapsed
        if self.state.mode == 'inactive' and self.my_ports['control_in'] in inputs:
            # Got control token, so ask for the required resources
            self.state.mode = 'request_resource'
            self.state.timer = 0.0
            # NOTE this violates DEVS, though is easy to debug
            #print('Activate ' + str(self.state.name) + ' at time ' + str(self.time_last[0] + self.elapsed))
        elif self.state.mode == 'wait_resource' and self.my_ports['resource_in'] in inputs and inputs[self.my_ports['resource_in']]['id'] == '%s-%s' % (self.state.name, self.state.counter):
            # Got required resources, so start execution
            self.state.mode = 'active'
            self.state.timer = self.state.random_sample()
        return self.state
        """
}

InputPort act_ri {
    name = "resource_in"
}

OutputPort act_ro {
    name = "resource_out"
}

DEVSBlockToPort (Activity, act_ri) {}
DEVSBlockToPort (Activity, act_ro) {}

InputPort act_ci {
    name = "control_in"
}

OutputPort act_co {
    name = "control_out"
}

DEVSBlockToPort (Activity, act_ci) {}
DEVSBlockToPort (Activity, act_co) {}

AtomicDEVSBlock ParallelSplit {
    name = "ParallelSplit"

    initialState = """
        self.state, self.elapsed = False, 0.0
    """

    timeAdvance = """
        if self.state:
            return 0.0
        else:
            return float('inf')
        """

    outputFnc = """
        return {self.my_ports['control_out']: {}}
        """

    intTransition = """
        return False
        """

    extTransition = """
        return True
        """
}

InputPort split_ci {
    name = "control_in"
}

OutputPort split_co {
    name = "control_out"
}

DEVSBlockToPort (ParallelSplit, split_ci) {}
DEVSBlockToPort (ParallelSplit, split_co) {}

AtomicDEVSBlock Synchronization {
    name = "Synchronization"

    initialState = """
        self.state, self.elapsed = {'current': self.parameters[0], 'max': self.parameters[0]}, 0.0
    """

    timeAdvance = """
        if self.state['current'] == 0:
            return 0.0
        else:
            return float('inf')
        """

    outputFnc = """
        return {self.my_ports['control_out']: {}}
        """

    intTransition = """
        self.state['current'] = self.state['max']
        return self.state
        """

    extTransition = """
        self.state['current'] -= 1
        return self.state
        """
}

InputPort syn_ci {
    name = "control_in"
}

OutputPort syn_co {
    name = "control_out"
}

DEVSBlockToPort (Synchronization, syn_ci) {}
DEVSBlockToPort (Synchronization, syn_co) {}

AtomicDEVSBlock ExclusiveChoice {
    name = "ExclusiveChoice"

    initialState = """
        class ExclusiveChoiceState(object):
            def __init__(self, distribution):
                self.counter = 0
                self.choice = None
                self.distribution = distribution

            def __str__(self):
                return str(vars(self))

            def make_choice(self):
                return self.distribution(self.counter)

        self.state, self.elapsed = ExclusiveChoiceState(self.parameters[0]), 0.0
        """

    timeAdvance = """
        if self.state.choice is not None:
            return 0.0
        else:
            return float('inf')
        """

    outputFnc = """
        if self.state.choice == True:
            return {self.my_ports['control_out1']: {}}
        else:
            return {self.my_ports['control_out2']: {}}
        """

    intTransition = """
        self.state.choice = None
        self.state.counter += 1
        return self.state
        """

    extTransition = """
        # Got a control token, so have to make a choice
        self.state.choice = self.state.make_choice()
        return self.state
        """
}

InputPort xor_ci {
    name = "control_in"
}

OutputPort xor_co1 {
    name = "control_out1"
}

OutputPort xor_co2 {
    name = "control_out2"
}

DEVSBlockToPort (ExclusiveChoice, xor_ci) {}
DEVSBlockToPort (ExclusiveChoice, xor_co1) {}
DEVSBlockToPort (ExclusiveChoice, xor_co2) {}

AtomicDEVSBlock SimpleMerge {
    name = "SimpleMerge"

    initialState = """
        self.state, self.elapsed = False, 0.0
    """

    timeAdvance = """
        if self.state:
            return 0.0
        else:
            return float('inf')
        """

    outputFnc = """
        return {self.my_ports['control_out']: {}}
        """

    intTransition = """
        return False
        """

    extTransition = """
        return True
        """
}

InputPort or_ci {
    name = "control_in"
}

OutputPort or_co {
    name = "control_out"
}

DEVSBlockToPort (SimpleMerge, or_ci) {}
DEVSBlockToPort (SimpleMerge, or_co) {}

AtomicDEVSBlock MultiInstance {
    name = "MultiInstance"

    initialState = """
        class MultiInstanceState(object):
            def __init__(self, name, num, distribution):
                self.spawned = num
                self.collected = 0
                self.counter = 0
                self.mode = 'inactive'
                self.running_tasks = []
                self.requested = []
                self.name = name
                self.distribution = distribution

            def __str__(self):
                return str(vars(self))

            def task_time(self):
                return self.distribution(self.counter)

        self.state, self.elapsed = MultiInstanceState(self.parameters[0], self.parameters[1], self.parameters[2]), 0.0
        """

    timeAdvance = """
        if self.state.mode == 'finish':
            return 0.0
        elif self.state.running_tasks:
            return self.state.running_tasks[0][0]
        elif self.state.mode == 'request_resources':
            return 0.0
        else:
            return float('inf')
        """

    outputFnc = """
        if self.state.mode == 'request_resources':
            # Request all resources in one go
            return {self.my_ports['resource_out']: [{'type': 'request', 'id': i} for i in self.state.requested]}
        elif self.state.mode == 'active':
            # Finished an instance, so release it
            return {self.my_ports['resource_out']: [{'type': 'release'}]}
        elif self.state.mode == 'finish':
            # Finished execution of all, so pass on token
            return {self.my_ports['control_out']: {}}
        else:
            return {}
        """

    intTransition = """
        ta = self.timeAdvance()
        for t in self.state.running_tasks:
            t[0] -= ta

        if self.state.mode == 'active':
            # Finished an instance, so pop it
            del self.state.running_tasks[0]
            self.state.collected += 1
            if self.state.collected == self.state.spawned:
                self.state.mode = 'finish'
        elif self.state.mode == 'request_resources':
            # Requested resources, so be ready for a response
            self.state.mode = 'active'
        elif self.state.mode == 'finish':
            self.state.mode = 'inactive'
            self.state.collected = 0
            self.state.counter += 1
            self.state.running_tasks = []
        return self.state
        """

    extTransition = """
        # Got input, so have to spawn #num of them
        for t in self.state.running_tasks:
            t[0] -= self.elapsed

        if self.state.mode == 'inactive' and self.my_ports['control_in'] in inputs:
            self.state.mode = 'request_resources'
            self.state.requested = ['%s-%s-%s' % (self.state.name, self.state.counter, i) for i in range(self.state.spawned)]

        if self.state.mode in ['active', 'release_resource'] and self.my_ports['resource_in'] in inputs:
            # Got a resource, so allocate it to an activity
            self.state.running_tasks.append([self.state.task_time(), self.state.requested.pop(0)])
            # NOTE this violates DEVS, though is easy to debug
            #print('Spawn ' + str(self.state.running_tasks[-1][1]) + ' at time ' + str(self.time_last[0] + self.elapsed))
            self.state.running_tasks.sort()
        return self.state
        """
}

InputPort mi_ci {
    name = "control_in"
}

OutputPort mi_co {
    name = "control_out"
}

InputPort mi_ri {
    name = "resource_in"
}

OutputPort mi_ro {
    name = "resource_out"
}

DEVSBlockToPort (MultiInstance, mi_ci) {}
DEVSBlockToPort (MultiInstance, mi_co) {}
DEVSBlockToPort (MultiInstance, mi_ri) {}
DEVSBlockToPort (MultiInstance, mi_ro) {}
