"""
BF-like squad system.

Maintainer: Triplefox
"""

from commands import add, rights, admin, name, get_player
import commands
import random

SQUAD_NAMES = set([
    'Alpha','Bravo','Charlie','Delta','Epsilon','Foxtrot','Gamma',
   'Golf','Hotel','India','Juliet','Kilo','Lima','Mike',
   'November','Oscar','Papa','Quebec','Romero','Sierra','Tango',
   'Uniform','Victor','Whiskey','X-ray','Yankee','Zulu'])

def get_grid_pos(connection):
    x,y,z=connection.get_location()
    x,y=int(x/(512/8)),int(y/(512/8))
    return "ABDEFGH"[x-1]+str(y+1)

def mates(self):
    if self.squad:
        for player in self.protocol.players.values():
            if player.team==self.team and player.squad==self.squad:
                self.send_chat("%s in %s" %(["You are",player.name+" is"][player!=self] , get_grid_pos(player))
)
def follow(self, playerkey = None):
    if playerkey is None:
        squad_pref = None
        squad = self.squad
    else:
        squad_pref = get_player(self.protocol, playerkey)
        squad = squad_pref.squad
        if squad_pref.team is not self.team:
            return '%s is not on your team!' % (squad_pref.name)
        if squad_pref is self:
            return "You can't follow yourself!"
        if squad_pref.squad is None:
            return ('%s is not in a squad and cannot be followed.' %
                    squad_pref.name)

    return self.join_squad(squad, squad_pref)

def squad(self, squadkey = None):
    if self.protocol.squad_size <= 1:
        return 'Squads are disabled on this server.'

    # squadlist

    if squadkey is None:
        allsquads = self.get_squads(self.team)
        result = []
        for squadkey in allsquads.keys():
            result.append(self.print_squad(
            squadkey, allsquads[squadkey]))
        result.append(('To join squads: /squad <squad name>. ' +
                       '/squad none to spawn normally.'))
        self.send_lines(result)
        return 

    if squadkey.lower() == 'none':
        squad = None
        squad_pref = None
    else:
        squad = squadkey
        squad_pref = None

    return self.join_squad(squad, squad_pref)

for func in (squad, follow, mates):
    add(func)

