# coding: utf-8
from random import randint

import cocos
from cocos.actions import MoveTo, Repeat, ScaleBy, Reverse, RotateTo
from cocos.sprite import Sprite
from sandbox.engulf.behaviour import GrassGrownUp, GrassSpawn, MoveTo as MoveToEvent, EatEvent, AttackEvent, EatenEvent
from sandbox.engulf.subject import Cell, Grass, PreyCell, PredatorCell
from synergine2.terminals import TerminalPackage, Terminal
from synergine2_cocos2d.gui import Gui, GridLayerMixin
from synergine2_cocos2d.gui import MainLayer as BaseMainLayer

cell_scale = ScaleBy(1.1, duration=0.25)


class CellsLayer(GridLayerMixin, BaseMainLayer):
    def __init__(self, game: 'Game', *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.game = game
        self.cell_positions = {}
        self.cell_ids = {}

    @property
    def move_duration(self):
        return self.game.cycle_duration

    @property
    def fake_move_rotate_duration(self):
        return self.move_duration / 3

    def born(self, subject_id: int, grid_position, cell_type):
        png = 'resources/cell.png' if cell_type is PreyCell else 'resources/cellp.png'

        cell = Sprite(png)
        cell.rotation = randint(0, 360)
        self.grid_manager.scale_sprite(cell)
        self.grid_manager.position_sprite(cell, grid_position)
        self.cell_positions[grid_position] = cell
        self.cell_ids[subject_id] = cell
        cell.do(Repeat(cell_scale + Reverse(cell_scale)))
        self.add(cell)

    def move(self, subject_id: int, position: tuple):
        if subject_id not in self.cell_ids:
            return  # Cell eaten before

        cell = self.cell_ids[subject_id]

        window_position = self.grid_manager.get_window_position(position[0], position[1])
        move_action = MoveTo(window_position, self.move_duration)

        fake_rotate = RotateTo(randint(0, 360), self.fake_move_rotate_duration)

        cell.do(move_action)
        cell.do(fake_rotate)

    def attack(self, attacker_id: int, attacked_id: int):
        attacker = self.cell_ids[attacker_id]
        attacker.do(ScaleBy(1.7, duration=0.25))

    def eaten(self, eaten_id: int):
        eaten = self.cell_ids[eaten_id]
        self.remove(eaten)
        # TODO: refact: On a pas nettoyer self.cell_positions par exemple
        del self.cell_ids[eaten_id]


class GrassLayer(GridLayerMixin, BaseMainLayer):
    def __init__(self, game: 'Game', *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.game = game
        self.grasses = {}

    def born(self, subject_id, grid_position, opacity=100):
        grass = Sprite('resources/grass.png')
        grass.rotation = randint(0, 360)
        grass.opacity = opacity
        self.grid_manager.scale_sprite(grass)
        self.grid_manager.position_sprite(grass, grid_position)
        self.grasses[subject_id] = grass
        self.add(grass)

    def set_density(self, subject_id, density):
        self.grasses[subject_id].opacity = density


class MainLayer(GridLayerMixin, BaseMainLayer):
    def __init__(self, game: 'Game', terminal: Terminal, *args, **kwargs):
        super().__init__(terminal, *args, **kwargs)
        self.game = game
        self.terminal = terminal

        self.cells = CellsLayer(game=game, terminal=terminal)
        self.add(self.cells)

        self.grasses = GrassLayer(game=game, terminal=terminal)
        self.add(self.grasses)


class Game(Gui):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.main_layer = MainLayer(game=self, terminal=self.terminal)
        self.main_scene = cocos.scene.Scene(self.main_layer)

        # Event registering
        self.terminal.register_event_handler(
            GrassGrownUp,
            self.on_grass_grown_up,
        )
        self.terminal.register_event_handler(
            GrassSpawn,
            self.on_grass_spawn,
        )
        self.terminal.register_event_handler(
            MoveToEvent,
            self.on_move_to,
        )
        self.terminal.register_event_handler(
            EatEvent,
            self.on_eat,
        )
        self.terminal.register_event_handler(
            AttackEvent,
            self.on_attack,
        )
        self.terminal.register_event_handler(
            EatenEvent,
            self.on_eaten,
        )

    def get_main_scene(self):
        return self.main_scene

    def before_received(self, package: TerminalPackage):
        if package.subjects:  # It's thirst package
            for subject in package.subjects:
                if isinstance(subject, PreyCell):
                    self.main_layer.cells.born(subject.id, subject.position, PreyCell)
                if isinstance(subject, PredatorCell):
                    self.main_layer.cells.born(subject.id, subject.position, PredatorCell)
                if isinstance(subject, Grass):
                    self.main_layer.grasses.born(
                        subject.id,
                        subject.position,
                        subject.density,
                    )

    def on_grass_spawn(self, event: GrassSpawn):
        self.main_layer.grasses.born(
            event.subject_id,
            event.position,
            event.density,
        )

    def on_grass_grown_up(self, event: GrassGrownUp):
        self.main_layer.grasses.set_density(
            event.subject_id,
            event.density,  # TODO: Recupe ces données depuis local plutôt que event ?
        )

    def on_move_to(self, event: MoveToEvent):
        self.main_layer.cells.move(
            event.subject_id,
            event.position,
        )

    def on_eat(self, event: EatEvent):
        self.main_layer.grasses.set_density(
            event.eaten_id,
            event.eaten_new_density,
        )

    def on_attack(self, event: AttackEvent):
        self.main_layer.cells.attack(
            event.attacker_id,
            event.attacked_id,
        )

    def on_eaten(self, event: EatenEvent):
        self.main_layer.cells.eaten(
            event.eaten_id,
        )