class Interface():
    """
    """
    def __init__(self, level, bg_color, pygame):
        """
        Initialize the game's interface
        """
        # Setup variables
        self._level = level
        self._pygame = pygame
        self._bg_color = bg_color

        #Start in player 1 move mode
        self.mode = Modes.Move

        # Load the level information
        # This method also initialize the screen size
        #   according to the map size
        self._loadLevel()

        #Load all the sounds
        self.sound_controller = SoundsController()

        # The status bar
        self.status_bar = pygame.Rect(0, self._map_resolution[1],
                                     self._screen_resolution[0],
                                     STATUS_BAR_HEIGHT)

        # The power bar
        self.power_bar = pygame.Rect(COLUMN_WIDTH + (COLUMN_WIDTH - 100)/2 , self._map_resolution[1] + 80,
                                     100,
                                     POWER_BAR_HEIGHT)

        self.power_outline = self.power_bar.copy()
        self.power_outline.w -= 1
        self.power_outline.h -= 1

        #Set friendly fire
        self.friendly_fire = True

        # Current power
        self.current_power = 20
        self.current_power_increasing = True

        #Initialize tanks
        self.p1_tank = Tank([70, 560], 1)
        self.p2_tank = Tank([1110, 560], 2)

        #Initilize turn number
        self.turn = 1
        #The first one to play is random (not always player 1)
        self.players_turn = randrange(2)+1

        #Set the number of turns
        self.num_teams = 2

        #Load the map information
        self._map = Map(level, bg_color)

        #Initialize the screen
        self._windowSurfaceObj = pygame.display.set_mode(self._screen_resolution)
        self._windowSurfaceObj.fill(bg_color)

        self._map.paintMountain(self._windowSurfaceObj)
        self.draw_bar()
        self.draw_tank(self.p1_tank)
        self.draw_tank(self.p2_tank)

    def _loadLevel(self):
        """
        Load the level information
        """
        #gets the filename
        filename = "maps/" + self._level + ".lvl"
        
        map_file = open(filename, 'r')
        
        # Move up to the line with the size of the map
        line = map_file.readline()
        while line.find("Size: ") < 0:
            line = map_file.readline()
            if line == "":
                raise Exception ("Expected Map Size.")

        # Get the size of the map
        line = line.lstrip("Size: ")
        line = line.strip()
        size = line.split('x')
        map_width, map_height = size
        map_width = int(map_width)
        map_height = int(map_height)

        # Move up to the line with the tank area
        line = map_file.readline()
        while line.find("TankArea: ") < 0:
            line = map_file.readline()
            if line == "":
                raise Exception ("Expected Tank Area.")

        # Get the size of the map
        line = line.lstrip("TankArea: ")
        self._tank_area = line.strip()

        self._map_resolution = (map_width, map_height)
        self._screen_resolution = (map_width, map_height+STATUS_BAR_HEIGHT)

    def update(self):
        """
        Update the interface. This method is called on every frame.
        """
        #Update the display
        self._pygame.display.update()

        # draw the bar
        self.draw_bar()

        #Check if we are supposed to draw the shot
        if self.mode == Modes.Draw_Shot:

            if self.shot_path_index < len(self.shot_path):
                
                #Erase old bullet
                if self.shot_path_index > 0:
                    pos = self.shot_path[self.shot_path_index-1]
                    x = pos[0]
                    y = pos[1]
                    self.erase_shot(x,y)

                #Draw current shot position
                pos = self.shot_path[self.shot_path_index]
                x = pos[0]
                y = pos[1]

                #Check for bounds
                if x >= 0 and y >= 0 and x < self._map_resolution[0] and   y < self._map_resolution[1]:

                    #Get the circle inside the rectangle
                    circle_rect = self.draw_shot(x,y)

                    #Check if the shot hit an obstacle
                    #  For example: if it hit the mountain or a tank
                    if self.shot_path_index > 2 and circle_rect.colliderect(self.p1_tank.get_rect()):
                        self.erase_shot(x,y)
                        self.finish_shot_firing(False, did_hit_team=1)
                    elif self.shot_path_index > 2 and circle_rect.colliderect(self.p2_tank.get_rect()):
                        self.erase_shot(x,y)
                        self.finish_shot_firing(False, did_hit_team=2)
                    elif self._map.didShotHitMountain(circle_rect, self.current_power, self._windowSurfaceObj):
                        self.erase_shot(x,y)
                        self.finish_shot_firing(True, pos=(x,y))



                #Increase the index
                self.shot_path_index += 1

            #If this is the last time, erase the shot
            elif self.shot_path_index == len(self.shot_path) and self.shot_path_index > 0:
                pos = self.shot_path[self.shot_path_index-1]
                x = pos[0]
                y = pos[1]
                self.erase_shot(x,y)

                self.finish_shot_firing(False)

            #Redraw both tanks (just in case the shot hit the tank)
            self.erase_tank(self.p1_tank)
            self.draw_tank(self.p1_tank)
            self.erase_tank(self.p2_tank)
            self.draw_tank(self.p2_tank)

            #If game over
            if self.mode == Modes.GameOver:
                #Call animation to destroy the tank
                self.explode_tank(self.enemy_team)


    def draw_bar(self):
        """
        Draws the info bar on the bottom of the screen. 
        """        
        #draw the background of the bar
        pygame.draw.rect(self._windowSurfaceObj, STATUS_BAR_COLOR, self.status_bar)
        
        #draw the outline of the bar
        outlineRect = self.status_bar.copy()
        outlineRect.w -= 1
        outlineRect.h -= 1
        pygame.draw.rect(self._windowSurfaceObj, OUTLINE_COLOR, outlineRect, 2)

        #draw lines between players information
        pygame.draw.line(
            self._windowSurfaceObj,
            OUTLINE_COLOR,
            (COLUMN_WIDTH, self._map_resolution[1]),
            (COLUMN_WIDTH, self._map_resolution[1]+STATUS_BAR_HEIGHT))

        pygame.draw.line(
            self._windowSurfaceObj,
            OUTLINE_COLOR,
            (2*COLUMN_WIDTH, self._map_resolution[1]),
            (2*COLUMN_WIDTH, self._map_resolution[1]+STATUS_BAR_HEIGHT))

        #draw player 1's information
        y = 0
        y += 5 + self.draw_info_text('Player 1', MEDIUM_FONT, MEDIUM_FONT_SIZE, y, 0)
        y += self.draw_info_text('HP:        {:10.1f}%'.format(self.p1_tank.get_hp_as_percentage()), FONT, FONT_SIZE, y, 0)
        y += self.draw_info_text('Angle:    {:10.1f}°'.format(self.p1_tank.get_angle()), FONT, FONT_SIZE, y, 0)
        self.draw_info_text('Power:  {:10.1f}%'.format(self.p1_tank.get_power_as_percentage()), FONT, FONT_SIZE, y, 0)

        #draw game information
        y = 0
        if self.mode == Modes.GameOver:
            y += 5 + self.draw_info_text('Game Over!', BIG_FONT, BIG_FONT_SIZE, y, 1)
            y += self.draw_info_text('Player {} won!'.format(self.players_turn), MEDIUM_FONT, MEDIUM_FONT_SIZE, y, 1)
        else:
            y += 5 + self.draw_info_text('Day {}'.format(self.turn), BIG_FONT, BIG_FONT_SIZE, y, 1)
            y += self.draw_info_text('Player {}\'s turn'.format(self.players_turn), FONT, FONT_SIZE, y, 1)


        #If we are firing, draw the power bar
        if self.mode == Modes.Firing:
            
            self.calculate_power()
            self.power_bar.w = self.current_power
            
            pygame.draw.rect(self._windowSurfaceObj, (POWER_BAR_COLOR[0]+self.current_power,POWER_BAR_COLOR[1]-self.current_power,POWER_BAR_COLOR[2]) , self.power_bar)
            pygame.draw.rect(self._windowSurfaceObj, OUTLINE_COLOR, self.power_outline, 2)


        #draw player 2's information
        y = 0
        y += 5 + self.draw_info_text('Player 2', MEDIUM_FONT, MEDIUM_FONT_SIZE, y, 2)
        y += self.draw_info_text('HP:        {:10.1f}%'.format(self.p2_tank.get_hp_as_percentage()), FONT, FONT_SIZE, y, 2)
        y += self.draw_info_text('Angle:    {:10.1f}°'.format(abs(self.p2_tank.get_angle())), FONT, FONT_SIZE, y, 2)
        self.draw_info_text('Power:  {:10.1f}%'.format(self.p2_tank.get_power_as_percentage()), FONT, FONT_SIZE, y, 2)



    def draw_info_text(self, text, font, font_size, y, column):
        """
        Draws given text with given information.
        """
        line_text = font.render(text, True, FONT_COLOR)
        self._windowSurfaceObj.blit(
            line_text,
            (column*COLUMN_WIDTH + PAD, self._map_resolution[1] + y + PAD))
        return font_size + PAD

    def draw_tank(self, tank):
        """
        Draws given tank
        """
        pos = tank.get_position()
        barrel_pos = tank.get_barrel_position()

        #Rotate the barrel image
        barrel_img = pygame.transform.rotate(tank.image_barrel, tank.get_angle())

        #Calculate the barrel's fixed position - because of the rotation
        y = math.sin(math.radians(abs(tank.get_angle())))*44

        #Defines the x according to the tank and update the initial shot position
        if tank.team == 1:
            x = 0
            tank.set_shot_start_position(barrel_pos[0] + barrel_img.get_width(), barrel_pos[1]-y)
        else: 
            x = 56 - barrel_img.get_width()
            tank.set_shot_start_position(barrel_pos[0] + x, barrel_pos[1]-y)

        #Draw the tank and the barrel
        self._windowSurfaceObj.blit(tank.image, (pos[0],pos[1]))
        self._windowSurfaceObj.blit(barrel_img, (barrel_pos[0] + x,barrel_pos[1]-y))
        pygame.display.flip()

    def draw_shot(self, x,y):
        """
        Draw a shot on given positon
        """
        return pygame.draw.circle(self._windowSurfaceObj, SHOT_COLOR, (x,y), SHOT_RADIUS)

    def erase_shot(self, x,y):
        """
        Erase a shot on given position
        """
        pygame.draw.circle(self._windowSurfaceObj, self._bg_color, (x,y), SHOT_RADIUS)


    def calculate_power(self):
        """
        Calculates the new power.

        It should increase every frame until it reaches 100.
        After, it decreases until 20 - and keeps going like this until
        the user hits space again
        """
        if self.current_power > 80:
            rate = 3.5
        elif self.current_power > 60:
            rate = 2.3
        elif self.current_power > 40:
            rate = 1.8
        elif self.current_power > 20:
            rate = 1.1
        else:
            rate = 0.5

        if self.current_power_increasing:
            self.current_power += rate

            if self.current_power >= 100:
                self.current_power = 100
                self.current_power_increasing = False

        else:
            self.current_power -= rate

            if self.current_power <= 0:
                self.current_power = 0
                self.current_power_increasing = True

        self.cur_team.update_power(self.current_power)

    def erase_tank(self, tank):
        """
        Erases tank
        """
        pygame.draw.rect(self._windowSurfaceObj, self._bg_color, (tank.position[0],tank.position[1]-45,103,85))
        #pygame.draw.rect(self._windowSurfaceObj, self._bg_color, tank.get_rect())
    
    def explode_tank(self, tank):
        """
        Animation to destroy tank
        """
        self.erase_tank(tank)

    @property
    def cur_team(self):
        """
        Returns the string name of the tank who's turn it currently is. 
        """
        if self.players_turn == 1:
            return self.p1_tank
        else:
            return self.p2_tank
       
    @property
    def enemy_team(self):
        """
        Returns the string name of the tank who's turn it currently is. 
        """
        if self.players_turn == 1:
            return self.p2_tank
        else:
            return self.p1_tank


    def move_tank(self, value):
        """
        Move the tank according to if the left or right arrow was pressed

        """
        if self.mode != Modes.Move:
            return
        
        #Get the current tank
        current_tank = self.cur_team

        #Erase it
        self.erase_tank(current_tank)

        #Move it accordingly
        if value == "left":
            current_tank.move_tank([-3,0])

        else:
            current_tank.move_tank([3,0])
        
        #Redraw it
        self.draw_tank(current_tank)

    def change_angle(self, value):
        """
        Change the angle of the tank barrel according to if the up 
        or down arrow key was pressed
        """
        if self.mode != Modes.Move:
            return

        #Get the current tank
        current_tank = self.cur_team

        #Erase it
        self.erase_tank(current_tank)

        #Change the angle accordingly
        if value == "up":
            current_tank.change_barrel_angle(1)

        else:
            current_tank.change_barrel_angle(-1)
            
        #Redraw the tank
        self.draw_tank(current_tank)


    def select_power(self):
        """
        After hitting space to fire, we need to use a timer or somthing
        between events to calculate how hard the shot should be.
        This will eventually read from the arduino potentiometer value and pass that 
        to the shot class. 
        So maybe for now we will hard code it to be (0-100)

        """
        if self.mode != Modes.Move:
            return

        self.change_mode(Modes.Firing)
        
        #self.draw_power_bar()
        #self.


    def release_power(self):
        if self.mode != Modes.Firing:
            return

        self.fire_shot()
        self.current_power = 20
        self.current_power_increasing = True


    def fire_shot(self):
        """
        Creates a shot according toa power value given
        Then creates the effects that follow a shot being fired
        """
        
        self.change_mode(Modes.Draw_Shot)

        enemy_tank = self.enemy_team
        current_tank = self.cur_team

        self.current_shot = Shot(self.current_power, current_tank.get_angle(), current_tank, enemy_tank, self._map_resolution[1], self._map_resolution[0])
        self.shot_path = self.current_shot.get_path()
        self.shot_path_index = 0 

        #play shot sound
        self.sound_controller.play("TankFire");
        #self.sound_controller.play("BombDrop");
         

    def finish_shot_firing(self, didHitMountain, did_hit_team=0, pos=None):
        """
        This method is called after we finish drawing the shot and need to finish the player's turn
        """

        enemy_tank = self.enemy_team
        current_tank = self.cur_team


        if self.players_turn == 1:
            enemy_team_number = 2
        else:
            enemy_team_number = 1

        if pos:
            self.explosion(pos[0], pos[1], 15)
        elif did_hit_team != 0:
            if did_hit_team == 1:
                pos = [(self.p1_tank.get_rect().x + (self.p1_tank.get_rect().w/2)),(self.p1_tank.get_rect().y + (self.p1_tank.get_rect().h/2))]
            elif did_hit_team == 2:
                pos = [(self.p2_tank.get_rect().x + (self.p2_tank.get_rect().w/2)),(self.p2_tank.get_rect().y + (self.p2_tank.get_rect().h/2))]

            self.explosion(pos[0], pos[1], 25)
            

        #If we didn't hit the mountain and did hit the other tank, decrease his hp
        if not didHitMountain and did_hit_team == enemy_team_number:
            enemy_tank.take_damage(self.current_power*HIT_PERCENTAGE)

            #If the enemy was killed, game over!
            if not enemy_tank.active:
                self.change_mode(Modes.GameOver)
                self.erase_tank(enemy_tank)
                return

        #If we didn't hit the mountain, friendly fire is on and we did hit ourselves
        if not didHitMountain and self.friendly_fire and did_hit_team == self.players_turn:
            current_tank.take_damage(self.current_power*HIT_PERCENTAGE)

            #If the played killed himself, game over!
            if not current_tank.active:
                if self.players_turn == 1:
                    self.players_turn = 2
                else:
                    self.players_turn = 1

                self.change_mode(Modes.GameOver)
                self.erase_tank(current_tank)
                return

        self.change_mode(Modes.Move)
        self.next_turn()


    def next_turn(self):
        self.turn +=1

        #Updates the the player's turn variable
        # Which is, whoever turn is it
        if self.players_turn == 1:
            self.players_turn = 2
        else:
            self.players_turn = 1


    def change_mode(self, mode):
        if self.mode == mode:
            return

        self.mode = mode

    def explosion(self, x, y, radius):
        """
        Creates an explosion animation centered at x,y with a radius
            Note: the animation is poorly done, but it's better than nothing.
                   and there's no time to improve it, unfortunatelly
        """
        self.sound_controller.play("Explosion")

        for i in range(1,10):
            pygame.draw.circle(self._windowSurfaceObj, ( round(100+(i*10)),round(100-(i*10)),0) , (round(x),round(y)), round((radius/10)*i))
            self._pygame.display.update()

        for i in range(10,1):
            pygame.draw.circle(self._windowSurfaceObj, ( round(100+(i*10)),round(100-(i*10)),0) , (round(x),round(y)), round((radius/10)*i))
            self._pygame.display.update()
        
        pygame.draw.circle(self._windowSurfaceObj, self._bg_color, (round(x),round(y)), round(radius))