# Path: misc/utils.py
# SEPARATOR = '\n'+85*'-'+'\n'
# def arg_info(frame, ignore=['self']):
# def function_name(stack): # NOTE - stack = inspect.stack()
# def get_path(frame):
# def get_directory(abs_path):
# def global_fn(name):
# def refresh(module_name):
# 
# Path: sas/downward.py
# class Problem(object):
#   default_val = False
#   def __init__(self, initial, goal, actions, axioms):
#     self.var_indices = {}
#     self.var_order = []
#     self.var_val_indices = {}
#     self.var_val_order = {}
#     self.axioms = axioms
#     self.mutexes = []
#     self.costs = True
# 
#     self.initial = initial
#     for var, val in initial.values.items():
#       self.add_val(var, val)
#     self.goal = goal
#     for var, val in goal.conditions.items():
#       self.add_val(var, val)
# 
#     self.actions = actions
#     for action in self.actions:
#       for var, val in action.conditions.items():
#         self.add_val(var, val)
#       for var, val in action.effects.items():
#         self.add_val(var, val)
# 
#   def print_problem(self):
#     print(self.initial.values.keys())
#     print(len(self.initial.values))
#     print(len(self.var_order))
#     print(set(self.var_order) - set(self.initial.values.keys()))
#     for var in self.var_order:
#       print(var)
#       print(self.var_val_order[var])
#       print()
# 
#   def add_var(self, var):
#     if var not in self.var_indices:
#       self.var_indices[var] = len(self.var_order)
#       self.var_order.append(var)
#       self.var_val_indices[var] = {}
#       self.var_val_order[var] = []
#       self.add_val(var, self.default_val) # NOTE - I assume a default False value
# 
#   def add_val(self, var, val):
#     self.add_var(var)
#     if val not in self.var_val_indices[var]:
#       self.var_val_indices[var][val] = len(self.var_val_order[var])
#       self.var_val_order[var].append(val)
# 
#   def get_var(self, var):
#     return self.var_indices[var]
# 
#   def get_val(self, var, val):
#     return self.var_val_indices[var][val]
# 
#   def get_var_val(self, var, val):
#     return self.get_var(var), self.get_val(var, val)
# 
# Path: sas/ff.py
# def get_layers(costs):
# def flat_layer(_, layer):
# def random_layer(_, layer):
# def easy_layer(costs, layer):
# def hard_layer(costs, layer):
# def zero_cost(*args): return 0
# def operator_cost(operator, operator_costs, **args): return operator_costs[operator]
# def new_conditions_cost(operator, operator_costs, variable_costs, goals): # TODO - include marked pruning
# def extract_relaxed_plan(goal, variable_costs, operator_costs):
# def plan_cost(goal, operator_costs, relaxed_plan, relaxed_goals):
# def plan_length(goal, operator_costs, relaxed_plan, relaxed_goals):
# def multi_cost(goal, operator_costs, relaxed_plan, relaxed_goals):
# def filter_axioms(operators):
# def none(goal, operator_costs, relaxed_plan, relaxed_goals):
# def applicable(goal, operator_costs, relaxed_plan, relaxed_goals):
# def first_goals(goal, operator_costs, relaxed_plan, relaxed_goals):
# def first_operators(goal, operator_costs, relaxed_plan, relaxed_goals):
# def first_combine(goal, operator_costs, relaxed_plan, relaxed_goals):
# def ff(state, goal, operators, heuristic, helpful_actions, op=sum, unit=False):
# def ff_fn(heuristic, helpful_actions, op=sum, unit=False):
# def h_ff_max(state, goal, operators):
# def h_ff_add(state, goal, operators):
# ALL_BACK_POINTERS = False
# LAYER_ORDER = easy_layer
# EASIEST_HEURISTIC = new_conditions_cost
# 
# Path: planner/progression/best_first.py
# def deferred_best_first_search(start, goal, generator, priority, stack=False, debug=None, **kwargs):
#     space = StateSpace(generator, start, max_extensions=INF, **kwargs) # TODO: max_extensions=1 can fail when test
#     queue = (FILOPriorityQueue if stack else FIFOPriorityQueue)([(None, space.root)])
#     while not queue.empty() and space.is_active():
#         cv = queue.pop()
#         cv.evaluate() # cv.generate()
#         space.new_iteration(cv)
#         if cv.is_dead_end():
#             continue
#         if not test_parent_operator(cv): # always lazy
#             continue
#         if debug is not None:
#             debug(cv)
#         if test_goal(cv, goal):
#             return space.solution(cv)
#         successors = list(cv.unexplored())
#         if not cv.enumerated():
#             successors.append(cv) # TODO: use its prior value
#         for nv in order_successors(successors, stack):
#             # TODO: incorporate the path cost of nv instead of cv
#             queue.push(priority(cv), nv)
#     return space.failure()
# 
# Path: sas/downward.py
# def solve_sas(problem):
#   write_sas(problem)
#   plan = fast_downward()
#   if plan is None:
#     return None
#   return convert_solution(plan, problem)


import time
from misc.utils import INF, randomize
from sas.downward import Problem
from .ff import filter_axioms, in_add, ff_fn, plan_cost, first_combine
from planner.progression import deferred_best_first_search
from .downward import solve_sas

#from .set_additive import *

def h_0(state, goal, operators):
  return 0
def h_naive(state, goal, operators):
  return sum(1 for var, value in goal.cond() if state[var] != value)
def h_blind(state, goal, operators):
  return min(operator.cost for operator in ha_applicable(state, goal, operators))

###########################################################################

def ha_all(state, goal, operators):