def apply_script(protocol, connection, config):
    protocol.squad_respawn_time = config.get('squad_respawn_time', 
        protocol.respawn_time)
    protocol.squad_size = config.get('squad_size', 0)
    protocol.auto_squad = config.get('auto_squad', True)
    
    class SquadConnection(connection):
        squad = None
        squad_pref = None

        def on_login(self, name):
            if self.protocol.auto_squad:
                self.join_squad(self.find_auto_squad(),None)
            return connection.on_login(self, name)
        
        def get_squad(self, team, squadkey):
            result = {'name' : squadkey, 'players' : []}
            if squadkey is None:
                for player in self.protocol.players.values():
                    if (player.team is team and
                        player.squad is None):
                        result['players'].append(player)
                        result['name'] = player.squad
            else:
                for player in self.protocol.players.values():
                    if (player.team is team and player.squad and
                        player.squad.lower() == squadkey.lower()):
                        result['players'].append(player)
                        result['name'] = player.squad
            return result

        def get_squads(self, team):
            squad_dict = {}
            for player in self.protocol.players.values():
                if player.team is team:
                    if squad_dict.has_key(player.squad):
                        squad_list = squad_dict[player.squad]
                    else:
                        squad_list = []
                        squad_dict[player.squad] = squad_list
                    squad_list.append(player)
            return squad_dict

        def print_squad(self, squadkey, squadlist):
            if squadkey is None:
                result = 'Unassigned: '
            else:
                result = 'Squad %s: ' % (squadkey)
            result+=', '.join([player.name for player in squadlist])
            return result

        def find_auto_squad(self):
            squad_dict = self.get_squads(self.team)
            available = set()
            unused = []
            for name in SQUAD_NAMES:
                if squad_dict.has_key(name):
                    if len(squad_dict[name]) < self.protocol.squad_size:
                        available.add(name)
                else:
                    unused.append(name)
            if len(available) > 0:
                return available.pop()
            else:
                return random.choice(unused)

        def join_squad(self, squad, squad_pref):

            if self.team is None or \
               self.team is self.protocol.spectator_team:
                return
            
            # same-squad check

            if squad is None or self.squad is None:
                newsquad = self.squad is not squad
            else:
                newsquad = self.squad.lower() != squad.lower()
            newpref = self.squad_pref is not squad_pref
            
            if not newsquad and not newpref:
                return 'Squad unchanged.'

            # unique squad, so check for squad size first

            existing = self.get_squad(self.team, squad)
            squad = existing['name'] # fixes the case
            
            if squad and (self.protocol.squad_size
                <= len(existing['players'])):
                return ('Squad %s is full. (limit %s)' %
                        (squad, self.protocol.squad_size))
            
            # assign to unique squad

            oldsquad = self.squad
            oldpref = self.squad_pref
            
            if newsquad and self.squad:
                self.leave_squad()

            self.squad = squad
            self.squad_pref = squad_pref
            
            if newsquad and squad:
                self.squad_broadcast('%s joined your squad.' %
                                           self.name)
            
            if squad is None:
                self.respawn_time = self.protocol.respawn_time
                self.squad_pref = None
                self.send_chat('You are no longer assigned to a squad.')
            else:
                self.respawn_time = self.protocol.squad_respawn_time
                if newpref and newsquad:
                    if squad_pref is None:
                        return ('You are now in squad %s.' % squad)
                    else:
                        return ('You are now in squad %s, following %s.' %
                                   (squad, squad_pref.name))
                elif newpref:
                    if squad_pref is None:
                        return ('You are no longer following %s.' %
                                oldpref.name)
                    else:
                        return ('You are now following %s.' %
                                   squad_pref.name)
                elif newsquad:
                    return 'You are now in squad %s.' % squad

        def leave_squad(self):
            if self.squad:
                self.squad_broadcast("%s left your squad." %
                                           self.name)
            self.squad = None
            self.squad_pref = None
            for player in self.protocol.players.values():
                if player.squad_pref is self:
                    player.squad_pref = None
            self.respawn_time = self.protocol.respawn_time

        def squad_broadcast(self, msg):
            if self.squad:
                squad = self.get_squad(self.team, self.squad)
                for player in squad['players']:
                    if player is not self:
                        player.send_chat(msg)
                    
        def get_follow_location(self, follow):
            x, y, z = (follow.world_object.position.get())
            return x, y, z

        def on_team_changed(self, old_team):
            self.leave_squad()
            return connection.on_team_changed(self, old_team)

        def on_spawn(self, pos):
            if self.squad:
                all_members = ([n for n in self.get_squad(self.team,
                            self.squad)['players'] if 
                            n is not self])
                live_members = [n for n in all_members if n.hp and not n.invisible and 
                                not n.god]
                membernames = [m.name for m in all_members]
                memberstr = ""
                for n in xrange(len(all_members)):
                    name = membernames[n]
                    if not all_members[n].hp:
                        name += " (DEAD)"
                    else:
                        name += " (%s hp)" % all_members[n].hp
                    if n==0:
                        memberstr+="%s" % name
                    elif n==len(membernames)-1:
                        memberstr+=" and %s" % name
                    else:
                        memberstr+=", %s" % name
                if len(all_members)>0:
                    self.send_chat('You are in squad %s with %s.' %
                                   (self.squad, memberstr))
                else:
                    self.send_chat('You are in squad %s, all alone.' %
                                   self.squad)
                if (self.squad_pref is not None and self.squad_pref.hp and
                    self.squad_pref.team is self.team and not self.squad_pref.invisible
                    and not self.squad_pref.god):
                    self.set_location_safe(self.get_follow_location(
                        self.squad_pref))
                else:
                    if len(live_members)>0:
                        self.set_location_safe(self.get_follow_location(
                            random.choice(live_members)))
            return connection.on_spawn(self, pos)

        def on_kill(self, killer, type, grenade):
            if killer is None or killer is self:
                self.squad_broadcast('Squadmate %s suicided' % self.name)
            else:
                self.squad_broadcast('Squadmate %s was killed by %s' %
                             (self.name, killer.name))
            return connection.on_kill(self, killer, type, grenade)
        
        def on_chat(self, value, global_message):
            if self.squad is not None and not global_message:
                value = '%s : %s' % (self.squad, value)
            return connection.on_chat(self, value, global_message)
    
    return protocol, SquadConnection
