"""
How do we implement an API for making modules that build
a GSPN and then run the transitions within?
"""
import logging


logger=logging.getLogger(__file__)


class Place:
    """
    A place has a unique key and holds tokens.
    """
    def __init__(self, key):
        self.key=key
        self.tokens=list()


class TokenFlow:
    """
    A TokenFlow represents movement of tokens among places to
    and from a transition. It encapsulates
    1. stoichiometry
    2. policy of which token is taken (first, last, random, specific)
    3. policy of how and whether new tokens are created
    """
    def __init__(self, take_edge, take_cnt, give_edge, give_cnt):
        self.take_edge=take_edge
        self.take_cnt=take_cnt
        self.give_edge=give_edge
        self.give_cnt=give_cnt
    def fire(self, localstate):
        take=list()
        for i in range(f.take_cnt):
            take.append(localstate[f.take_edge].pop())
        for j in range(f.give_cnt):
            if length(take)>0:
                localstate[f.give_edge].append(take.pop())

class TransitionModifier:
    """
    A TransitionModifier is part of a transition used to change
    its enabling or hazard rate.
    """
    def __init__(self, token_flows):
        self.flows=token_flows

    def places(self):
        return None

    def enabled(self, globalstate, localstate, offset, te, t0, rng):
        """
        The offset is an integer offset into which integer-indexed
        part of the local state corresponds to places required by
        this TransitionModifier.
        """
        return False

    def hazard_modifier(self):
        return 1.0


class Transition:
    """
    Transition keys aren't unique.
    """
    def __init__(self, key, token_flows, stochastic_variable):
        self.key=key
        self.flows=token_flows
        self.stochastic=stochastic_variable

    def enabled(self, globalstate, localstate, t0, rng):
        """
        globalstate would have the scenario.
        localstate is an integer-indexed set of edges to local marking.
        t0 is current time.
        rng is random number generator
        """
        return False

    def fire(self, globalstate, localstate, t0, rng):
        """
        globalstate would have the scenario.
        localstate is an integer-indexed set of edges to local marking.
        t0 is current time.
        rng is random number generator
        """
        pass



class StoichiometricTransition:
    """
    This transition enforces stoichiometric coefficients and no more.
    But there may be more than one token type, each with different
    stoichiometry, expressed in a different flow.
    When enabled() is called, if the transition was already enabled,
    it compares the current invariant with the previous value to 
    determine whether to set a new te.
    """
    def __init__(self, key, token_flows, stochastic_variable):
        """
        key is the non-unique id for this transition.
        token_flows is a list of TokenFlows.
        stochastic_variable is a stochastic variable.
        """
        self.key=key
        self.flows=token_flows
        self.stochastic=stochastic_variable

    def enabled(self, globalstate, localstate, t0, rng):
        """
        globalstate would have the scenario.
        localstate is an integer-indexed set of edges to local marking.
        te is enabling time.
        t0 is current time.
        rng is random number generator
        returns distribution, and an invariant.
        """
        for f in self.flows:
            if length(localstate[f.take_edge])<f.take_cnt:
                return None

        return (self.stochastic.build(te), [])

    def fire(self, globalstate, localstate, t0, rng):
        """
        globalstate would have the scenario.
        localstate is an integer-indexed set of edges to local marking.
        t0 is current time.
        rng is random number generator
        """
        for f in self.flows:
            f.fire(localstate)


class ModifiedTransition(Transition):
    """
    This is a transition that has been modified by TransitionModifier objects.
    """
    def __init__(self, base_transition):
        self.base_transition=base_transition
        self.modifiers=list()
        self.key=self.base_transition.key
        self.flows=base_transition.flows

    def enabled(self, globalstate, localstate, te, t0, rng):
        b_enabled=self.base_transition.enabled()
        for m in self.modifiers:
            if not m.enabled(globalstate, localstate, te, t0, rng):
                b_enabled=False
        modifier=1.0
        for m in self.modifiers:
            modifier*=m.hazard_modifier(globalstate, localstate, t0, rng)
        hazard=base_transition.hazard(globalstate, localstate, t0, rng, modifier)
        return hazard, b_enabled

    def fire(self, globalstate, localstate, t0, rng):
        pass


class GSPN:
    """
    A GSPN is a bipartite graph between places and transitions
    and a directed dependency graph for transitions.
    Places have unique keys. Transitions have non-unique keys.