import random
import unittest

from Planning import *
from PlanningHeuristics import *
from Searching import *

def make_action_list(action_strs):
    return [Action(parse_list(s)) for s in action_strs]

valid_actions = [
    '(:action pick-up :parameters (d ) :precondition (and (clear d ) (ontable d ) (handempty ) ) :effect (and (holding d ) (not (ontable d ) ) (not (clear d ) ) (not (handempty ) ) ) )',
    '(:action pick-up :parameters (b ) :precondition (and (clear b ) (ontable b ) (handempty ) ) :effect (and (holding b ) (not (ontable b ) ) (not (clear b ) ) (not (handempty ) ) ) )',
    '(:action pick-up :parameters (a ) :precondition (and (clear a ) (ontable a ) (handempty ) ) :effect (and (holding a ) (not (ontable a ) ) (not (clear a ) ) (not (handempty ) ) ) )',
    '(:action pick-up :parameters (c ) :precondition (and (clear c ) (ontable c ) (handempty ) ) :effect (and (holding c ) (not (ontable c ) ) (not (clear c ) ) (not (handempty ) ) ) )']

correct_plan = [
    '(:action pick-up :parameters (b ) :precondition (and (clear b ) (ontable b ) (handempty ) ) :effect (and (holding b ) (not (ontable b ) ) (not (clear b ) ) (not (handempty ) ) ) )',
    '(:action stack :parameters (b a ) :precondition (and (holding b ) (clear a ) ) :effect (and (clear b ) (handempty ) (on b a ) (not (holding b ) ) (not (clear a ) ) ) )', 
    '(:action pick-up :parameters (c ) :precondition (and (clear c ) (ontable c ) (handempty ) ) :effect (and (holding c ) (not (ontable c ) ) (not (clear c ) ) (not (handempty ) ) ) )',
    '(:action stack :parameters (c b ) :precondition (and (holding c ) (clear b ) ) :effect (and (clear c ) (handempty ) (on c b ) (not (holding c ) ) (not (clear b ) ) ) )',
    '(:action pick-up :parameters (d ) :precondition (and (clear d ) (ontable d ) (handempty ) ) :effect (and (holding d ) (not (ontable d ) ) (not (clear d ) ) (not (handempty ) ) ) )', 
    '(:action stack :parameters (d c ) :precondition (and (holding d ) (clear c ) ) :effect (and (clear d ) (handempty ) (on d c ) (not (holding d ) ) (not (clear c ) ) ) )']

class TestPlanning(unittest.TestCase):
    def setUp(self):
        self.blockscons = file2cons('blocks/domain.pddl')
        self.blocks = parse_domain(self.blockscons)

        self.prob1cons = file2cons('blocks/probBLOCKS-4-0.pddl')
        self.prob1 = parse_problem(self.prob1cons, self.blocks)

        self.valid_actions = make_action_list(valid_actions)
        self.correct_plan = make_action_list(correct_plan)

        self.successor_state = State(parse_list('((clear c ) (clear a ) (clear b ) (ontable c ) (ontable a ) (ontable b ) (holding d ) )'))

    def test_pred_eq(self):
        pred1 = Predicate(parse_list('(ontable b)'))
        pred2 = Predicate(parse_list('(ontable b)'))
        pred3 = Predicate(parse_list('(ontable c)'))
        self.assertEqual(pred1, pred2)
        self.assertNotEqual(pred1, pred3)

    def test_domain(self):
        self.assertEqual(str(self.blocks), str(self.blockscons))

    def test_instantiations(self):
        self.assertEqual(self.valid_actions,
                         self.prob1.all_valid_actions())

    def test_first_round(self):
        inst = self.prob1.all_valid_actions()
        succ = self.prob1.start_state.successor(inst[0])
        self.assertEqual(succ, self.successor_state)

    def test_correct_plan(self):
        prob = self.prob1
        for act in self.correct_plan:
            prob = prob.successor(act)
        self.assertTrue(prob.goal_satisfied())

    def test_breadth_first(self):
        plan, proc, gen, purged = breadth_first(self.prob1)
        self.assertEqual(plan, self.correct_plan)
        
    def test_best_first(self):
        plan, proc, gen, purged = best_first(self.prob1, unmet_goals)
        self.assertEqual(plan, self.correct_plan)

if __name__ == '__main__':
    unittest.main()
