"""
Rewrite of match.py.
Intended to be used for tournaments and clan matches.
"""
from pyspades.common import coordinates, to_coordinates, prettify_timespan
from commands import add, admin, name
from twisted.internet import reactor
from twisted.internet.task import LoopingCall
from math import degrees,atan,sin,cos
from random import randint

S_MATCH_START = 'The match has started!'
S_MATCH_TIMELEFT = '{time} remaining.'
S_MATCH_CANCELLED = 'The match has been stopped by {who}!'
S_MATCH_OVER = 'The match is over! {team} wins {score1} to {score2}!'
S_MATCH_TIE = 'The match is over! It\'s a tie!'
S_MATCH_TIE_EXTEND = 'Tie! Extending match by {time}. {sd}'
S_MATCH_SUDDEN_DEATH = 'Sudden death!'
S_MATCH_PAUSED = "The match is paused"
S_MATCH_RESUMED = "Match unpaused"
    
S_MATCH_KILL = '{killer} killed {victim}!'
S_MATCH_FLAG_TAKE = '{who} took the {team} flag in {coord}!'
S_MATCH_FLAG_DROP = '{who} dropped the {team} flag in {coord}!'
S_MATCH_FLAG_CAP = '{who} captured the {team} flag!'
    
# In minutes.
MATCH_OVERTIME_LENGTH = 15
MATCH_ALLOW_TIES = True # set MATCH_EXTEND_ON_TIE if you want a "one-time only" extension in the event of a tie
MATCH_DISABLE_CHAT = True
MATCH_FEED_IN_CHAT = False
MATCH_RESET_FLAG_IN_WATER = True
MATCH_EXTEND_ON_TIE = True # only extends once
MATCH_SUDDEN_DEATH_ON_TIE = True

@admin
def intelpos(connection,distance,radius=90):
    distance,radius=int(distance),int(radius)
    protocol = connection.protocol
    blue,green = protocol.blue_team,protocol.green_team
    bx,by,bz = blue.base.x,blue.base.y,blue.base.z
    gx,gy,gz = green.base.x,green.base.y,green.base.z
    xd,yd = bx-gx,by-gy
    angle = degrees(atan(xd/yd)) + randint(radius/-2,radius/2)
    x,y = distance*degrees(sin(angle)) + gx,distance*degrees(cos(angle))*-1 + gy
    green.flag.set(x,y,protocol.map.get_z(x,y))
    green.flag.update()
    angle = degrees(atan(xd/yd)) + randint(radius/-2,radius/2)
    x,y = distance*degrees(sin(angle))*-1 + bx,distance*degrees(cos(angle)) + by
    blue.flag.set(x,y,protocol.map.get_z(x,y))
    blue.flag.update()
    return "Flags repositioned successfully"

@admin
@name('timer')
def begin_timer(connection, time=60):
    if connection.protocol.match_in_progress:
        return 'A match is already running!'
    time = int(time) * 60
    connection.protocol.begin_match(time)
    
add(begin_timer)
    
@admin
@name('stoptimer')
def stop_timer(connection):
    if not connection.protocol.match_in_progress:
        return 'There\'s no match to stop!'
    connection.protocol.end_match(cancelled=True, cancelled_by=connection.printable_name)
    
add(stop_timer)
    
@admin
@name('toggleovertime')
def toggle_overtime(connection):
    global MATCH_EXTEND_ON_TIE
    MATCH_EXTEND_ON_TIE = not MATCH_EXTEND_ON_TIE
    return 'Overtime is now %s!' % ['disabled', 'enabled'][int(MATCH_EXTEND_ON_TIE)]
    
add(toggle_overtime)

@admin
def pause(connection):
    if connection.protocol.paused_since:
        return "Game already paused"
    if not connection.protocol.match_in_progress:
        return "No match in progress"
    connection.protocol.pause_match()

add(pause)

@admin
def resume(connection):
    if not connection.protocol.match_timer_ends:
        return "No game in progress"
    if not connection.protocol.paused_since:
        return "Game not paused"
    connection.protocol.resume_match()

add(resume)
   
