from transcendence.abstract.AbstractRoom import AbstractRoom
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember

from channels.generic.websocket import WebsocketConsumer

from asgiref.sync import async_to_sync

from .Ball import Ball
from .Player import Player
from .Spectator import Spectator
from .Wall import Wall
from .Point import Point
from .Segment import Segment

import math

from .. import config

from ..models import GameModel

from ..routine import routine

import threading

class Game(AbstractRoom):

    def __init__(self, game_id):
        super().__init__(None)
        
        self.ball: Ball = Ball()
        self.model: GameModel = GameModel.objects.get(pk = game_id)
        
        self.started = False
        
        radius: float = min(config.MAP_SIZE_X, config.MAP_SIZE_Y) / 2 - 10 

        players_id: list[int] = self.model.get_players_id()

        nb_sides = len(players_id) * 2
        
        polygon: list[Point] = []
        
        for i in range(nb_sides):

            angle: float = (i * 2 * math.pi / nb_sides) + (math.pi * 3 / nb_sides)

            x: float = config.MAP_CENTER_X + radius * math.cos(angle)
            y: float = config.MAP_CENTER_Y + radius * math.sin(angle)
            
            polygon.append(Point(x, y))
        
        segments: list[Point] = []
        for i in range(nb_sides):
            segments.append(Segment(polygon[i], polygon[(i + 1) % nb_sides]))

        self.players: list[Player] = []
        for i, player_id in enumerate(players_id):
            player = Player(self, player_id, None, segments[i * 2])
            self.players.append(player)
    
        self.spectators: list[Spectator] = []
        
        self.walls: list[Wall] = []
        
        for i in range(1, nb_sides, 2):
            self.walls.append(Wall(segments[i]))

        self._updated_players: list[Player] = []

        self.game_id: int = game_id

        self.thread = threading.Thread(target = routine, args=(self,))
        
        self.thread.start()
    
    def get_players_id(self):
        return [player.user_id for player in self.players]
    
    def get_players_connected(self) -> list[Player]:
        return [player for player in self.players if player.is_connected()]
    
    def broadcast(self, detail: str, data: dict = {}, excludeds: list[Spectator | Player] = []):
        
        members: list[Player | Spectator] = self.get_players_connected() + self.spectators
        
        for excluded in excludeds:
            if (excluded in members):
                members.remove(excluded)
        
        for member in members:
            member.send(detail, data)      
    
    def get_player_by_user_id(self, user_id: int) -> Player:
        for player in self.players:
            if (player.user_id == user_id):
                return player
        return None
    
    def _send_game_data(self, member: Spectator | Player):
        member.send("init_game", self.to_dict())
    
    def everbody_is_here(self):
        for player in self.players:
            if not player.is_connected():
                return False
        return True
    
    def _player_join(self, user_id: int, socket: WebsocketConsumer):
        
        # check if player is already connected
        player = self.get_player_by_user_id(user_id)
        if (player is None):
            return None
        
        if (player.is_connected()):
            player.disconnect(1001)

        player.socket = socket
        
        if (self.everbody_is_here()):
            print("chie moi dessu")
            print("start")
            self.start()

        self._update_player(player)

        return player
    
    def _update_player(self, player: Player):
        self._updated_players.append(player)
    
    def _player_leave(self, player: Player):
        print(player.socket)
        self._update_player(player)
    
    def _spectator_join(self, user_id: int, socket: WebsocketConsumer):
        
        spectator: Spectator = Spectator(user_id, socket, self)
        
        self.spectators.append(spectator)
        
        return spectator
    
    def _spectator_leave(self, spectator: Spectator):
        self.spectators.remove(spectator)        
    
    def join(self, user_id: int, socket: WebsocketConsumer) -> Spectator | Player:
        member: Player = self._player_join(user_id, socket)
        if (member is None):
            member: Spectator = self._spectator_join(user_id, socket)
        self._send_game_data(member)
        return member    
                    
    
    def start(self):
        if (self.started == True):
            return
        self.started = True
        self.model.start()
        
    def leave(self, member: AbstractRoomMember):
        if (isinstance(member, Player)):
            self._player_leave(member)
        elif (isinstance(member, Spectator)):
            self._spectator_leave(member)
    
    def to_dict(self):
        
        data: dict = {"ball": self.ball.to_dict(),
                      "players": [player.to_dict() for player in self.players],
                      "walls": [wall.to_dict() for wall in self.walls],
                      }
        
        return data