# Path: misc/utils.py
# SEPARATOR = '\n'+85*'-'+'\n'
# def arg_info(frame, ignore=['self']):
# def function_name(stack): # NOTE - stack = inspect.stack()
# def get_path(frame):
# def get_directory(abs_path):
# def global_fn(name):
# def refresh(module_name):
# 
# Path: sas/downward.py
# class Problem(object):
#   default_val = False
#   def __init__(self, initial, goal, actions, axioms):
#     self.var_indices = {}
#     self.var_order = []
#     self.var_val_indices = {}
#     self.var_val_order = {}
#     self.axioms = axioms
#     self.mutexes = []
#     self.costs = True
# 
#     self.initial = initial
#     for var, val in initial.values.items():
#       self.add_val(var, val)
#     self.goal = goal
#     for var, val in goal.conditions.items():
#       self.add_val(var, val)
# 
#     self.actions = actions
#     for action in self.actions:
#       for var, val in action.conditions.items():
#         self.add_val(var, val)
#       for var, val in action.effects.items():
#         self.add_val(var, val)
# 
#   def print_problem(self):
#     print(self.initial.values.keys())
#     print(len(self.initial.values))
#     print(len(self.var_order))
#     print(set(self.var_order) - set(self.initial.values.keys()))
#     for var in self.var_order:
#       print(var)
#       print(self.var_val_order[var])
#       print()
# 
#   def add_var(self, var):
#     if var not in self.var_indices:
#       self.var_indices[var] = len(self.var_order)
#       self.var_order.append(var)
#       self.var_val_indices[var] = {}
#       self.var_val_order[var] = []
#       self.add_val(var, self.default_val) # NOTE - I assume a default False value
# 
#   def add_val(self, var, val):
#     self.add_var(var)
#     if val not in self.var_val_indices[var]:
#       self.var_val_indices[var][val] = len(self.var_val_order[var])
#       self.var_val_order[var].append(val)
# 
#   def get_var(self, var):
#     return self.var_indices[var]
# 
#   def get_val(self, var, val):
#     return self.var_val_indices[var][val]
# 
#   def get_var_val(self, var, val):
#     return self.get_var(var), self.get_val(var, val)
# 
# Path: sas/ff.py
# def get_layers(costs):
# def flat_layer(_, layer):
# def random_layer(_, layer):
# def easy_layer(costs, layer):
# def hard_layer(costs, layer):
# def zero_cost(*args): return 0
# def operator_cost(operator, operator_costs, **args): return operator_costs[operator]
# def new_conditions_cost(operator, operator_costs, variable_costs, goals): # TODO - include marked pruning
# def extract_relaxed_plan(goal, variable_costs, operator_costs):
# def plan_cost(goal, operator_costs, relaxed_plan, relaxed_goals):
# def plan_length(goal, operator_costs, relaxed_plan, relaxed_goals):
# def multi_cost(goal, operator_costs, relaxed_plan, relaxed_goals):
# def filter_axioms(operators):
# def none(goal, operator_costs, relaxed_plan, relaxed_goals):
# def applicable(goal, operator_costs, relaxed_plan, relaxed_goals):
# def first_goals(goal, operator_costs, relaxed_plan, relaxed_goals):
# def first_operators(goal, operator_costs, relaxed_plan, relaxed_goals):
# def first_combine(goal, operator_costs, relaxed_plan, relaxed_goals):
# def ff(state, goal, operators, heuristic, helpful_actions, op=sum, unit=False):
# def ff_fn(heuristic, helpful_actions, op=sum, unit=False):
# def h_ff_max(state, goal, operators):
# def h_ff_add(state, goal, operators):
# ALL_BACK_POINTERS = False
# LAYER_ORDER = easy_layer
# EASIEST_HEURISTIC = new_conditions_cost
# 
# Path: planner/progression/best_first.py
# def deferred_best_first_search(start, goal, generator, priority, stack=False, debug=None, **kwargs):
#     space = StateSpace(generator, start, max_extensions=INF, **kwargs) # TODO: max_extensions=1 can fail when test
#     queue = (FILOPriorityQueue if stack else FIFOPriorityQueue)([(None, space.root)])
#     while not queue.empty() and space.is_active():
#         cv = queue.pop()
#         cv.evaluate() # cv.generate()
#         space.new_iteration(cv)
#         if cv.is_dead_end():
#             continue
#         if not test_parent_operator(cv): # always lazy
#             continue
#         if debug is not None:
#             debug(cv)
#         if test_goal(cv, goal):
#             return space.solution(cv)
#         successors = list(cv.unexplored())
#         if not cv.enumerated():
#             successors.append(cv) # TODO: use its prior value
#         for nv in order_successors(successors, stack):
#             # TODO: incorporate the path cost of nv instead of cv
#             queue.push(priority(cv), nv)
#     return space.failure()
# 
# Path: sas/downward.py
# def solve_sas(problem):
#   write_sas(problem)
#   plan = fast_downward()
#   if plan is None:
#     return None
#   return convert_solution(plan, problem)


# Path: sas/utils.py
import time
from misc.utils import INF, randomize
from sas.downward import Problem
from .ff import filter_axioms, in_add, ff_fn, plan_cost, first_combine
from planner.progression import deferred_best_first_search
from .downward import solve_sas


#from .set_additive import *

def h_0(state, goal, operators):
  return 0
def h_naive(state, goal, operators):
  return sum(1 for var, value in goal.cond() if state[var] != value)
def h_blind(state, goal, operators):
  return min(operator.cost for operator in ha_applicable(state, goal, operators))

###########################################################################

def ha_all(state, goal, operators):

  return filter_axioms(operators)