def apply_script(protocol, connection, config):
    class MatchProtocol(protocol):
        match_in_progress = False
        match_timer_ends = 0
        match_intel_caps = {}
        match_sudden_death = False
        match_timer_call = None
        match_messages = []
        match_message_loop = None
        match_overtime = False
        blue_kill_count = 0
        green_kill_count = 0
        blue_death_count = 0
        green_death_count = 0
        paused_since = False
    
        def msg(self, message, override=False):
            if MATCH_FEED_IN_CHAT or override:
                self.send_chat(message, irc=True)
            else:
                self.irc_say(message)
    
        def begin_match(self, time):
            self.match_in_progress = True
            self.match_timer_ends = reactor.seconds() + time
            self.match_message_loop = LoopingCall(self.show_match_messages)
            self.match_message_loop.start(3)
                
            self.msg(S_MATCH_START, override=True)
    
            for p in self.players:
                player = self.players[p]
                player.spawn(player.team.get_random_location(force_land=True))
    
            # This will start a loop of callLater()s.
            self.show_match_timer()
        
        def pause_match(self):
            self.match_in_progress = False
            self.paused_since = reactor.seconds()
            self.msg(S_MATCH_PAUSED)
        
        def resume_match(self):
            self.match_timer_ends+=reactor.seconds-self.paused_since
            self.match_in_progress=True
            self.paused_since=False
            self.msg(S_MATCH_RESUMED)
            self.show_match_timer()

        def end_match(self, cancelled=False, cancelled_by=None):
            if self.green_team.score > self.blue_team.score:
                result = S_MATCH_OVER.format(team=self.green_team.name, score1=self.green_team.score, score2=self.blue_team.score)
            elif self.blue_team.score > self.green_team.score:
                result = S_MATCH_OVER.format(team=self.blue_team.name, score1=self.blue_team.score, score2=self.green_team.score)
            elif not cancelled:
                if (not MATCH_ALLOW_TIES) or (MATCH_EXTEND_ON_TIE and not self.match_overtime):
                    self.match_sudden_death = MATCH_SUDDEN_DEATH_ON_TIE
                    self.match_overtime = True
                    self.extend_match(MATCH_OVERTIME_LENGTH * 60)
                    self.show_match_timer()
                    return
                else:
                    result = S_MATCH_TIE
            greenkills = self.green_kill_count
            greendeaths = float(max(1,self.green_death_count))
            bluekills = self.blue_kill_count
            bluedeaths = float(max(1,self.blue_death_count))
            self.match_messages.append("Green team had %s kill(s) and %s death(s) (%.2f). Blue team had %s kill(s) and %s death(s) (%.2f)." % (self.green_kill_count, self.green_death_count, greenkills/greendeaths, self.blue_kill_count, self.blue_death_count, bluekills/bluedeaths))
            for player in self.players.values():
                player.drop_flag()
                if player.kill_count > 0 or player.death_count > 0:
                    self.match_messages.append("%s has %s kill(s) and %s death(s) (%.2f) and (%s Caps, %s Grabs)." % (player.name, player.kill_count, player.death_count, player.kill_count/float(max(1,player.death_count)), player.cap_count, player.grab_count))
            self.match_in_progress = False
            self.match_sudden_death = False
            self.match_timer_ends = 0
            self.green_kill_count = 0
            self.blue_kill_count = 0
            self.green_death_count = 0
            self.blue_death_count = 0
            for player in self.players.values():
                player.kill_count = 0
                player.death_count = 0
                player.cap_count = 0
                player.grab_count = 0
                
            if cancelled:
                self.match_timer_call.cancel()
                msg = S_MATCH_CANCELLED.format(who=cancelled_by)
                self.msg(msg, override=True)
            else:
                self.msg(result, override=True)
    
        def extend_match(self, time):
            self.match_timer_ends += time
            sd = S_MATCH_SUDDEN_DEATH if self.match_sudden_death else ''
            self.msg(S_MATCH_TIE_EXTEND.format(time=prettify_timespan(time, get_seconds=True), sd=sd), override=True)
    
        def show_match_timer(self):
            
            time_left = [self.match_timer_ends - reactor.seconds(),self.match_timer_ends-self.paused_since][self.paused_since]
            if time_left > 60:
                next_call = 60
            else:
                next_call = max(1, int(time_left / 2.0))
                
            if time_left <= 0:
                if self.match_in_progress:
                    self.end_match()
                return
            if self.paused_since:
                self.msg(S_MATCH_PAUSED)
            else:
                self.msg(S_MATCH_TIMELEFT.format(time=prettify_timespan(time_left, get_seconds=True)), override=True)
            self.match_timer_call = reactor.callLater(next_call, self.show_match_timer)
        
        def show_match_messages(self):
            if len(self.match_messages) == 0:
                if not self.match_in_progress:
                    self.match_message_loop.stop()
                return
            message = self.match_messages.pop(0)
            self.msg(message)
    
    
    class MatchConnection(connection):
        connection.kill_count = 0
        connection.death_count = 0
        connection.cap_count = 0
        connection.grab_count = 0
        def on_block_build_attempt(self, x, y, z):
            if not self.protocol.match_in_progress:
                return False
            return connection.on_block_build_attempt(self, x, y, z)
    
        def on_block_destroy(self, x, y, z, value):
            if not self.protocol.match_in_progress:
                return False
            return connection.on_block_destroy(self, x, y, z, value)
    
        def on_chat(self, message, global_message):
            if MATCH_DISABLE_CHAT and global_message and self.protocol.match_in_progress and not any(
                    t in self.user_types for t in ('admin', 'moderator', 'guard', 'trusted')):
                self.send_chat("Global messages are disabled for the duration of this match.")
                return False
            return connection.on_chat(self, message, global_message)
    
        def on_command(self, command, arguments):
            if self.protocol.match_in_progress and command == 'time':
                time_left = self.protocol.match_timer_ends - reactor.seconds()
                msg = S_MATCH_TIMELEFT.format(time=prettify_timespan(time_left, get_seconds=True))
                self.send_chat(msg)
                return False
            if command == 'kill':
                self.kill()
            return connection.on_command(self, command, arguments)
    
        def on_flag_drop(self):
            x, y, z = self.get_location()
            if self.team.other.flag.z >= 63:
                x_offset = 3
                y_offset = 5
                z = self.protocol.map.get_z(x, y)
                if y > 256:
                    self.team.other.flag.set(x - x_offset, y - y_offset, z - 1)
                if y < 256:
                    self.team.other.flag.set(x - x_offset, y + y_offset, z - 1)
                self.team.other.flag.update()
            self.protocol.match_messages.append(S_MATCH_FLAG_DROP.format(
                who=self.printable_name, team=self.team.other.name, coord=to_coordinates(x,y)))
            return connection.on_flag_drop(self)
    
        def on_flag_take(self):
            if not self.protocol.match_in_progress:
                return False
            x, y, z = self.get_location()
            self.grab_count += 1
            self.protocol.match_messages.append(S_MATCH_FLAG_TAKE.format(
                who=self.printable_name, team=self.team.other.name, coord=to_coordinates(x,y)))
            return connection.on_flag_take(self)
    
        def on_flag_capture(self):
            if not self.protocol.match_in_progress:
                return False
            self.cap_count += 1
            result = connection.on_flag_capture(self)
            message = S_MATCH_FLAG_CAP.format(
                    who=self.printable_name, team=self.team.other.name)
    
            if self.protocol.match_sudden_death:
                self.protocol.msg(message, override=True)
                self.protocol.end_match()
            else:
                self.protocol.match_messages.append(S_MATCH_FLAG_CAP.format(
                    who=self.printable_name, team=self.team.other.name))
    
            return result
    
        def on_kill(self, killer, type, grenade):
            if not self.protocol.match_in_progress:
                return False
            if self.team == self.protocol.green_team:
                if killer is not None and self.team is not killer.team:
                    if self != killer:
                        killer.kill_count += 1
                        self.protocol.blue_kill_count += 1
                self.death_count += 1
                self.protocol.green_death_count += 1
            if self.team == self.protocol.blue_team:
                if killer is not None and self.team is not killer.team:
                    if self != killer:
                        killer.kill_count += 1
                        self.protocol.green_kill_count += 1
                self.death_count += 1
                self.protocol.blue_death_count += 1
            self.protocol.match_messages.append(S_MATCH_KILL.format(
                killer=killer.name if killer else self.name, victim=self.name))
            return connection.on_kill(self, killer, type, grenade)
               
        def on_line_build_attempt(self, points):
            if not self.protocol.match_in_progress:
                return False
            return connection.on_line_build_attempt(self, points)
              
    return MatchProtocol, MatchConnection
