#!/usr/bin/env python2
# -*- coding: utf-8 -*-

from pyecs import *
from pyecs.components import *

from collections import defaultdict
from testing import *

class TestEntity():
    def test__reset_global(self):
        Entity.__uid__ = "foo"
        Entity.__tags__ = "bar"
        assert hasattr(Entity, "__uid__")
        assert hasattr(Entity, "__tags__")
        assert type(Entity.__uid__) != int
        assert type(Entity.__tags__) != defaultdict
        
        Entity._reset_global()

        assert hasattr(Entity, "__uid__")
        assert hasattr(Entity, "__tags__")
        assert type(Entity.__uid__) == int
        assert type(Entity.__tags__) == defaultdict
        assert Entity.__uid__ == 0
        assert Entity.__tags__ == defaultdict(set)

    def test_entity_static_members(self):
        Entity._reset_global()
        e = Entity()
        assert hasattr(Entity, "__uid__")
        assert type(Entity.__uid__) == int
        assert hasattr(Entity, "__tags__")
        assert type(Entity.__tags__) == defaultdict

    def test_initial_state(self):
        Entity._reset_global()
        e = Entity()
        assert hasattr(e, "uid")
        assert hasattr(e, "parent")
        assert hasattr(e, "children")
        assert hasattr(e, "tags")
        assert hasattr(e, "components")
        assert type(e.uid) == int
        assert type(e.components) == defaultdict
        assert type(e.children) == list
        assert type(e.tags) == set
        assert e.parent == None
        assert len(list(e.components.iterkeys())) == 0
        assert len(e.children) == 0
        assert len(e.tags) == 0

    def test_add_tag(self):
        Entity._reset_global()
        e = Entity()
        assert "foo" not in e.tags
        assert "foo" not in Entity.__tags__
        assert e not in Entity.__tags__["foo"]
        e.add_tag("foo")
        assert "foo" in e.tags
        assert "foo" in Entity.__tags__
        assert e in Entity.__tags__["foo"]

    def test_remove_tag(self):
        Entity._reset_global()
        e = Entity()
        assert "foo" not in e.tags
        assert "foo" not in Entity.__tags__
        assert e not in Entity.__tags__["foo"]
        e.add_tag("foo")
        assert "foo" in e.tags
        assert "foo" in Entity.__tags__
        assert e in Entity.__tags__["foo"]
        e.remove_tag("foo")
        assert "foo" not in e.tags
        assert "foo" in Entity.__tags__ # because __tags__ is a defaultdict(set) we only removed from the set
        assert e not in Entity.__tags__["foo"]

    def test_has_tag(self):
        Entity._reset_global()
        e = Entity()
        assert e.has_tag("foo") == False
        e.add_tag("foo")
        assert e.has_tag("foo") == True

    def test_add_component_when_added_does_nothing_and_returns_None(self):
        Entity._reset_global()
        e = Entity()
        c = Component()
        e.add_component(c)
        i = len(e.components[type(c)])
        assert e.add_component(c) == None
        assert len(e.components[type(c)]) == i

    def test_add_component_returns_component(self):
        Entity._reset_global()
        e = Entity()
        c = Component()
        assert e.add_component(c) == c

    def test_add_component(self):
        Entity._reset_global()
        Component._reset_global()
        e = Entity()
        c = Component()

        assert c not in e.components[type(c)]
        assert c not in Component.__added_components__[type(c)]

        e.add_component(c)
        assert c in e.components[type(c)]
        assert c in Component.__added_components__[type(c)]

    def test_add_component_callbacks(self):
        Entity._reset_global()
        e = Entity()
        c = Component()

        def component_added(component,entity):
            assert e == entity
            assert c == component
            component_added.called = True

        def component_attached():
            component_attached.called = True

        e.register_callback("component_added", component_added)
        c.register_callback("component_attached", component_attached)
        component_added.called = False
        component_attached.called = False
        e.add_component(c)
        assert component_added.called
        assert component_attached.called


    def test_remove_component(self):
        Entity._reset_global()
        Component._reset_global()
        e = Entity()
        c = Component()

        e.add_component(c)
        assert c in e.components[type(c)]
        assert c in Component.__added_components__[type(c)]

        e.remove_component(c)
        assert c not in e.components[type(c)]
        assert c not in Component.__added_components__[type(c)]

    
    def test_remove_component_callbacks(self):
        Entity._reset_global()
        e = Entity()
        c = Component()

        def component_removed(component,entity):
            assert e == entity
            assert c == component
            component_removed.called = True

        def component_detached(entity):
            assert e == entity
            component_detached.called = True

        e.register_callback("component_removed", component_removed)
        c.register_callback("component_detached", component_detached)
        e.add_component(c)

        component_removed.called = False
        component_detached.called = False
        e.remove_component(c)
        assert component_removed.called
        assert component_detached.called

    def test_find_parent_entity_with_component(self):
        e0 = Entity()
        e1 = Entity()
        e2 = Entity()
        e3 = Entity()
        e4 = Entity()
        c = Component()
        e0.add_entity(e1)
        e1.add_entity(e2)
        e2.add_entity(e3)
        e3.add_entity(e4)
        e2.add_component(c)

        assert e0.find_parent_entity_with_component(type(c)) == None
        assert e1.find_parent_entity_with_component(type(c)) == None
        assert e2.find_parent_entity_with_component(type(c)) == None
        assert e3.find_parent_entity_with_component(type(c)) == e2
        assert e4.find_parent_entity_with_component(type(c)) == e2

    def test_add_entity(self):
        e_parent = Entity()
        e_child = Entity()
        def entity_added(parent,entity):
            assert parent == e_parent
            assert entity == e_child
            entity_added.called = True

        assert e_child not in e_parent.children
        assert e_child.parent != e_parent
        entity_added.called = False
        e_child.register_callback("entity_added",entity_added)
        e_parent.add_entity(e_child)
        assert e_child in e_parent.children
        assert e_child.parent == e_parent
        assert entity_added.called

    def test_remove_entity_result(self):
        e_parent = Entity()
        e_child = Entity()
        
        assert e_parent.remove_entity(e_child) == False
        e_parent.add_entity(e_child)
        assert e_parent.remove_entity(e_child) == True
        assert e_parent.remove_entity(e_child) == False
    
    def test_remove_entity(self):
        e_parent = Entity()
        e_child = Entity()
        
        def entity_removed(parent,entity):
            assert parent == e_parent
            assert entity == e_child
            entity_removed.called = True

        e_child.register_callback("entity_removed",entity_removed)
        e_parent.add_entity(e_child)

        assert e_child.parent is not None
        assert e_child in e_parent.children

        entity_removed.called = False
        e_parent.remove_entity(e_child)

        assert entity_removed.called
        assert e_child.parent is None
        assert e_child not in e_parent.children

    def test_remove_from_parent(self):
        e_parent = Entity()
        e_child = Entity()
        
        def entity_removed(parent,entity):
            assert parent == e_parent
            assert entity == e_child
            entity_removed.called = True

        e_child.register_callback("entity_removed",entity_removed)
        e_parent.add_entity(e_child)

        assert e_child.parent is not None
        assert e_child in e_parent.children

        entity_removed.called = False
        e_child.remove_from_parent()

        assert entity_removed.called
        assert e_child.parent is None
        assert e_child not in e_parent.children

    def test_remove_entity_result_from_parent(self):
        e_parent = Entity()
        e_child = Entity()
        
        assert e_child.remove_from_parent() == False