from direct.gui.OnscreenImage import OnscreenImage
from direct.interval.LerpInterval import LerpPosInterval
from direct.interval.MetaInterval import Sequence
from direct.showbase.DirectObject import DirectObject
from panda3d.core import Vec3, Texture, NodePath, Vec4, CardMaker, TransparencyAttrib, BitMask32, CollisionNode, \
    CollisionNode, CollisionSphere, CollisionTraverser, CollisionHandlerPusher
from helper import base, BUILDING_MASK, CITY_SIZE, PERSON_CLICK_MASK, PAUSED

JUMP_DURATION = 0.3
JUMP_HEIGHT = 0.2

HOP_DURATION = 0.5
HOP_HEIGHT = 0.07


class Person(DirectObject):
    NO_MASK = BitMask32(0x0)
    PERSON_MASK = BitMask32(0x10)
    PLAYER_ONLY_MASK = BitMask32(0x4)
    PLAYER_MASK = PLAYER_ONLY_MASK | PERSON_MASK
    PEDESTRIAN_ONLY_MASK = BitMask32(0x8)
    PEDESTRIAN_MASK = PEDESTRIAN_ONLY_MASK | PERSON_MASK
    ALLY_ONLY_MASK = BitMask32(0x20)
    ALLY_MASK = ALLY_ONLY_MASK | PERSON_MASK
    ENEMY_ONLY_MASK = BitMask32(0x40)
    ENEMY_MASK = ENEMY_ONLY_MASK | PERSON_MASK

    traverser = CollisionTraverser()

    ally_tex = base.loader.load_texture('img/red_jacket.png')
    enemy_tex = base.loader.load_texture('img/black_jacket.png')
    neutral_tex = base.loader.load_texture('img/no_jacket.png')
    player_tex = base.loader.load_texture('img/maroon_jacket.png')
    police_tex = base.loader.load_texture('img/police.png')
    tex = neutral_tex
    for tex in (ally_tex, enemy_tex, neutral_tex, player_tex):
        assert isinstance(tex, Texture)
        tex.set_border_color(Vec4(0, 0, 1, 1))
        tex.set_wrap_u(Texture.WM_border_color)
        tex.set_wrap_v(Texture.WM_border_color)
        tex.set_clear_color(Vec4(0, 1, 0, 1))

    counter = [0]

    def __init__(self, pos=Vec3(0, 0, 0)):
        self.alive = True
        self.name = "person" + str(self.counter[0])
        self.counter[0] += 1

        cm = CardMaker('card')
        cm.set_frame(-self.tex.get_orig_file_x_size(), self.tex.get_orig_file_x_size(),
                     -self.tex.get_orig_file_y_size(), self.tex.get_orig_file_y_size())

        self.model = NodePath('model')
        self.card = NodePath(cm.generate())
        self.card.set_texture(self.tex)
        self.card.reparent_to(self.model)
        self.card.set_transparency(TransparencyAttrib.M_alpha)
        self.card.set_pos(0, 0, 0.5)
        self.card.set_scale(1/1000)
        self.card.set_color(Vec4(1, 1, 1, 1))
        self.card.set_bin("opaque", 0)

        self.cols = []
        self.push_collider = self.create_collider(self.name, 0.3, self.PERSON_MASK, self.PERSON_MASK | BUILDING_MASK)
        self.click_collider = self.create_collider(self.name + ' click', 1, PERSON_CLICK_MASK, self.NO_MASK)
        self.click_collider.set_pos(0, 0, 0.5)

        assert isinstance(self.model, NodePath)
        self.model.reparent_to(base.render)
        self.model.set_pos(pos)
        self.card.set_pos(0, 0, 0.5)
        self.card.set_hpr(180, 0, 0)
        self.card.set_light_off()

        self.velocity = Vec3(0, 0, 0)

        self.pusher = CollisionHandlerPusher()
        self.pusher.add_collider(self.push_collider, self.model)
        self.traverser.add_collider(self.push_collider, self.pusher)

        self.model.set_scale(0.25)

        base.task_mgr.add(self.do_update, name=self.name + ' update')

        self.jump = Sequence(
            LerpPosInterval(self.card, JUMP_DURATION/2, (0, 0, 0.5+JUMP_HEIGHT), (0, 0, 0.5), None, 'easeOut'),
            LerpPosInterval(self.card, JUMP_DURATION/2, (0, 0, 0.5), (0, 0, 0.5+JUMP_HEIGHT), None, 'easeIn'),
            name=self.name + 'jump'
        )

        self.hop = Sequence(
            LerpPosInterval(self.card, HOP_DURATION/2, (0, 0, 0.5+HOP_HEIGHT), (0, 0, 0.5), None, 'easeOut'),
            LerpPosInterval(self.card, HOP_DURATION/2, (0, 0, 0.5), (0, 0, 0.5+HOP_HEIGHT), None, 'easeIn'),
            name=self.name + 'hop'
        )

    def __del__(self):
        for c in self.cols:
            self.set_mask(c, self.NO_MASK)
            self.traverser.remove_collider(c)
        self.ignore_all()
        for c in self.cols:
            del c
        base.task_mgr.remove(self.name + ' update')

    def create_collider(self, name, radius, into_mask, from_mask=None):
        collider = self.model.attach_new_node(CollisionNode(name))
        collider.node().add_solid(CollisionSphere(0, 0, -0.5, radius))
        collider.set_collide_mask(into_mask)
        if from_mask is None:
            from_mask = into_mask
        collider.node().set_from_collide_mask(from_mask)
        self.cols.append(collider)
        return collider

    def set_mask(self, collider, new_mask):
        collider.set_collide_mask(new_mask)
        collider.node().set_from_collide_mask(new_mask)

    @property
    def pos(self):
        return self.model.get_pos()

    @pos.setter
    def pos(self, value):
        value.x = max(min(value.x, CITY_SIZE-1), 0)
        value.y = max(min(value.y, CITY_SIZE-1), 0)
        self.model.set_fluid_pos(value)

    def update(self):
        self.pos += self.velocity * globalClock.getDt()

        # keep facing camera
        self.model.look_at(base.camera)
        self.model.set_z(0)

    def do_update(self, task):
        if not PAUSED[0]:
            self.update()
        return task.cont

    @staticmethod
    def update_traverser(task):
        Person.traverser.traverse(base.render)
        return task.cont


base.task_mgr.add(Person.update_traverser)
Person.traverser.set_respect_prev_transform(True)
