# Path: planner/operators.py
# def derive_predicates(state, axioms):
#   if not axioms:
#     return state, []
#   is_strips = 'strips' in axioms[0].__class__.__module__ # TODO: clean this up
#   if is_strips:
#     from strips.operators import derive_state
#     return derive_state(state, axioms)
#   return derive_predicates_slow(state, axioms)
# 
# class MacroOperator(object):
#   def __init__(self, *operators):
#     self.operators = tuple(operators)
#     self.cost = sum(operator.cost for operator in operators)
#   def __contains__(self, state):
#     return self(state) is not None
#   def __call__(self, state):
#     states = [state]
#     for operator in self.operators:
#       new_state = operator(states[-1])
#       if new_state is None: return None
#       states.append(new_state)
#     return states
#   def __iter__(self):
#     return (operator for operator in self.operators)
#   def __len__(self):
#     return len(self.operators)
#   def __eq__(self, other):
#     if type(self) != type(other): return False
#     return self.operators == other.operators
#   def __ne__(self, other):
#     return not self == other
#   def __hash__(self):
#     return hash((self.__class__, self.operators))
#   def __str__(self):
#     return self.__class__.__name__ + str_object(self.operators)
#   __repr__ = __str__
# 
# Path: misc/functions.py
# def elapsed_time(start_time):
#   return time.time() - start_time
# 
# def safe_remove(sequence, item):
#   try:
#     sequence.remove(item)
#     return True
#   except ValueError:
#     return False
# 
# def implies(a, b):
#   return not a or b
# 
# Path: misc/numerical.py
# INF = float('inf')
# 
# Path: misc/objects.py
# def str_line(*args):
#   return ''.join(str_object(item) for item in args)


from collections import namedtuple
from .operators import derive_predicates, MacroOperator
from misc.functions import elapsed_time, safe_remove, implies
from misc.numerical import INF
from misc.objects import str_line
import time

def test_parent_operator(sink_vertex):
    parent_edge = sink_vertex.parent_edge
    if parent_edge is None:
        return True
    return parent_edge.evaluate_test()

class Vertex(object):
    def __init__(self, state, state_space):
        self.state = state
        self.derived_state, self.axiom_plan = derive_predicates(state, state_space.axioms)
        self.state_space = state_space
        self.generator = state_space.generator_fn(self)
        self.operators = []
        self.incoming_edges = []
        self.outgoing_edges = []
        self.extensions = 0 # TODO: deprecate
        self.generations = 0
        self.explored = 0
        self.h_cost = None
        self.reset_path()
    # @property
    # def extensions(self):
    #     return len(self.outgoing_edges)
    @property
    def num_unexplored(self):
        #return self.extensions - self.explored
        return len(self.outgoing_edges) - self.explored
    def reset_path(self):
        self.cost = INF # TODO: path_cost

# Path: planner/operators.py
# def derive_predicates(state, axioms):
#   if not axioms:
#     return state, []
#   is_strips = 'strips' in axioms[0].__class__.__module__ # TODO: clean this up
#   if is_strips:
#     from strips.operators import derive_state
#     return derive_state(state, axioms)
#   return derive_predicates_slow(state, axioms)
# 
# class MacroOperator(object):
#   def __init__(self, *operators):
#     self.operators = tuple(operators)
#     self.cost = sum(operator.cost for operator in operators)
#   def __contains__(self, state):
#     return self(state) is not None
#   def __call__(self, state):
#     states = [state]
#     for operator in self.operators:
#       new_state = operator(states[-1])
#       if new_state is None: return None
#       states.append(new_state)
#     return states
#   def __iter__(self):
#     return (operator for operator in self.operators)
#   def __len__(self):
#     return len(self.operators)
#   def __eq__(self, other):
#     if type(self) != type(other): return False
#     return self.operators == other.operators
#   def __ne__(self, other):
#     return not self == other
#   def __hash__(self):
#     return hash((self.__class__, self.operators))
#   def __str__(self):
#     return self.__class__.__name__ + str_object(self.operators)
#   __repr__ = __str__
# 
# Path: misc/functions.py
# def elapsed_time(start_time):
#   return time.time() - start_time
# 
# def safe_remove(sequence, item):
#   try:
#     sequence.remove(item)
#     return True
#   except ValueError:
#     return False
# 
# def implies(a, b):
#   return not a or b
# 
# Path: misc/numerical.py
# INF = float('inf')
# 
# Path: misc/objects.py
# def str_line(*args):
#   return ''.join(str_object(item) for item in args)


# Path: planner/state_space.py
from collections import namedtuple
from .operators import derive_predicates, MacroOperator
from misc.functions import elapsed_time, safe_remove, implies
from misc.numerical import INF
from misc.objects import str_line
import time


def test_parent_operator(sink_vertex):
    parent_edge = sink_vertex.parent_edge
    if parent_edge is None:
        return True
    return parent_edge.evaluate_test()

class Vertex(object):
    def __init__(self, state, state_space):
        self.state = state
        self.derived_state, self.axiom_plan = derive_predicates(state, state_space.axioms)
        self.state_space = state_space
        self.generator = state_space.generator_fn(self)
        self.operators = []
        self.incoming_edges = []
        self.outgoing_edges = []
        self.extensions = 0 # TODO: deprecate
        self.generations = 0
        self.explored = 0
        self.h_cost = None
        self.reset_path()
    # @property
    # def extensions(self):
    #     return len(self.outgoing_edges)
    @property
    def num_unexplored(self):
        #return self.extensions - self.explored
        return len(self.outgoing_edges) - self.explored
    def reset_path(self):
        self.cost = INF # TODO: path_cost

        self.length = INF # TODO: path_length