#pysprint_tracks.py
import math
import json
import random
from numpy import False_, object_, positive
from numpy.lib.index_tricks import _fill_diagonal_dispatcher
from numpy.lib.polynomial import poly
import pygame
from pygame import Surface, gfxdraw, draw

from loguru import logger
from gfx.cone import Cone
from managers.font_manager import FontManager

DEBUG_OBSTACLES = False
DEBUG_RAMPS = False
DISABLE_LOGGING = True
if DISABLE_LOGGING:
    logger.remove()

#Spills
oil_spill_image = None
water_spill_image = None
grease_spill_image = None
oil_spill_mask = None
water_spill_mask = None
grease_spill_mask = None

#Wrench
wrench_image = None
wrench_mask = None
wrench_position = None
wrench_display_interval = 7000
wrench_display_duration = 240000

#Traffic Cone
traffic_cone = None
traffic_cone_mask = None
traffic_cone_shade = None

#Tornado
tornado_frames = None
tornado_frames_masks = None
tornado_mask = None

#Bonus Frames
bonus_frames = None
bonus_frames_masks = None
bonus_shade_frames = None
bonus_position = None
bonus_display_interval = 6000
bonus_display_duration = 10000

#Poles Frames
poles_frames = None
poles_frames_masks = None
poles_gate = None
poles_pop_up_interval = 900
poles_stay_up_duration = 1800
#Max and min random time for a gate to be closed or open
max_random_time = 3000
min_random_time = 2000

game_display = None
display_width = None
display_height = None
road_gate_frames = None
road_gate_mask_frames = None
road_gate_shade_frames = None

def calculate_distance(point1,point2):
    return math.sqrt( ((point1[0]-point2[0])**2)+((point1[1]-point2[1])**2))

class Track:

    def __init__(self):

        self.font_manager = FontManager.get_manager("fonts")

        self.difficulty_level = None
        self.wrenches = None
        self.background_filename = None
        self.track_mask_filename = None
        self.track_upper_mask_filename = None
        self.overlay_filename =  None
        self.thumbnail_filename = None
        self.background = None
        self.base_mask = None
        self.track_mask = None
        self.track_mask_mask = None
        self.track_upper_mask = None
        self.track_upper_mask_mask = None
        self.track_overlay = None
        self.thumbnail = None
        self.first_car_start_position = None
        self.flag_anchor = None
        self.track_number = None
        self.start_sprite_angle = None
        self.score_time_reference = None
        self.complete_lap_score = None
        self.external_borders = None
        self.internal_borders = None
        self.secondary_internal_borders = None
        self.finish_line_rect = None
        self.finish_line = None
        self.finish_line_direction = None
        #To check if players have driven through mandatory checkpoints (i.e. roundabouts)
        self.mandatory_gates = None
        #External gate points
        self.external_gate_points = None
        self.internal_gate_points = None
        self.gate_surfs = []
        self.gate_masks = []
        #Track Opening Gates
        self.road_gates_anchors = None
        self.road_gates_frames_index = []
        self.road_gates_timers = []
        self.road_gates_opening = []
        #Ramps
        self.ramp_gates = None
        self.ramp_masks = []
        self.ramp_surfs = []
        #Bridges
        self.bridge_gates = None
        self.bridge_masks = []
        self.bridge_surfs = []
        #When timer = next event time open or close the gate depending on status
        self.external_ai_gates_shortcuts = None
        self.internal_ai_gates_shortcuts = None
        self.ai_gate_surfs = []
        self.ai_gate_masks = []
        #Player Shortcuts
        self.external_player_gates_shortcut = None
        self.internal_player_gates_shortcut = None
        self.player_shortcut_bookend_gates = None
        #Timer for Bonus display animation
        self.bonus_timer = None
        self.bonus_displayed = False
        self.bonus_rolling = False
        self.bonus_frame_index = None
        self.bonus_value = None
        self.bonus_position = None
        self.on_bridge_or_ramp_bonus = False
        #Timer for Wrench display
        self.wrench_timer = None
        self.wrench_displayed = False
        self.wrench_position = None
        self.on_bridge_or_ramp_wrench = False
        #Obstacles
        self.display_pole = False
        self.display_tornado = False
        self.display_oil_spill = False
        self.display_grease_spill = False
        self.display_water_spill = False
        self.display_cones = False
        self.on_bridge_pole = False
        self.on_bridge_oil_spill = False
        self.on_bridge_grease_spill = False
        self.on_bridge_water_spill = False

        self.cones_count = 0
        #Spills
        self.oil_spill_position = None
        self.water_spill_position = None
        self.grease_spill_position = None
        #Timer for Poles display animation
        self.poles_timer = None
        self.poles_popping_up = False
        self.poles_popping_down = False
        self.poles_frame_indexes = None
        self.poles_moving_index = None
        self.poles_gate_index = None
        self.pole_mask = None
        self.external_pole_position = None
        self.internal_pole_position = None
        self.middle_pole_position = None

        #Traffic Cones
        self.traffic_cones = []

        #All static Obstacles Gates
        self.obstacle_gates = []

        #Tornado
        self.tornado_position = None
        self.tornado_timer = None
        self.tornado_frame_index = None
        self.tornado_mask = None

    def load_track_definition(self, filename):
        with open(filename) as track_file:
            track_json = json.load(track_file)

        self.track_number = track_json["track_number"]
        self.difficulty_level = track_json["difficulty_level"]
        self.wrenches = track_json["wrenches"]
        self.background_filename = track_json["background_filename"]
        self.track_mask_filename = track_json["mask_filename"]
        if "upper_mask_filename" in track_json:
            self.track_upper_mask_filename = track_json["upper_mask_filename"]
        self.overlay_filename =  track_json["overlay_filename"]
        if "thumbnail_filename" in track_json:
            self.thumbnail_filename =  track_json["thumbnail_filename"]

        self.first_car_start_position = track_json["first_car_start_position"]
        self.flag_anchor = track_json["flag_anchor"]
        self.start_sprite_angle = track_json["start_sprite_angle"]
        self.score_time_reference = track_json["score_time_reference"]
        self.complete_lap_score = track_json["complete_lap_score"]
        self.external_borders = track_json["external_borders"]
        self.internal_borders = track_json["internal_borders"]
        if "secondary_internal_borders" in track_json:
            self.secondary_internal_borders = track_json["secondary_internal_borders"]
        self.finish_line_rect = track_json["finish_line_rect"]
        self.finish_line_direction = track_json["finish_line_direction"]

        self.external_gate_points = track_json["external_gate_points"]
        self.internal_gate_points = track_json["internal_gate_points"]
        for i in range(len(self.external_gate_points)):
            self.gate_surfs.append(pygame.Rect(min(self.internal_gate_points[i][0],self.external_gate_points[i][0]), min(self.internal_gate_points[i][1],self.external_gate_points[i][1]), abs(self.internal_gate_points[i][0]-self.external_gate_points[i][0])+1, abs(self.internal_gate_points[i][1]-self.external_gate_points[i][1])+1))

        if "mandatory_gates" in track_json:
            self.mandatory_gates = track_json["mandatory_gates"]
        if "road_gates_anchors" in track_json:
            self.road_gates_anchors = track_json["road_gates_anchors"]
        if "external_ai_gates_shortcuts" in track_json:
            self.external_ai_gates_shortcuts = track_json["external_ai_gates_shortcuts"]
        if "internal_ai_gates_shortcuts" in track_json:
            self.internal_ai_gates_shortcuts = track_json["internal_ai_gates_shortcuts"]
        if "external_player_gates_shortcut" in track_json:
            self.external_player_gates_shortcut = track_json["external_player_gates_shortcut"]
        if "internal_player_gates_shortcut" in track_json:
            self.internal_player_gates_shortcut = track_json["internal_player_gates_shortcut"]

        if "player_shortcut_ramp_gates" in track_json:
            self.ramp_gates = track_json["player_shortcut_ramp_gates"]
            for ramp in self.ramp_gates:
                new_ramp = []
                for polygon in ramp:
                    gate_points = []
                    i = 0
                    while i<len(polygon):
                        gate_points.append(self.external_player_gates_shortcut[polygon[i]])
                        i+=1
                    i = len(polygon) - 1
                    while i>=0:
                        gate_points.append(self.internal_player_gates_shortcut[polygon[i]])
                        i-=1
                    ramp_surf = pygame.Surface((display_width,display_height))
                    ramp_surf.fill((0,0,0))
                    ramp_surf.set_colorkey((0,0,0))
                    pygame.draw.polygon(ramp_surf,(34,170,102),gate_points)
                    ramp_mask = pygame.mask.from_surface(ramp_surf, 50)
                    new_ramp.append(ramp_mask)
                    self.ramp_surfs.append(ramp_surf)
                self.ramp_masks.append(new_ramp)
        if "player_shortcut_bookend_gates" in track_json:
            self.player_shortcut_bookend_gates = track_json["player_shortcut_bookend_gates"]

        if "ramp_gates" in track_json:
            self.ramp_gates = track_json["ramp_gates"]
            for ramp in self.ramp_gates:
                new_ramp = []
                for polygon in ramp:
                    gate_points = []
                    i = 0
                    while i<len(polygon):
                        gate_points.append(self.external_gate_points[polygon[i]])
                        i+=1
                    i = len(polygon) - 1
                    while i>=0:
                        gate_points.append(self.internal_gate_points[polygon[i]])
                        i-=1
                    ramp_surf = pygame.Surface((display_width,display_height))
                    ramp_surf.fill((0,0,0))
                    ramp_surf.set_colorkey((0,0,0))
                    pygame.draw.polygon(ramp_surf,(34,170,102),gate_points)
                    ramp_mask = pygame.mask.from_surface(ramp_surf, 50)
                    new_ramp.append(ramp_mask)
                    self.ramp_surfs.append(ramp_surf)
                self.ramp_masks.append(new_ramp)

        if "bridge_gates" in track_json:
            self.bridge_gates = track_json["bridge_gates"]
            for bridge in self.bridge_gates:
                new_bridge = []
                for polygon in bridge:
                    gate_points = []
                    i = 0
                    while i<len(polygon):
                        gate_points.append(self.external_gate_points[polygon[i]])
                        i+=1
                    i = len(polygon) - 1
                    while i>=0:
                        gate_points.append(self.internal_gate_points[polygon[i]])
                        i-=1
                    bridge_surf = pygame.Surface((display_width,display_height))
                    bridge_surf.fill((0,0,0))
                    bridge_surf.set_colorkey((0,0,0))
                    pygame.draw.polygon(bridge_surf,(34,170,102),gate_points)
                    bridge_mask = pygame.mask.from_surface(bridge_surf, 50)
                    new_bridge.append(bridge_mask)
                    self.bridge_surfs.append(bridge_surf)
                self.bridge_masks.append(new_bridge)

    def find_gate_point(self, position, gate_points):
        shortest_distance = -1
        shortest_index = -1
        for i in range (0, len(gate_points)):
            distance =  calculate_distance(gate_points[i], position)
            if (shortest_distance < 0) or (distance < shortest_distance):
                shortest_distance = distance
                shortest_index = i
        return shortest_index

    def find_shortcut_point(self, position, ai_shortcut_points, currentgate):
        shortest_distance = -1
        shortest_index = -1
        for i in range(0, len(self.road_gates_anchors)):
            for j in range (1, len(ai_shortcut_points[i])):
                distance =  calculate_distance(ai_shortcut_points[i][j], position)
                if (shortest_distance < 0) or (distance < shortest_distance):
                    shortest_distance = distance
                    shortest_index = (i,j)
        #Check if we have an open gate close by and if we can take the shortcut
        #if current gate is a tuple, we already engaged in a shortcut,
        #we need to continue on it regardless of teh gate status
        if currentgate is None or not type(currentgate) is tuple:
            #If we're not already in a shortcut then we only return a shortcut if the gate is open or opening
            if not self.road_gates_frames_index[i] == 4 or not self.road_gates_opening[i]:
                return -1
            #And if the shortcut gate detected is the first one of teh shortcut (i.e. index = 1)
            if shortest_index[1]>1:
                return -1
        return shortest_index

    def find_progress_gate(self, position, actual_gate_step = None, currentgate = None, car = None):
        #Check closest Internal and external gate point,
        int_index = self.find_gate_point(position, self.internal_gate_points)
        ext_index = self.find_gate_point(position, self.external_gate_points)

        closest_gate_index = None
        #If External & Internal Points belong to widely distant gates, return lowets gate
        #o rthe closest to the current gate when available
        if abs(ext_index-int_index)>= 3:
            if currentgate is None or type(currentgate) is tuple:
                if ext_index > int_index:
                    closest_gate_index = int_index
                else:
                    closest_gate_index = ext_index
            else:
                if abs(int_index-currentgate) < abs(ext_index-currentgate):
                    closest_gate_index = int_index
                else:
                    if abs(int_index-currentgate) > abs(ext_index-currentgate):
                        closest_gate_index = ext_index
                    else:
                        if ext_index > int_index:
                            closest_gate_index = int_index
                        else:
                            closest_gate_index = ext_index
            #Eliminate index for which Vector collides with track
            if not car is None:
                if car.test_vector_track_collision(self,int_index):
                    closest_gate_index = ext_index
                else:
                    if car.test_vector_track_collision(self,ext_index):
                        closest_gate_index = int_index
        else:
            #Return the highest index to ignore postion relative to borders
            if ext_index >= int_index:
                closest_gate_index = ext_index
            else:
                closest_gate_index = int_index

        closest_shortcut_index = None
        if not actual_gate_step is None and not  self.internal_ai_gates_shortcuts is None:
            int_shortcut = self.find_shortcut_point(position, self.internal_ai_gates_shortcuts, currentgate)
            ext_shortcut = self.find_shortcut_point(position, self.external_ai_gates_shortcuts, currentgate)

            if type(int_shortcut) is tuple and type(ext_shortcut) is tuple:
                #If External & Internal Points belong to wodely distant gates, return the lowest one
                if abs(ext_shortcut[1]-int_shortcut[1])>= 3:
                    if ext_shortcut[1] > int_shortcut[1]:
                        closest_shortcut_index = int_shortcut
                    else:
                        closest_shortcut_index = ext_shortcut
                else:
                    #Return the highest index to ignore postion relative to borders
                    if ext_shortcut[1] >= int_shortcut[1]:
                        closest_shortcut_index = ext_shortcut
                    else:
                        closest_shortcut_index = int_shortcut

        #if current gate is a tuple, we already engaged in a shortcut,
        #we need to continue on it regardless i.e. force usage of a shortcut gate
        #Except f we already are on the last gate of teh shortcut
        if not currentgate is None and type(currentgate) is tuple:
            if currentgate[1] < len(self.external_ai_gates_shortcuts[currentgate[0]]):
                if not closest_shortcut_index is None:
                    return closest_shortcut_index
        #if we detect a potential shortcut determine if the next gate is closer to the car than the shortcut gate
        if not closest_shortcut_index is None:
            next_gate_index = closest_gate_index + actual_gate_step
            if next_gate_index >= len(self.external_gate_points):
                next_gate_index -= len(self.external_gate_points)

            next_mid_point = ((self.external_gate_points[next_gate_index][0] + self.internal_gate_points[next_gate_index][0])/2, (self.external_gate_points[next_gate_index][1] + self.internal_gate_points[next_gate_index][1])/2)
            shortcut_mid_point = ((self.external_ai_gates_shortcuts[closest_shortcut_index[0]][closest_shortcut_index[1]][0] + self.internal_ai_gates_shortcuts[closest_shortcut_index[0]][closest_shortcut_index[1]][0])/2, (self.external_ai_gates_shortcuts[closest_shortcut_index[0]][closest_shortcut_index[1]][1] + self.internal_ai_gates_shortcuts[closest_shortcut_index[0]][closest_shortcut_index[1]][1])/2)
            if calculate_distance(next_mid_point, position)>calculate_distance(shortcut_mid_point,position):
                return closest_shortcut_index
        return closest_gate_index

    def get_score_from_laptime(self, laptime):
        if laptime == 0:
            return 0
        else:
            #calculate score based on difference between best laptime and reference lap time for the track
            score = math.ceil((500 * (1 + self.score_time_reference/(laptime/1000)) - 600) / 10) * 10
            if score < 0:
                return 0
            else:
                return score

    def update_score_from_position(self, car):
        if car.last_passed_gate is None:
            return
        index = car.last_passed_gate
        score_increment = 0
        if not type(index) is tuple:
            score_increment = math.ceil((car.lap_count+1) * (index/len(self.internal_borders))) * 30
        #Increment score if there has been progress since last score increment
        if score_increment > car.previous_score_increment:
            car.score += score_increment - car.previous_score_increment
            car.previous_score_increment = score_increment

    def blit_background(self,race_started):
        surf = self.background.copy()
        if not self.road_gates_anchors is None:
            if len(self.road_gates_timers)==0:
                #Initialise gates status and timers
                i = 0
                for anchor in self.road_gates_anchors:
                    self.road_gates_frames_index.append(0)
                    # if race_started then set timer (gates should be closed and frozen when "get ready" is desplayed
                    if race_started:
                        self.road_gates_timers.append(pygame.time.get_ticks() + i * 500)
                        self.road_gates_opening.append(False)
                        i+=1
            if race_started:
                now = pygame.time.get_ticks()
            for i in range(0, len(self.road_gates_anchors)):
                if race_started:
                    #If gate is closed or open (frame 0 or frame 4): blit the same frame until timer is reached
                    if self.road_gates_frames_index[i] == 0 or self.road_gates_frames_index[i] == 4:
                        if now >=self.road_gates_timers[i]:
                            self.road_gates_timers[i] = now + 100
                            if self.road_gates_frames_index[i] == 0:
                                self.road_gates_opening[i] = True
                                self.road_gates_frames_index[i] += 1
                            elif self.road_gates_frames_index[i] == 4:
                                self.road_gates_opening[i] = False
                                self.road_gates_frames_index[i] -= 1
                    #If gate is closing or opening (Frames 1,2 & 3): blit the next (or previous) frame until timer is reached
                    if self.road_gates_frames_index[i] > 0 and self.road_gates_frames_index[i] < 4:
                        if now >=self.road_gates_timers[i]:
                            if self.road_gates_opening[i]:
                                self.road_gates_frames_index[i] += 1
                                #if Gate is fully open, randomize timer to start closing it
                                if self.road_gates_frames_index[i] == 4:
                                    self.road_gates_timers[i] = now + random.randint(min_random_time,max_random_time)
                                else:
                                    self.road_gates_timers[i] = now + 100
                            else:
                                self.road_gates_frames_index[i] -= 1
                                #if Gate is fully closed, randomize timer to start opening it
                                if self.road_gates_frames_index[i] == 0:
                                    self.road_gates_timers[i] = now + random.randint(min_random_time,max_random_time)
                                else:
                                    self.road_gates_timers[i] = now + 10

                surf.blit(road_gate_frames[self.road_gates_frames_index[i]],(self.road_gates_anchors[i][0],self.road_gates_anchors[i][1]))
        self.update_track_mask()
        game_display.blit(surf,(0,0))

    def blit_tornado(self, race_started):
        if self.display_tornado:
            #Display Tornado
            if self.tornado_position is None:
                self.tornado_position = (random.randint(0, self.track_mask.get_width()-tornado_frames[0].get_width()),random.randint(0, self.track_mask.get_height()-tornado_frames[0].get_height()))
                self.tornado_frame_index = 0
                self.tornado_timer = pygame.time.get_ticks()
                self.tornado_mask = tornado_frames_masks[self.tornado_frame_index]
            if race_started:
                now =  pygame.time.get_ticks()
                if now >=self.tornado_timer:
                    if self.tornado_frame_index == 0:
                        self.tornado_frame_index = 1
                    else:
                        self.tornado_frame_index = 0
                    rand_x = random.randint(self.tornado_position[0]-5,self.tornado_position[0]+5)
                    if rand_x >= self.track_mask.get_width()-tornado_frames[0].get_width():
                        rand_x = self.tornado_position[0] - 5
                    if rand_x < 0:
                        rand_x = 0
                    rand_y = random.randint(self.tornado_position[1]-5,self.tornado_position[1]+5)
                    if rand_y >= self.track_mask.get_height()-tornado_frames[0].get_height():
                        rand_y = self.tornado_position[1] - 5
                    if rand_y<0:
                        rand_y = 0
                    self.tornado_position = (rand_x,rand_y)
                    self.tornado_timer = now + 100
                self.tornado_mask = tornado_frames_masks[self.tornado_frame_index]
                game_display.blit(tornado_frames[self.tornado_frame_index],self.tornado_position)

    def blit_overlay(self, race_started):
        surf = self.track_overlay.copy()
        if not self.road_gates_anchors is None:
            for i in range(0, len(self.road_gates_anchors)):
                surf.blit(road_gate_shade_frames[self.road_gates_frames_index[i]],(self.road_gates_anchors[i][0],self.road_gates_anchors[i][1]))

        if DEBUG_RAMPS:
            for ramp in self.ramp_surfs:
                surf.blit(ramp,(0,0))
            for bridge in self.bridge_surfs:
                surf.blit(bridge,(0,0))
        game_display.blit(surf,(0,0))

    def update_track_mask(self):
        self.track_mask = self.base_mask.copy()
        if not self.road_gates_anchors is None:
            for i in range(0, len(self.road_gates_anchors)):
                self.track_mask.blit(road_gate_mask_frames[self.road_gates_frames_index[i]],(self.road_gates_anchors[i][0],self.road_gates_anchors[i][1]))
        self.track_mask_mask = pygame.mask.from_surface(self.track_mask, 50)

    def get_random_poles_position(self):
        #Pick a gate at random where teh gate is more than 70 pixels long
        random_gate = random.randint(1, len(self.external_gate_points)-1)
        while calculate_distance(self.external_gate_points[random_gate],self.internal_gate_points[random_gate])<70:
            random_gate = random.randint(0, len(self.external_gate_points)-1)
        return random_gate

    def test_object_on_track(self, position, width, height):
        object_mask = pygame.mask.from_surface(pygame.Surface((width,height)), 50)
        return  self.track_mask_mask.overlap(object_mask, (round(position[0]),round(position[1])))

    def test_object_on_bridge(self, position, width, height):
        object_mask = pygame.mask.from_surface(pygame.Surface((width,height)), 50)
        bridges_found = []
        for i in range(0, len(self.bridge_masks)):
            for j in range(0,len(self.bridge_masks[i])):
                bridge_mask = self.bridge_masks[i][j]
                if bridge_mask.overlap(object_mask, (round(position[0]),round(position[1]))):
                    bridges_found.append((i,j))
        return len(bridges_found)>0

    def test_object_on_ramp(self, position, width, height):
        object_mask = pygame.mask.from_surface(pygame.Surface((width,height)), 50)
        ramps_found = []
        for i in range(0, len(self.ramp_masks)):
            for j in range(0,len(self.ramp_masks[i])):
                ramp_mask = self.ramp_masks[i][j]
                if ramp_mask.overlap(object_mask, (round(position[0]),round(position[1]))):
                    ramps_found.append((i,j))
        return len(ramps_found)>0

    def test_ramp_gate(self, random_gate):
        #Ignore shortcuts
        if not self.ramp_gates is None and self.player_shortcut_bookend_gates is None:
            for i in self.ramp_gates:
                for j in i:
                    for k in j:
                        if k==random_gate:
                            return True
        return False

    def test_bridge_gate(self, random_gate):
        if not self.bridge_gates is None:
            for i in self.bridge_gates:
                for j in i:
                    for k in j:
                        if k==random_gate:
                            return True
        return False

    def get_random_position(self, height, width, force_gate = None):
        #Pick a gate at random and place the object randomly on the gate
        free_gate = False
        if force_gate is None:
            while not free_gate:
                random_gate = random.randint(0, len(self.external_gate_points)-1)
                free_gate = True
                if self.test_ramp_gate(random_gate):
                    free_gate=False
                else:
                    for gate_index in self.obstacle_gates:
                        if random_gate == gate_index:
                            free_gate=False
        else:
            random_gate = force_gate
        is_bridge_gate = self.test_bridge_gate(random_gate)
        self.obstacle_gates.append(random_gate)

        #define a box with the gate ahead of the one that was picked at randow and place the obstacle randomly inside it
        next_random_gate = random_gate + 1

        if next_random_gate>=len(self.external_gate_points):
            next_random_gate = 0

        if is_bridge_gate:
            if not self.test_bridge_gate(next_random_gate):
                is_bridge_gate = False

        ext_x = round(self.external_gate_points[random_gate][0] - (width/2))
        ext_y = round(self.external_gate_points[random_gate][1] - (height/2))
        int_x = round(self.internal_gate_points[next_random_gate][0] - (width/2))
        int_y = round(self.internal_gate_points[next_random_gate][1] - (height/2))

        if ext_x<int_x:
            min_x  = ext_x
            max_x = int_x
        else:
            min_x  = int_x
            max_x = ext_x

        random_x = random.randint(min_x,max_x)

        if ext_y<int_y:
            min_y  = ext_y
            max_y = int_y
        else:
            min_y  = int_y
            max_y = ext_y
        random_y = random.randint(min_y,max_y)

        #test if object is on track, if not move it closer to the most distant gate point
        if self.test_object_on_track((random_x,random_y),width,height):
            move_x = 0
            move_y = 0
            diff_max_x = max_x - random_x
            diff_min_x = min_x - random_x
            if abs(diff_max_x)>abs(diff_min_x):
                diff_x = diff_max_x
            else:
                diff_x = diff_min_x

            diff_max_y = max_y - random_y
            diff_min_y = min_y - random_y
            if abs(diff_max_y)>abs(diff_min_y):
                diff_y = diff_max_y
            else:
                diff_y = diff_min_y

            #move according to the most distant coordinate
            if abs(diff_x)>=(abs(diff_y)):
                if diff_x>0:
                    move_x=1
                elif diff_x<0:
                    move_x=-1
            else:
                if diff_y>0:
                    move_y=1
                elif diff_y<0:
                    move_y=-1

            while self.test_object_on_track((random_x,random_y),width,height):
                random_x+=move_x
                random_y+=move_y

            random_x+=move_x*3
            random_y+=move_y*3

        return ((random_x,random_y), is_bridge_gate)

    def hide_bonus(self):
        self.bonus_displayed = False
        self.bonus_rolling = False
        self.bonus_frame_index = -1
        self.bonus_timer = pygame.time.get_ticks() + bonus_display_interval

    def blit_bonus(self, race_started, overlay_blitted = False):
        #Initialize timer for bonus
        if self.bonus_timer is None:
            self.hide_bonus()
        if race_started:
            now = pygame.time.get_ticks()
            #if timer is reached: if Bonus displayed = False the we start rolling out the bonus, if False, we start rolling in.
            if now >=self.bonus_timer:
                if self.bonus_displayed:
                    if self.bonus_rolling:
                        #Animate the rolling out and display of the bonus
                        self.bonus_frame_index+=1
                        self.bonus_timer = now +100
                        if self.bonus_frame_index>3:
                            #Bonus fully displayed
                            self.bonus_frame_index=3
                            self.bonus_timer = now + bonus_display_duration
                            self.bonus_rolling = False
                    else:
                        #Now we hide the bonus
                        self.bonus_rolling = True
                        self.bonus_displayed = False
                        self.bonus_timer = now + 133
                else:
                    if self.bonus_rolling:
                        #Animate the rolling in and hiding of the bonus
                        self.bonus_frame_index-=1
                        self.bonus_timer = now + 133
                        if self.bonus_frame_index<0:
                            #Bonus Fully Hidden
                            self.bonus_frame_index=-1
                            self.bonus_timer = now + bonus_display_interval
                            self.bonus_rolling = False
                    else:
                        #Now we display the bonus
                        self.bonus_value = '{}'.format(random.randint(2,10) * 50)
                        result = self.get_random_position(16,30)
                        self.bonus_position = result[0]
                        self.on_bridge_or_ramp_bonus = result[1]
                        self.bonus_rolling = True
                        self.bonus_displayed = True
                        self.bonus_timer = now +100

        if self.bonus_frame_index>=0:
            if not overlay_blitted or self.on_bridge_or_ramp_bonus:
                slice_index = self.bonus_frame_index

                tiny_font = self.font_manager.get_truetype_font("tiny_font")
                bonus_value_surf = tiny_font.render(self.bonus_value[0:slice_index], False, (255, 255, 255))
                game_display.blit(bonus_frames[self.bonus_frame_index],self.bonus_position)
                game_display.blit(bonus_value_surf,(self.bonus_position[0]+1,self.bonus_position[1]+3))
                if self.bonus_frame_index<=2:
                    game_display.blit(bonus_shade_frames[self.bonus_frame_index],self.bonus_position)

    def hide_wrench(self):
        self.wrench_displayed = False
        self.wrench_timer = pygame.time.get_ticks() + wrench_display_interval

    def blit_wrench(self, race_started, overlay_blitted = False):
        #Initialize timer for bonus
        if self.wrench_timer is None:
            self.hide_wrench()
        if race_started:
            now = pygame.time.get_ticks()
            #if timer is reached: if Wrench displayed = False the we display the wrench if not we hide it.
            if now >=self.wrench_timer:
                if self.wrench_displayed:
                    #Now we hide the wrench
                    self.hide_wrench()
                else:
                    #Now we display the wrench
                    result = self.get_random_position(12,26)
                    self.wrench_position = result[0]
                    self.on_bridge_or_ramp_wrench = result[1]
                    self.wrench_displayed = True
                    self.wrench_timer = now + wrench_display_duration
            if self.wrench_displayed:
                if not overlay_blitted or self.on_bridge_or_ramp_wrench:
                    game_display.blit(wrench_image,self.wrench_position)

    def test_pole_on_track(self, position):
        pole_mask = pygame.mask.from_surface(pygame.Surface((10,16)), 50)
        return  self.track_mask_mask.overlap(pole_mask, (round(position[0]),round(position[1])))

    def test_pole_on_bridge(self, position):
        object_mask = pygame.mask.from_surface(pygame.Surface((10,16)), 50)
        bridges_found = []
        for i in range(0, len(self.bridge_masks)):
            for j in range(0,len(self.bridge_masks[i])):
                bridge_mask = self.bridge_masks[i][j]
                if bridge_mask.overlap(object_mask, (round(position[0]),round(position[1]))):
                    bridges_found.append((i,j))
        return len(bridges_found)>0

    def test_pole_on_ramp(self, position):
        object_mask = pygame.mask.from_surface(pygame.Surface((10,16)), 50)
        ramps_found = []
        for i in range(0, len(self.ramp_masks)):
            for j in range(0,len(self.ramp_masks[i])):
                ramp_mask = self.ramp_masks[i][j]
                if ramp_mask.overlap(object_mask, (round(position[0]),round(position[1]))):
                    ramps_found.append((i,j))
        return len(ramps_found)>0

    def init_obstacles(self, race_counter):
        #Determine the number of obstacles
        nb_obstacles = 0
        min_obstacles = 0
        max_obstacles = 0
        nb_cones_max = 0

        #No obstacles the first 2 races
        if race_counter>2:
            #No more than 1 obstacles the 3rd to 5th race
            if race_counter <6:
                max_obstacles = 1
            #6th to 11th race: max obstacles depending on difficulty (wrenches)
            elif race_counter < 12:
                max_obstacles = self.wrenches
            #12th to 18th minimum 1 obstacle
            elif race_counter < 18:
                min_obstacles = 1
                max_obstacles = self.wrenches+1
            #we can now have 4 cones
            elif race_counter < 24:
                nb_cones_max = 4
                min_obstacles = 1
                max_obstacles = self.wrenches+1
            else:
            #Formula beyond 24 races
                min_obstacles = 2 + int(round((race_counter-24)/12))
                #3 spills + Tornado + Poles
                max_obstacles = 5
                nb_cones_max = 4 + 2 * int(round((race_counter-24)/6))

        nb_obstacles = random.randint(min_obstacles,max_obstacles)
        #if we have 5 obstacles, we display all of them
        #if less, we randomly pick the obstacles
        self.display_pole = False
        self.display_tornado = False
        self.display_oil_spill = False
        self.display_grease_spill = False
        self.display_water_spill = False

        if nb_obstacles < 5:
            obstacle_count = 0
            while obstacle_count < nb_obstacles:
                new_obstacle = random.randint(0,4)
                if new_obstacle == 0 and not self.display_pole:
                    obstacle_count+=1
                    self.display_pole = True
                elif new_obstacle == 1 and not self.display_tornado:
                    obstacle_count+=1
                    self.display_tornado = True
                elif new_obstacle == 2 and not self.display_oil_spill:
                    obstacle_count+=1
                    self.display_oil_spill = True
                elif new_obstacle == 3 and not self.display_grease_spill:
                    obstacle_count+=1
                    self.display_grease_spill = True
                elif new_obstacle == 4 and not self.display_water_spill:
                    obstacle_count+=1
                    self.display_water_spill = True

        self.display_cones = False
        self.cones_count = 0

        if DEBUG_OBSTACLES:
            self.display_pole = True
            self.display_tornado = True
            self.display_oil_spill = True
            self.display_grease_spill = True
            self.display_water_spill = True
            self.display_cones = True
            self.cones_count = 16
            nb_cones_max = 16

        if nb_cones_max > 0:
            if random.randint(0,1)>0:
                self.display_cones = True

            if self.display_cones:
                self.cones_count = random.randint(3, nb_cones_max)
                if DEBUG_OBSTACLES:
                    self.cones_count = 16
                logger.debug(f"Creating {self.cones_count} cones")
                for i in range(self.cones_count):
                    cone = Cone(display=game_display, image=traffic_cone, shade_image=traffic_cone_shade)

                    self.traffic_cones.append(cone)
                    logger.debug(f"Cone at {cone.pos} added")

    def blit_obstacles(self, race_started, overlay_blitted = False):

        if self.display_pole:
            if self.poles_gate_index is None:
                self.poles_gate_index = self.get_random_poles_position()
                self.obstacle_gates.append(self.poles_gate_index)
                is_ramp_or_bridge_gate = False
                if not self.ramp_gates is None:
                    for i in self.ramp_gates:
                        for j in i:
                            for k in j:
                                if k==self.poles_gate_index:
                                    self.on_bridge_pole = True
                if not self.bridge_gates is None:
                    for i in self.bridge_gates:
                        for j in i:
                            for k in j:
                                if k==self.poles_gate_index:
                                    self.on_bridge_pole = True

                ext_x = self.external_gate_points[self.poles_gate_index][0] - 5
                ext_y = self.external_gate_points[self.poles_gate_index][1] - 8
                int_x = self.internal_gate_points[self.poles_gate_index][0] - 5
                int_y = self.internal_gate_points[self.poles_gate_index][1] - 8

                #test if pole is on track, if not move it closer to the opposite gate point
                if self.test_pole_on_track((int_x,int_y)):
                    move_x = 0
                    move_y = 0
                    diff_x = ext_x - int_x
                    diff_y = ext_y - int_y
                    #move according to the most distant coordinate
                    if abs(diff_x)>=(abs(diff_y)):
                        if diff_x>0:
                            move_x=1
                        elif diff_x<0:
                            move_x=-1
                    else:
                        if diff_y>0:
                            move_y=1
                        elif diff_y<0:
                            move_y=-1
                    while self.test_pole_on_track((int_x,int_y)):
                        int_x+=move_x
                        int_y+=move_y
                    int_x+=move_x*3
                    int_y+=move_y*3

                if self.test_pole_on_track((ext_x,ext_y)):
                    move_x = 0
                    move_y = 0
                    while self.test_pole_on_track((ext_x,ext_y)):
                        diff_x = int_x - ext_x
                        diff_y = int_y - ext_y
                        #move according to the most distant coordinate
                        if abs(diff_x)>=(abs(diff_y)):
                            if diff_x>0:
                                move_x=1
                            elif diff_x<0:
                                move_x=-1
                        else:
                            if diff_y>0:
                                move_y=1
                            elif diff_y<0:
                                move_y=-1
                        ext_x+=move_x
                        ext_y+=move_y
                    ext_x+=move_x*15
                    ext_y+=move_y*15

                self.external_pole_position = (ext_x,ext_y)
                self.internal_pole_position = (int_x,int_y)
                self.middle_pole_position = ((ext_x+int_x)/2,(ext_y+int_y)/2)

            if self.poles_timer is None:
                self.poles_timer =  pygame.time.get_ticks()
                self.poles_frame_indexes = [0,0,0]
                self.poles_moving_index = 0
                self.poles_popping_down = False
                self.poles_popping_up = True
            if race_started and not self.poles_gate_index is None:
                now =  pygame.time.get_ticks()
                if now >=self.poles_timer:
                    if self.poles_popping_up:
                        #Animate the popping up of the active pole
                        self.poles_frame_indexes[self.poles_moving_index]+=1
                        self.poles_timer = now +100
                        if self.poles_frame_indexes[self.poles_moving_index]>3:
                            #Pole fully popped up wait to pop down
                            self.poles_frame_indexes[self.poles_moving_index]=3
                            self.poles_timer = now + poles_stay_up_duration
                            self.poles_popping_up = False
                            self.poles_popping_down = True
                    elif self.poles_popping_down:
                        #Animate the popping down of the active pole
                        self.poles_frame_indexes[self.poles_moving_index]-=1
                        self.poles_timer = now +100
                        if self.poles_frame_indexes[self.poles_moving_index]<0:
                            #Pole fully popped up - move to the next pole and wait
                            self.poles_frame_indexes[self.poles_moving_index]=0
                            self.poles_timer = now + poles_pop_up_interval
                            self.poles_popping_up = True
                            self.poles_popping_down = False
                            self.poles_moving_index+=1
                            if self.poles_moving_index>2:
                                self.poles_moving_index = 0

                self.pole_mask = poles_frames_masks[0]
                if self.poles_frame_indexes[0]>0:
                    self.pole_mask = poles_frames_masks[self.poles_frame_indexes[0]]
                if self.poles_frame_indexes[1]:
                    self.pole_mask = poles_frames_masks[self.poles_frame_indexes[1]]
                if self.poles_frame_indexes[2]:
                    self.pole_mask = poles_frames_masks[self.poles_frame_indexes[2]]
                if not overlay_blitted or self.on_bridge_pole:
                    game_display.blit(poles_frames[self.poles_frame_indexes[0]],self.external_pole_position)
                    game_display.blit(poles_frames[self.poles_frame_indexes[1]],self.middle_pole_position)
                    game_display.blit(poles_frames[self.poles_frame_indexes[2]],self.internal_pole_position)

        #Display spills
        if self.display_oil_spill:
            if self.oil_spill_position is None:
                result = self.get_random_position(oil_spill_image.get_height(),oil_spill_image.get_width())
                self.oil_spill_position = result[0]
                self.on_bridge_oil_spill = result[1]
            if race_started:
                if not overlay_blitted or self.on_bridge_oil_spill:
                    game_display.blit(oil_spill_image,self.oil_spill_position)

        if self.display_water_spill:
            if self.water_spill_position is None:
                result = self.get_random_position(water_spill_image.get_height(),water_spill_image.get_width())
                self.water_spill_position = result[0]
                self.on_bridge_water_spill = result[1]
            if race_started:
                if not overlay_blitted or self.on_bridge_water_spill:
                    game_display.blit(water_spill_image,self.water_spill_position)
        if self.display_grease_spill:
            if self.grease_spill_position is None:
                result = self.get_random_position(grease_spill_image.get_height(),grease_spill_image.get_width())
                self.grease_spill_position = result[0]
                self.on_bridge_grease_spill = result[1]
            if race_started:
                if not overlay_blitted or self.on_bridge_grease_spill:
                    game_display.blit(grease_spill_image,self.grease_spill_position)

        #Display Traffic Cones
        if race_started:
            for cone in self.traffic_cones:

                # should be done before
                if cone.pos is None:
                    pos, on_object = self.get_random_position(traffic_cone.get_height(), traffic_cone_shade.get_width())
                    cone.update(pos)
                    cone.set_on_bridge_or_ramp(on_object)

                if not overlay_blitted or cone.is_on_bridge:
                    cone.blit()

        #blit Tornado last so it always is on top:
        self.blit_tornado(race_started)


from torch import Tensor

from . import InformationRetrievalWeigther
from ... import BiSentenceTransformer
from sentence_transformers.util import cos_sim, semantic_search
import numpy as np
from typing import Dict, Callable

class RTANCEWeighter(InformationRetrievalWeigther):
    """
    This class returns the similarity weights with each document, for every query.
    """

    def __init__(self,
                 query_chunk_size: int = 100,
                 corpus_chunk_size: int = 50000,
                 show_progress_bar: bool = False,
                 batch_size: int = 32,
                 score_function: Callable[[Tensor, Tensor], Tensor] = cos_sim,
                 period: int = 1
                 ):
        self.period = period * batch_size
        self.query_chunk_size = query_chunk_size
        self.corpus_chunk_size = corpus_chunk_size
        self.show_progress_bar = show_progress_bar
        self.batch_size = batch_size
        self.score_function = score_function
        self.corpus_embeddings = None
        self.counter = self.period
        self.model = None
        self.queries = None
        self.corpus = None
        self.rel_queries = None
        self.corpus_embeddings = None
        self.query_args, self.document_args = {}, {}

    def setup(self, model, queries: Dict[str, str], corpus: Dict[str, str], rel_queries: Dict[str, str], query_embeddings: Tensor = None, corpus_embeddings: Tensor = None):
        self.model = model
        self.queries = queries
        self.corpus = corpus
        self.rel_queries = rel_queries
        self.corpus_embeddings = corpus_embeddings
        self.counter = self.period

        if isinstance(model, BiSentenceTransformer):
            self.query_args['encoder'] = 'query'
            self.document_args['encoder'] = 'document'

    def __call__(self, q_id):
        self.update_counter()
        query_embedding = self.model.encode(self.queries.get(q_id), show_progress_bar=self.show_progress_bar, batch_size=self.batch_size, convert_to_tensor=True, **self.query_args)

        query_result_list = semantic_search(query_embeddings=query_embedding,
                                              corpus_embeddings=self.corpus_embeddings,
                                              query_chunk_size=self.query_chunk_size,
                                              corpus_chunk_size=self.corpus_chunk_size,
                                              top_k=len(self.corpus_embeddings))[0]
        return np.abs(np.array(list(map(lambda x: x['score'], sorted(query_result_list, key=lambda x: x['corpus_id'])))) + 1.0)

    def update_counter(self):
        if self.counter == self.period:
            self.counter = 0
            # update corpus_embeddings
            self.corpus_embeddings = self.model.encode(list(map(self.corpus.get, self.rel_queries)), show_progress_bar=self.show_progress_bar, batch_size=self.batch_size, convert_to_tensor=True, **self.document_args)

        self.counter += 1


from django.conf import settings
from django.conf.urls.static import static
from keras.models import model_from_json
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from nltk.translate.bleu_score import sentence_bleu,corpus_bleu
from tqdm import tqdm
from os import listdir
from app1.compiler.classes.Compiler import *
from .config import *

import numpy as np
import h5py as h5py
import subprocess
import glob
import os
import tensorflow as tf

class ScreenShot():

    def __init__(self):
        self.max_length = 48
        self.is_empty(PNG_PATH)
        self.is_empty(CODE_PATH)
        self.is_empty(FEATUR_PATH)

    # ファイルを読み込んで文字列を返す
    def load_doc(self, filename):
        file = open(filename, 'r')
        text = file.read()
        file.close()
        return text

    def load_data(self):
        text = []
        images = []
        all_filenames = glob.glob(FEATUR_PATH + "\\*")
        all_filenames.sort()
        print((all_filenames)[-2:])
        for filename in (all_filenames)[-2:]:
            if filename[-3:] == "npz":
                image = np.load(filename)
                images.append(image['features'])

            else:
                # Bootstrap tokenをロードして開始タグと終了タグでラップ
                syntax = '<START> ' + self.load_doc(filename) + ' <END>'
                # Sすべての単語を1つのスペースで区切る
                syntax = ' '.join(syntax.split())
                # 各コンマの後にスペースを追加
                syntax = syntax.replace(',', ' ,')
                text.append(syntax)
        images = np.array(images, dtype=float)
        return images, text

    def create_gui(self,file_name):
        cmd = 'python manage.py create_gui' + " " + file_name
        print("cmd=",cmd)
        print("current_dir=", os.getcwd())
        os.chdir(settings.BASE_DIR)
        rc_code = subprocess.call(cmd, shell=True)
        return rc_code

    def create_npz(self):
        cmd = 'python manage.py create_npz'
        os.chdir(settings.BASE_DIR)
        rc_code = subprocess.call(cmd, shell=True)
        return rc_code

    # 語彙を作成するための関数を初期化
    def init_token(self):
        tokenizer = Tokenizer(filters='', split=" ", lower=False)
        # 語録の生成
        tokenizer.fit_on_texts([self.load_doc(MODEL_PATH + r"\bootstrap.vocab")])
        return tokenizer

    def load_model(self):
        json_file = open(MODEL_PATH + r"\model.json", 'r')
        loaded_model_json = json_file.read()
        json_file.close()
        loaded_model = model_from_json(loaded_model_json)
        loaded_model.load_weights(MODEL_PATH + r"\weights.h5")
        print("Loaded model from disk")
        graph = tf.get_default_graph()
        return loaded_model, graph

    # word Indexを単語に変換する。
    def word_for_id(self, integer, tokenizer):
        for word, index in tokenizer.word_index.items():
            if index == integer:
                return word
        return None

    # 画像の説明文を生成する
    def generate_desc(self, model, tokenizer, photo, max_length, graph):
        photo = np.array([photo])
        # 生成プロセスをシードする
        in_text = '<START> '
        # シーケンスの全長にわたって反復する
        print('\nPrediction---->\n\n<START> ', end='')
        for i in range(150):
            # 整数エンコード入力シーケンス
            sequence = tokenizer.texts_to_sequences([in_text])[0]
            # 入力データをPaddingする。
            sequence = pad_sequences([sequence], maxlen=max_length)
            #  next wordを予測
            with graph.as_default():
                yhat = model.predict([photo, sequence], verbose=0)
                # 確率を整数に変換
                yhat = np.argmax(yhat)
                # 整数を単語に写像する
                word = self.word_for_id(yhat, tokenizer)
                # 単語をマッピングできない場合は停止
                if word is None:
                    break
                # 次の単語を生成するための入力として追加
                in_text += word + ' '
                # シーケンスの終わりを予測したら停止
                print(word + ' ', end='')
                if word == '<END>':
                    break
        return in_text

    # モデルのスキルを評価する
    def evaluate_model(self, model, texts, photos, tokenizer, max_length, graph):
        actual, predicted = list(), list()
        for i in range(len(texts)):
            yhat = self.generate_desc(model, tokenizer, photos[i], max_length, graph)
            # Actualと予測
            print('\n\nReal---->\n\n' + texts[i])
            actual.append([texts[i].split()])
            predicted.append(yhat.split())
            # BLEUスコアを計算する
            bleu = corpus_bleu(actual, predicted)
            return bleu, actual, predicted

    def generate_html(self):
        # トークンをHTMLとcssにコンパイルします。
        dsl_path = "compiler/assets/web-dsl-mapping.json"
        compiler = Compiler(dsl_path)
        compiled_website = compiler.compile(predicted[0], 'index.html')
        return compiled_website

    def output_html(self, compiled_website):
        with open(os.path.join(HTML_PATH, "auto_create.html"), 'wb') as file:
            file.write(compiled_website.encode('utf-8'))

    def remove_files(self, target_dir):
        file_list = glob.glob(target_dir + "\\*")
        for file in file_list:
            os.remove(file)

    def is_empty(self, directory):
        if os.listdir(directory):
            self.remove_files(directory)


__license__ = "BSD"

import os, sys
import numpy as np

sys.path.append(os.path.abspath(os.path.join(__file__, "..", "..", "..")))
from autoPyTorch import AutoNetImageClassification

# Note: You can write your own datamanager! Call fit with respective train, valid data (numpy matrices)
csv_dir = os.path.abspath("../../datasets/example.csv")

def main():
    X_train = np.array([csv_dir])
    Y_train = np.array([0])

    # Note: every parameter has a default value, you do not have to specify anything. The given parameter allow a fast test.
    autonet = AutoNetImageClassification(config_preset="full_cs", result_logger_dir="logs/")

    res = autonet.fit(X_train=X_train,
                      Y_train=Y_train,
                      images_shape=[3, 32, 32],
                      min_budget=600,
                      max_budget=900,
                      max_runtime=1800,
                      save_checkpoints=True,
                      images_root_folders=[os.path.abspath("../../datasets/example_images")])

    print(res)
    print("Score:", autonet.score(X_test=X_train, Y_test=Y_train))

if __name__ == '__main__':
    main()


import sys
import numpy as np
import pandas as pd
from sqlalchemy import create_engine
import nltk
nltk.download(['punkt', 'wordnet','averaged_perceptron_tagger','stopwords'])
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer
from nltk.corpus import stopwords
import re
import pickle
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report
from sklearn.multioutput import MultiOutputClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import GradientBoostingClassifier

def load_data(database_filepath):

    '''
    Function to retreive data from sql database (database_filepath) and split the dataframe into X and y variable

    Input: Databased filepath
    Output: Returns the Features X & target y along with target columns names catgeory_names
    '''
    engine = create_engine('sqlite:///'+ database_filepath)
    df = pd.read_sql_table('disaster_clean',con=engine)
    #  define feature and target variables X and Y
    X = df['message'].values
    y = df[df.columns[4:]]
    category_names = y.columns.tolist()

    return X, y, category_names

def tokenize(text):
    '''
    Function that splits text into words and return the root form of the words
    after removing the stop words

    Input: text(str): the message
    Output: lemm(list of str): a list of the root form of the message words
    '''
    #Regex to find urls
    url_regex = 'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'

    # Finds all urls from the provided text
    detected_urls = re.findall(url_regex, text)

    #Replaces all urls found with the "urlplaceholder"
    for url in detected_urls:
        text = text.replace(url, "urlplaceholder")

    # Normalize text
    text = re.sub(r"[^a-zA-Z0-9]", " ", text.lower())

    # Extracts the word tokens from the provided text
    tokens = word_tokenize(text)

    # Remove stop words
    stop = stopwords.words("english")
    words = [t for t in tokens if t not in stop]

    #Lemmanitizer to remove inflectional and derivationally related forms of a word
    lemmatizer = WordNetLemmatizer()

    # Makes a list of clean tokens
    clean_tokens = []
    for tok in words:
        clean_tok = lemmatizer.lemmatize(tok).lower().strip()
        clean_tokens.append(clean_tok)

    return clean_tokens

def build_model():
    '''
    Function that specifies the pipeline and
    the grid search parameters in order to build a
    classification model

    Output:  cv: classification model
    '''

    # create pipeline
    pipeline = Pipeline([
        ('vect', CountVectorizer(tokenizer = tokenize)),
        ('tfidf', TfidfTransformer()),
        ('clf', MultiOutputClassifier(GradientBoostingClassifier(max_depth=6)))
    ])

    # choose parameters
    parameters = {'clf__estimator__n_estimators': [100, 140]}

    # create grid search object
    model = GridSearchCV(pipeline, param_grid=parameters, scoring='recall_micro', cv=4)

    return model

def evaluate_model(model, X_test, Y_test, category_names):
    '''
    Function to evaluate the model printing scores for each category_name
    Input: model, X_test, Y_test, category_names
    Output: classification report and accuracy score for all category_names
    '''

    y_pred = model.predict(X_test)

    # Calculate the accuracy for each of them.
    for i in range(len(category_names)):
        print('Category: {} '.format(category_names[i]))
        print(classification_report(Y_test.iloc[:, i].values, y_pred[:, i]))
        print('Accuracy {}\n\n'.format(accuracy_score(Y_test.iloc[:, i].values, y_pred[:, i])))

def save_model(model, model_filepath):
    '''
    Function to save a pickle file of the model

    Input:
    model: the classification model
    model_filepath (str): the path of pickle file
    '''

    with open(model_filepath, 'wb') as f:
        pickle.dump(model, f)

def main():
    if len(sys.argv) == 3:
        database_filepath, model_filepath = sys.argv[1:]
        print('Loading data...\n    DATABASE: {}'.format(database_filepath))
        X, Y, category_names = load_data(database_filepath)
        X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2)

        print('Building model...')
        model = build_model()

        print('Training model...')
        model.fit(X_train, Y_train)

        print('Evaluating model...')
        evaluate_model(model, X_test, Y_test, category_names)

        print('Saving model...\n    MODEL: {}'.format(model_filepath))
        save_model(model, model_filepath)

        print('Trained model saved!')

    else:
        print('Please provide the filepath of the disaster messages database '\
              'as the first argument and the filepath of the pickle file to '\
              'save the model to as the second argument. \n\nExample: python '\
              'train_classifier.py ../data/DisasterResponse.db classifier.pkl')

if __name__ == '__main__':
    main()


import torch
from torch import nn
from torch import optim
from torch.nn.utils import clip_grad_norm_
from torch.utils.data import DataLoader
from torch.utils.data import Dataset
from torchvision import transforms
from PIL import Image
import os

from model.iResnet import iresnet50
from model.fmobilenet import MobileFaceNet
import torch.nn.functional as F
from torch.optim.lr_scheduler import StepLR
from metrics import ArcMarginProduct, ArcFace
import numpy as np

torch.autograd.set_detect_anomaly(True)  # 自动检测nan

# 定义FocalLoss
class FocalLoss(nn.Module):

    def __init__(self, gamma=0, eps=1e-7):
        super(FocalLoss, self).__init__()
        self.gamma = gamma
        self.eps = eps
        self.ce = torch.nn.CrossEntropyLoss()

    def forward(self, input, target):
        logp = self.ce(input, target)
        p = torch.exp(-logp)
        loss = (1 - p) ** self.gamma * logp
        return loss.mean()

# 定义训练集
class TrainDataSet(Dataset):
    def __init__(self, path, transform):
        self.transform = transform
        # 将category映射到label
        self.category_reflect = {}
        for label, category in enumerate(os.listdir(path)):
            self.category_reflect[category] = label

        self.dataset = []
        for category in os.listdir(path):
            category_path = os.path.join(path, category) + '/'
            category_label = self.category_reflect[category]
            for face in os.listdir(category_path):
                face_path = os.path.join(category_path, face)
                if os.path.splitext(face_path)[-1] == '.jpg':
                    img = Image.open(face_path)
                    face_tensor = transform(img)
                    self.dataset.append((face_tensor, category_label))
        print('all train data has been loaded')

    def __getitem__(self, index):
        face_tensor, label = self.dataset[index]
        return face_tensor, label

    def __len__(self):
        return len(self.dataset)

if __name__ == '__main__':
    '''定义transform'''
    norm_mean = [0.5, 0.5, 0.5]
    norm_std = [0.5, 0.5, 0.5]

    transform = transforms.Compose([
        transforms.Resize([112, 112]),
        transforms.RandomHorizontalFlip(),
        # transforms.CenterCrop(112),
        transforms.ToTensor(),
        # transforms.Lambda()
        transforms.Normalize(norm_mean, norm_std), ])

    '''定义训练集'''
    train_path = r"data\train_cut_result"
    batch_size = 8  # batch_size初始化
    train_dataset = TrainDataSet(train_path, transform)
    train_iter = DataLoader(train_dataset, batch_size, shuffle=True, drop_last=True)

    '''定义训练方式'''
    if torch.cuda.is_available():
        device = torch.device('cuda')
        print('GPU applied ')
    else:
        print('CPU applied')
        device = torch.device('cpu')

    # device = torch.device('cpu')

    '''定义网络'''
    net = iresnet50(dropout=0.0, fp16=True, num_features=512)  # 网络实例化并传入cuda
    # num_features = net.fc.in_features
    # net.fc = nn.Linear(num_features, 512)

    net = net.to(device)
    params_load_path = r"params\face_discern.pth"  # 参数的保存路径
    params_save_path = r"params\face_discern_res.pth"
    if os.path.exists(params_load_path):
        net.load_state_dict(torch.load(params_load_path))
        print('Params loaded')
    else:
        print("NO Params")

    '''参数初始化'''
    learning_rate = 1e-1  # 学习率初始化
    lr_step = 10  # 每10个epoch学习下降一次
    lr_decay = 0.95  # lr = lr*lr_decay
    weight_decay_rate = 5e-4
    epoch_num = 100

    '''优化器、学习率、loss函数'''
    # arc_face = ArcMarginProduct(in_features=512, out_features=152).to(device)  # Arcface_metrics
    arc_face = ArcFace().to(device)  # Arcface_loss
    optimizer = optim.SGD(
        net.parameters(),
        lr=learning_rate,
        weight_decay=weight_decay_rate,
        momentum=0.9)  # 优化器
    scheduler = StepLR(optimizer, step_size=lr_step, gamma=lr_decay)  # 学习率控制
    loss_func = FocalLoss().to(device)  # loss函数

    '''训练开始'''
    iter_num = 0
    for epoch in range(epoch_num):

        count = 0
        loss_sum = 0
        net.train()
        for features, labels in train_iter:
            features, labels = features.to(device), labels.to(device)
            # print(features.shape)
            if hasattr(torch.cuda, 'empty_cache'):
                torch.cuda.empty_cache()  # 释放GPU缓存
            logits = net(features)
            assert torch.isnan(logits).sum() == 0, print(logits)
            logits = F.normalize(logits)
            assert torch.isnan(logits).sum() == 0, print(logits)
            logits = arc_face.forward(logits, labels)
            assert torch.isnan(logits).sum() == 0, print(logits)
            loss = loss_func(logits, labels).sum()
            assert torch.isnan(loss).sum() == 0, print(loss)

            optimizer.zero_grad()
            # clip_grad_norm_(net.parameters(), max_norm=5, norm_type=2)  # 梯度截断
            loss.backward()
            optimizer.step()
            loss_sum += loss.data.item()
            count += 1
            print('epoch: %2d' % epoch, ' count: %2d' % count, ' loss: %6.3lf' % loss.data.item())
            iter_num += 1
        scheduler.step()

        '''保存参数'''
        if epoch % 5 == 0 or epoch == epoch_num - 1:
            torch.save(net.state_dict(), params_save_path)
            print("第 {} 轮参数保存成功！".format(epoch))
            print(str(epoch) + ' Train_ave_Loss: %.3lf' % (loss_sum / count))
        else:
            print(str(epoch) + ' Train_Loss : %.3lf' % (loss_sum / count))


import os
import sys
import cv2
import argparse
import numpy as np
import logging

import torch
from torch import nn
from torch.nn import MSELoss
from torch.optim import Adam
import torch.nn.functional as F
from torch.optim.lr_scheduler import MultiStepLR
from torch.utils.data import DataLoader

from tensorboardX import SummaryWriter
import skimage.measure as ms
import progressbar
import torchvision.utils
from dataset import *
import timeit
from unet_models import *
from eval import eval_net
from imutils import paths
from PIL import Image

torch.cuda.manual_seed_all(2019)
torch.manual_seed(2019)

def ensure_dir(dir_path):
    if not os.path.isdir(dir_path):
        os.makedirs(dir_path)

def print_network(net):
    num_params = 0
    for param in net.parameters():
        num_params += param.numel()
    print(net)
    print('Total number of parameters: %d' % num_params)

class Session:
    def __init__(self,dataset_dir = './dataset',images_dir='images',masks_dir = 'masks',train=True,camvid=False):
        self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
        self.log_dir = './logs'
        self.model_path = './models/latest_foreground.pth'
        ensure_dir(self.log_dir)
        self.dataset_dir = dataset_dir
        self.images_dir = images_dir
        self.masks_dir = masks_dir
        self.width = 224
        self.height = 224
        self.net = UNet16().to(self.device)
        self.train = train
        self.data_augmentation = True
        self.camvid = camvid
        self.step = 0
        self.save_step = 4
        self.epochs = 16
        self.batch_size = 16
        self.opt = Adam(self.net.parameters(),lr=5e-3)
        self.sche = MultiStepLR(self.opt, milestones=[500, 1500,2000,3000], gamma=0.1)
        self.BCE = nn.BCEWithLogitsLoss()
        self.step_time = 0
        self.pretrained = True
        self.dataset_len = 0
        self.num_classes = 1

    def get_dataloader(self,train=True):
        dataset = PairDataset(self.dataset_dir,self.images_dir,self.masks_dir,self.train,self.data_augmentation,self.camvid)
        self.dataset_len = dataset.__len__()
        if train:
            dataloader = DataLoader(dataset, batch_size=self.batch_size, shuffle=True)
            return iter(dataloader)
        else:
            dataloader = DataLoader(dataset, batch_size=1, shuffle=False)
            return dataloader

    def inf_batch(self, name, batch):
        images, masks = batch['image'],batch['mask']
        images, masks = images.to(self.device),masks.to(self.device)
        mask_pre= self.net(images)
        # loss
        loss = self.BCE(mask_pre,masks)

        return  mask_pre,loss

    def run_train_val(self):
        if self.pretrained:
            print('load pretrained model........')
            self.net.load_state_dict(torch.load(self.model_path))
        for epoch in range(self.epochs):
            print('%d epoch begin......'%(epoch))
            dt_train = self.get_dataloader()
            dt_val = self.get_dataloader()
            self.step = 0
            while self.step < self.dataset_len//self.batch_size:
                start = timeit.default_timer()
                self.sche.step()
                self.net.train()
                self.net.zero_grad()
                batch_tr = next(dt_train)
                mask_pre_t,loss_t = self.inf_batch('train', batch_tr)
                loss_t.backward()
                self.step_time = timeit.default_timer() - start
                self.opt.step()
                if self.step % self.save_step == 0:
                    self.net.eval()
                    batch_v = next(dt_val)
                    mask_pre_v,loss_v = self.inf_batch('val', batch_v)
                    torchvision.utils.save_image(batch_v['image'],'./logs/epoch %d_val_img_step_%d.png'%(epoch,self.step))
                    torchvision.utils.save_image(batch_v['mask'],'./logs/epoch %d_val_mask_true_step_%d.png'%(epoch,self.step))
                    torchvision.utils.save_image(mask_pre_v,'./logs/epoch %d_val_mask_pre_step_%d.png'%(epoch,self.step))
                    print('%d step loss: %.4f, train step time: %.2f'%(self.step,loss_v,self.step_time))
                    #torch.save(self.net.state_dict(), self.model_path)
                self.step += 1
            if epoch %4 == 0:
                torch.save(self.net.state_dict(), self.model_path)

def run_test():
    sess = Session()
    #sess.net = sess.net.load_state_dict(torch.load(sess.model_path))
    sess.batch_size = 1
    sess.shuffle = False
    dt = sess.get_dataloader(train_mode=False)
    val_score = eval_net(sess.net, dt, sess.device, sess.test_dataset_len)
    if sess.net.num_classes > 1:
        print('Validation cross entropy: {}'.format(val_score))

    else:
        print('Validation Dice Coeff: {}'.format(val_score))

def test_customer_img_list(sess,img_paths=[]):
    sess.net.load_state_dict(torch.load(sess.model_path))
    sess.net.eval()
    if len(img_paths):
        for pth in img_paths:
            fname = pth.split(os.path.sep)[-1].split('.')[0]
            img = cv2.imread(pth)
            frame = img.copy()
            width,height,_ = img.shape
            img = np.asarray(cv2.resize(img,(sess.width,sess.height))).astype('float32')
            cv2.imwrite('test results/'+fname+'_input.png',frame)
            img = np.transpose(normalize(img),(2,0,1))
            img = np.expand_dims(img,0)
            img = torch.from_numpy(img)
            mask_pre = sess.net(img.to(sess.device))
            mask_pre = mask_pre.cpu().detach().numpy().squeeze()
            mask_pre = mask_pre*255
            mask_pre = mask_pre.clip(0,255)
            #cv2.imwrite('test/'+fname+'_mask_pre.png',mask_pre)
            mask_pre_resize = cv2.resize(mask_pre,(height,width),cv2.INTER_NEAREST)
            cv2.imwrite('test results/'+fname+'_mask_pre.png',mask_pre_resize)
            ind = mask_pre_resize[:,:]>0
            ind = np.dstack((ind, ind, ind))
            cv2.imwrite('test results/'+fname+'_input_with_mask_INTER_NEAREST.png',np.multiply(frame,ind))
            mask_processed = find_max_contour(mask_pre_resize)
            cv2.imwrite('test results/'+fname+'_mask_processed.png',mask_processed)
        print('total %d images processed....'%len(img_paths))

def find_max_contour(image):
    image = image.astype('uint8')
    nb_components, labels, stats, _ = cv2.connectedComponentsWithStats(image, connectivity=4)
    sizes = stats[:, -1]

    max_label = 1
    max_size = sizes[1]
    for i in range(2, nb_components):
        if sizes[i] > max_size:
            max_label = i
            max_size = sizes[i]
    img2 = np.zeros(labels.shape)
    img2[labels == max_label] = 255
    return img2

if __name__ == "__main__":
    '''
    dataset_dir = 'D:\\GIT\\PiCANet-Implementation\\dataset\\DUTS-TR'
    images_dir = 'DUTS-TR-Image'
    masks_dir = 'DUTS-TR-Mask'
    sess = Session(dataset_dir,images_dir,masks_dir)
    run_train_val(sess)
    '''
    dataset_dir = 'D:\\GIT\\00Dataset\\Segmentation\\camvid'
    images_dir = 'images'
    masks_dir = 'masks'
    sess = Session(dataset_dir,images_dir,masks_dir,True,True)
    sess.pretrained=False
    sess.save_step = 40
    sess.epochs = 16
    #sess.run_train_val()
    img_paths = list(paths.list_images('C:\\Users\\yixin\\Pictures\\QQplayerPic'))
    test_customer_img_list(sess,img_paths)
   #run_train_camvid_dataloader(sess)


# To add a new cell, type '# %%'
# To add a new markdown cell, type '# %% [markdown]'
# %%
from IPython import get_ipython

# %% [markdown]
# # week2 - 文本数据分析
#
# ## 任务描述
#
# 利用 python 数据结构完成简单的文本分析任务
#
# ## 任务目标及要求
#
# 1. 提供了中英文两个数据集，见资源/ data，分别讨论和分析
# 2. 一行一条评论或一条 tweet
# 3. 一行可以视为一个文档
# 4. 读入所有文档并分词（中文需要 jieba)
# 5. 统计词频，找到高频词，过滤停用词，确定特征集（停用词表自行检索并构建，如提供的示例stopwords_list.txt）
# 6. 利用特征集为每一条评论生成向量表示
# 7. 计算一下不同评论之间的距离（自定义，如欧氏或余弦），找到所有评论的“重心”或者所有文档中的代表性文档并输出原文。
# 8. （附加1）能不能实现关键词的词云可视化？(import WordCloud)
# 9. （附件2）能不能用学过的统计方法进行简单的聚类？
# 10. 注意：通过函数进行封装，并在main函数中调用；提交的作业报告格式如资源/作业模板示例。
# %% [markdown]
# ## 报告说明
# 本报告为 Jupyter Notebook 直接导出
#
# 每个部分用三个 cell 来表示：代码块、运行结果块、分析说明块
#
# 注：并非所有部分都包含三个 cell

# %%
# 引入需要使用的库
import requests as rs
import os
import jieba as jb
import pandas as pd
import numpy as np
from wordcloud import WordCloud as wc
import matplotlib.pyplot as plt
import re
import math
from PIL import Image

# %%
# 获取数据
def get_data(url):
    '''
    '''
    head = {
        'user-agent':
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
    }
    response = rs.get(url, headers=head)
    return response.text.strip().split('\n')

url_list = {
    'online_reviews_texts':
    r'https://course.e2.buaa.edu.cn/access/content/group/6095b6c8-3c07-4799-ba79-dc9e5a379db6/data/online_reviews_texts.txt',
    'stopwords_list':
    r'https://course.e2.buaa.edu.cn/access/content/group/6095b6c8-3c07-4799-ba79-dc9e5a379db6/data/stopwords_list.txt',
    'tweets_apple_stock':
    r'https://course.e2.buaa.edu.cn/access/content/group/6095b6c8-3c07-4799-ba79-dc9e5a379db6/data/tweets_apple_stock.txt'
}

ort = get_data(url_list['online_reviews_texts'])
sl = get_data(url_list['stopwords_list'])
tas = get_data(url_list['tweets_apple_stock'])

# 输出末尾看看是不是
print(ort[-1], sl[-1], tas[-1], sep='\n\n')

# %% [markdown]
# 数据的获取部分并没有选择从本地直接读入，而是从网站上爬取，由于均为txt文件，故没有使用bs4等专业的爬虫库，直接用requests抓取源码即可。
#
# 当然我也下载文件到了本地，也可以通过文件io进行读取
#
# 运行结果展示的是每个文件最后几行话（为了确保爬取到了全部的内容）

# %%
# 处理文本 包括中英文去标点符号 英文去大小写等
def process_data(data):
    '''
    '''
    punctuation = '''`~!@#$%^&*()-=_+\[\]{}|;:,.<>/?·！￥……（）【】‘“《》，。、？"：；～'\\ '''
    for i in range(len(data)):
        data[i] = re.sub(r'[{}]+'.format(punctuation), ' ', data[i]).lower()
    return data

ort_ap = process_data(ort)
tas_ap = process_data(tas)

print(ort_ap[-1], tas_ap[-1], sep='\n\n')

# %% [markdown]
# 利用正则表达式将所有标点换成空格，以便后期的分词处理和词云的绘制
#
# 运行结果展示的是去除标点之后的文本

# %%
# 分词并写入文件
def every_cut(cut_list, language='Chinese', to_file=''):
    '''
    '''
    res = []
    if language == 'Chinese':
        for i in cut_list:
            temp = []
            for j in jb.cut(i, cut_all=True):
                if j not in ['', ' ']:
                    temp.append(j)
            res.append(temp)
    elif language == 'English':
        for i in cut_list:
            res.append(i.split())
    else:
        print('Try again!')
        return -1
    if to_file:
        with open(to_file, 'w', encoding='unicode-escape') as f:
            for i in res:
                f.write(','.join(i))
    return res

def all_cut(cut_list, language='Chinese', to_file=''):
    '''
    '''
    res = []
    if language == 'Chinese':
        temp = []
        for i in cut_list:
            temp.extend(jb.cut(i, cut_all=True))
        for i in temp:
            if i not in ['', ' ']:
                res.append(i)
    elif language == 'English':
        for i in cut_list:
            res.extend(i.split())
    else:
        print('Try again!')
        return -1
    if to_file:
        with open(to_file, 'w', encoding='unicode-escape') as f:
            f.write(','.join(res))
    return res

ort_cut = all_cut(ort_ap, to_file=r'.\data\ort_cut.csv')
tas_cut = all_cut(tas_ap, 'English', to_file=r'.\data\tas_cut.csv')
ort_cut_eve = every_cut(ort_ap, to_file=r'.\data\ort_cut_eve.csv')
tas_cut_eve = every_cut(tas_ap, 'English', to_file=r'.\data\tas_cut_eve.csv')
print(ort_cut[-100:], tas_cut[-100:], sep='\n\n')
print(ort_cut_eve[-1], tas_cut_eve[-1], sep='\n\n')

# %% [markdown]
# 利用jieba库进行分词
#
# 为了后期统计关键词更加精准，分词的时候选择了精确模式
#
# 运行结果为分词结果
#
# 但还是感觉有些问题：比如我分词之后，“不喜欢”被分成了“不”和“喜欢”，这样的话感情倾向就变了。

# %%
# 统计词频 词频以 TF（Term Frequency）策略统计
# 词频（TF）= 某个词在文章中的出现次数 / 文章的总词数
def refresh_stopwords_list():
    with open(r'.\data\stopwords_list.txt', 'r') as f:
        sl = f.read().strip().split('\n')
    return sl

def statistical_frequency_TF(data):
    res_dict = {}
    for i in data:
        if i not in sl:
            res_dict[i] = res_dict.get(i, 0) + 1
    for i in res_dict.keys():
        res_dict[i] = res_dict[i] / len(data)
    return pd.DataFrame({'TF': res_dict})

sl = refresh_stopwords_list()
ort_fre_tf = statistical_frequency_TF(ort_cut)
tas_fre_tf = statistical_frequency_TF(tas_cut)

ort_fre_tf

# %%
tas_fre_tf

# %% [markdown]
# 首先通过停用词表进行无用词的过滤，我感觉老师给的表对英文停用不是很友好，所以我在网上又找了一个，因此这一部分将停用词表重新读入了。
#
# 利用tf方式统计词频，这里的词频并非传统意义的词频，而是该词在文章中的重要程度
# 我觉得传统词频统计方式并不能展现某个词的重要性，比如‘我’几乎所有文本都会出现，但绝大多数情况下其都不是一个重要的词汇。
# 为了避免这种现象的出现，我选择使用 tf 算法进行词频统计，结果如图。

# %%
# 找到高频词 确定特征集
# 考虑到特征集并非词频越高越好 因此使用 TF—IDF 算法找到高频词并确定特征集
# 逆文档频率（IDF） = log(语料库的文档总数 / （包含该词的文档数+1）)
# TF-IDF = TF * IDF
def statistical_frequency_IDF(data_df, data_ap):
    '''
    '''
    res_dict = {}
    nums = len(data_ap)  # 语料总条数
    for row in data_df.itertuples():
        name = row[0]
        temp = 0
        for i in data_ap:
            if name in i:
                temp += 1
        res_dict[name] = temp
    for i in res_dict.keys():
        res_dict[i] = math.log(nums / (res_dict[i] + 1))
    data_df['IDF'] = res_dict.values()
    return data_df

def find_feature_sets_by_TFIDF(df):
    '''
    '''
    res = []
    for row in df.itertuples():
        res.append(row[1] * row[2])
    df['TF-IDF'] = res
    return df

ort_fre_idf = statistical_frequency_IDF(ort_fre_tf, ort_ap)
tas_fre_idf = statistical_frequency_IDF(tas_fre_tf, tas_ap)

ort_fre_tfidf = find_feature_sets_by_TFIDF(ort_fre_idf)
tas_fre_tfidf = find_feature_sets_by_TFIDF(tas_fre_idf)

ort_fre_tfidf

# %%
tas_fre_tfidf

# %% [markdown]
# 找到高频词、确定特征集
#
# 为了避免非重要的词进入特征集，本文采用 tf-idf 算法来进行特征集的筛选
#
# 首先计算所有词的 tf-idf 得分，再选取得分最高的 100 个词作为特征集（本来想选更多的，但无奈算力不足，只好选了100个）

# %%
# 确定特征集
# 取tfidf得分最高的前100个词作为特征集
def select_characteristic(df):
    df = df.sort_values(by='TF-IDF', ascending=False)
    return df.head(100)

ort_cha = select_characteristic(ort_fre_tfidf)
tas_cha = select_characteristic(tas_fre_tfidf)

ort_cha

# %%
tas_cha

# %% [markdown]
# 上述结果即为特征集和相应的tf-idf得分

# %%
# 利用特征集 为每条评论生成向量
# 依然使用 tf-idf 算法
def doc2tfidf_matrix(vocab, words_list, to_file=''):
    '''
    '''
    # 建立一个 M 行 V 列的全 0 矩阵，M 是文档样本数，这里是行数，V 为不重复词语数，即编码维度
    V = len(vocab)
    M = len(words_list)
    onehot = np.zeros((M, V))  # 二维矩阵要使用双括号
    tf = np.zeros((M, V))

    for i, doc in enumerate(words_list):
        for word in doc:
            if word in vocab:
                pos = vocab.index(word)
                onehot[i][pos] = 1
                tf[i][pos] += 1  # tf,统计某词语在一条样本中出现的次数

    row_sum = tf.sum(axis=1)  # 行相加，得到每个样本出现的词语数
    # 计算TF(t,d)
    tf = tf / row_sum[:, np.
                      newaxis]  #分母表示各样本出现的词语数，tf为单词在样本中出现的次数，[:,np.newaxis]作用类似于行列转置
    # 计算DF(t,D)，IDF
    df = onehot.sum(axis=0)  # 列相加，表示有多少样本包含词袋某词
    idf = list(map(lambda x: math.log10((M + 1) / (x + 1)), df))
    # 计算TFIDF
    tfidf = tf * np.array(idf)
    tfidf = pd.DataFrame(tfidf, columns=vocab)
    if to_file:
        tfidf.to_csv(to_file)
    return tfidf

ort_vec = doc2tfidf_matrix(ort_cha._stat_axis.values.tolist(),
                           ort_cut_eve,
                           to_file=r'.\data\ort_vec.csv')
tas_vec = doc2tfidf_matrix(tas_cha._stat_axis.values.tolist(),
                           tas_cut_eve,
                           to_file=r'.\data\ort_vec.csv')

ort_vec

# %%
tas_vec

# %% [markdown]
# 利用特征集 为每条评论生成向量
#
# 文本转向量 这里使用的算法依然是 tf-idf 算法，词袋是特征集
#
# 运行结果是每天评论的向量表示（100维）

# %%
# 计算距离并找到重心

def calculate_distance(vec_df, fuc='欧式距离', to_file=''):
    '''
    '''
    LEN = vec_df.shape[0]
    res = [[0 for _ in range(LEN)] for __ in range(LEN)]
    for i in range(LEN):
        for j in range(i):
            res[i][j] = math.sqrt(sum((vec_df.loc[i] - vec_df.loc[j])**2))
    res = pd.DataFrame(res)
    if to_file:
        res.to_csv(to_file)
    return res

def find_heart(vec_df, odata):
    '''
    '''
    heart = vec_df.mean(axis=0)
    min_index = -1
    min_num = 99999
    for i in range(vec_df.shape[0]):
        temp = math.sqrt(sum((vec_df.loc[i] - heart)**2))
        if temp <= min_num:
            min_num = temp
            min_index = i
    return odata[min_index]

ort_dis = calculate_distance(ort_vec, to_file=r'.\data\ort_dis.csv')
tas_dis = calculate_distance(tas_vec, to_file=r'.\data\tas_dis.csv')

print(ort_dis.head())
print(tas_dis.head())

ort_heart = find_heart(ort_vec, ort)
tas_heart = find_heart(tas_vec, tas)

print(ort_heart)
print(tas_heart)

# %% [markdown]
# 计算距离矩阵
#
# 利用 Euclid 距离进行计算，100 维的情况下计算两两评论的距离大概需要 20 分钟的时间...我的算力实在不允许我计算更高维的距离了
#
# 重心求法类似三角形的重心求法，计算每个向量的平均值。
#
# 找到重心后并未输出，而是进一步找到距离重心最近的一条文本输出的，作为整个文章的代表
#
# 但输出后才发现，距离重心最近的文本，并没有突出的关键词（正是因为这个才最近的），反而不能很好的代表正文章。

# %%
# 实现词云可视化
get_ipython().run_line_magic('matplotlib', 'inline')

def show_worldcloud(cut_list, bgp='', to_file=''):
    '''
    '''
    if bgp:
        mask = np.array(Image.open(bgp))
    else:
        mask = None
    wc_ = wc(
        font_path='C:\Windows\Fonts\STZHONGS.TTF',  # 设置字体
        background_color="white",  # 背景颜色
        max_words=1000,  # 词云显示的最大词数
        random_state=42,  #随机数
        collocations=False,  #避免重复单词
        mask=mask,
    ).generate(" ".join(cut_list))
    plt.imshow(wc_, interpolation='bilinear')
    plt.axis("off")  #隐藏坐标
    if to_file:
        wc_.to_file(to_file)

show_worldcloud(ort_cut,
                bgp=r'./img/pangxie_bgp.jpg',
                to_file=r'.\img\ort_cloud.png')

# %%
show_worldcloud(tas_cut,
                bgp=r'./img/pingguo.jpg',
                to_file=r'.\img\tas_cloud.png')

# %% [markdown]
# 生成词云 分别使用螃蟹和苹果做蒙板
# %% [markdown]
# 致歉：这次作业完成的有些仓促，虽然时间很长，但开学来手头的事情非常多，导致没有分配给这个作业太多时间。最后的效果也不是我想要的，如果后期有时间我会对本次作业进行完善和补充
#
# flag：经过思考，我在此立下本学期大作业的 flag，大作业代码量 5000+，使用深度学习算法进行辅助分析，完全在 Linux 系统上进行开发，请老师监督。


import numpy as np
import matplotlib.pyplot as plt
import os
import sys

import gensim
import os
import subprocess
import sys
import cv2
import scipy.misc
import skimage.io
import time
import math

caffe_root = 'path_to_caffe_root'
os.chdir(caffe_root)
sys.path.append(caffe_root)
sys.path.insert(0, 'python')
import caffe

from skimage.transform import resize
from google.protobuf import text_format
from caffe.proto import caffe_pb2
from nltk.tag import pos_tag

import httplib
import json
from random import choice
from string import ascii_uppercase

import pdb

sys.path.insert(0, 'python')

os.environ['GLOG_minloglevel'] = '1'
#os.chdir(caffe_root)

#constants
gradCamPrototxt='path_to_grad_cam_model_prtotxt' #optional
gradCamCaffemodel='path_to_grad_cam_weights' #optional
ssdPrototxt = 'path_to_ssd_prototxt'
ssdCaffeModel = 'path_to_ssd_weights'

dataFolder = caffe_root+'/data/demo/'
interComData = caffe_root+'/examples/InterFiles/'

class DepthQuestionAnwering(object):
    def __init__(self):
        self.genSimModel = gensim.models.Word2Vec.load_word2vec_format(caffe_root+'/examples/GoogleNews-vectors-negative300.bin', binary=True)
        print '\n Loaded Gensim model \n'

        # load PASCAL VOC labels
        labelmap_file = caffe_root+'/data/coco/labelmap_coco.prototxt'
        file = open(labelmap_file, 'r')
        self.labelmap = caffe_pb2.LabelMap()
        text_format.Merge(str(file.read()), self.labelmap)

        #load grad-cam caffe. It's not used in this experiment
        #caffe.set_device(0)
        #caffe.set_mode_gpu()
        #gradCamNet = caffe.Net(gradCamPrototxt, gradCamCaffemodel, caffe.TEST)
        #print '\n Loaded grad-cam caffe \n'

        #load SSD caffe
        caffe.set_device(0)
        caffe.set_mode_gpu()
        self.ssdNet = caffe.Net(ssdPrototxt, ssdCaffeModel,caffe.TEST)
        print '\n Loaded ssd caffe \n'

        # input preprocessing: 'data' is the name of the input blob == net.inputs[0]
        transformer = caffe.io.Transformer({'data': self.ssdNet.blobs['data'].data.shape})
        transformer.set_transpose('data', (2, 0, 1))
        transformer.set_mean('data', np.array([104,117,123])) # mean pixel
        transformer.set_raw_scale('data', 255)  # the reference model operates on images in [0,255] range instead of [0,1]
        transformer.set_channel_swap('data', (2,1,0))  # the reference model has channels in BGR order instead of RGB
        self.transformer = transformer

        # set net to batch size of 1
        image_resize = 512
        self.ssdNet.blobs['data'].reshape(1,3,image_resize,image_resize)

    def get_labelname(self,labelmap, labels):
        num_labels = len(labelmap.item)
        labelnames = []
        if type(labels) is not list:
            labels = [labels]
        for label in labels:
            found = False
            for i in xrange(0, num_labels):
                if label == labelmap.item[i].label:
                    found = True
                    labelnames.append(labelmap.item[i].display_name)
                    break
            assert found == True
        return labelnames

    #get relavant objects
    def get_rel_objects(self,words,classes,conf,all_dets):
        rel_dets = []
        rel_classes = []
        rel_conf = []
        max_acc = 0.0;
        #get best match accuracy
        for i in range(len(words)):
           for j in range(len(classes)):
              try:
                 sim = self.genSimModel.similarity(classes[j], words[i])
              except KeyError:
                 sim = -1
              max_acc = max(max_acc,sim)
        if max_acc<0.5: #make sure the highest similarity match is a strong match
           max_acc=0.0

        #words to class idx
        for i in range(len(words)):
           for j in range(len(classes)):
              try:
                 sim = self.genSimModel.similarity(classes[j],words[i])
              except KeyError:
                 sim = -1
              if sim==max_acc:
                 rel_dets.append(all_dets[j][0:4])
                 rel_classes.append(classes[j])
                 rel_conf.append(conf[j])

        return (rel_conf,rel_classes,rel_dets)

    def getDepthInfo(self,imgPathL, imgPathR, interComData,rel_dets,relSegMask):
        #writing bbox and segmenation to a file
        f = open(interComData+'Bbox.txt','w')
        for i in range(len(rel_dets)):
            f.write('%d %d %d %d\n' %(round(rel_dets[i][0]),round(rel_dets[i][1]),round(rel_dets[i][2]),round(rel_dets[i][3])))
        f.close()

        #saving segmantation mask
        scipy.misc.toimage(relSegMask, cmin=0.0, cmax=255.0).save(interComData+'SegMask.png')
        print "Finished saving bbox and segmentation mask"

        print "Start extracting depth map"
        leftImage = imgPathL
        rightImage = imgPathR
        BboxPath = interComData+'Bbox.txt'
        segmaskPath = interComData+'SegMask.png'
        disparityPath = interComData+'Disparity.jpg'
        distancePath = interComData+'Distance.txt'
        heightWidthPath = interComData+'HeightWidth.txt'
        paramPath = '/home/bosch-server/Desktop/CurrentCode/caffe/data/demo/'+'StereoParamsZed.yml'
        #pdb.set_trace()
        subprocess.call("path to compiled ./Depth_extraction "+leftImage+" "+rightImage+" "+BboxPath+" "+segmaskPath+" "+disparityPath+" "+distancePath+" "+paramPath+" "+heightWidthPath, shell=True)
        print "Finish extracting depth map"

        #read distances
        distances = [];
        f = open(interComData+'Distance.txt','r')
        for line in f:
            data = line.split()
            distances.append((float(data[0]),float(data[1]),float(data[2])))
        f.close()

        disparity = skimage.io.imread(disparityPath)

        return (distances,disparity)

    def getRealSegmentation(self,image_path, interComData):

        #option 1 to call segmentation right away using subprocess (slow)
        #subprocess.call("python getSegmentation.py -i "+image_path+" -o "+interComData+"SegMask.png", shell=True, cwd="/home/bosch-server/Desktop/CRFasRNN/crfasrnn/caffe/examples/python-scripts/")

        #option 2 to send signal to already running segmentation (fast)
        testRequest = json.dumps({'inputPath': image_path, 'outputPath': interComData+'SegMask.png'}, sort_keys=False, indent=4, separators=(',', ': '))
        c = httplib.HTTPConnection('localhost', 8080)
        c.request('POST', '/process', testRequest)
        doc = c.getresponse().read()
        print doc
        relSegMask = skimage.io.imread(interComData+'SegMask.png')
        #relSegMask = cv2.imread(interComData+'realSegMask.png')
        #relSegMask = cv2.cvtColor(relSegMask, cv2.COLOR_BGR2GRAY)
        return relSegMask

    def getSingleObject(self,rel_box,distances,index,imgSize):
        final_dist = []
        final_box = []
        if index == 0:
            left_idx = sorted(range(len(rel_box)), key=lambda k: rel_box[k][2], reverse=False)
            final_dist = [ distances[i] for i in left_idx]
            final_box = [ rel_box[i] for i in left_idx]
        elif index == 1:
            right_idx = sorted(range(len(rel_box)), key=lambda k: rel_box[k][2], reverse=True) #rightxcord
            final_dist = [ distances[i] for i in right_idx]
            final_box = [ rel_box[i] for i in right_idx]
        elif index==3:
            bottom_idx = sorted(range(len(rel_box)), key=lambda k: rel_box[k][3], reverse=True) #bottomycord
            final_dist = [ distances[i] for i in bottom_idx]
            final_box = [ rel_box[i] for i in bottom_idx]
        elif index==2:
            top_idx = sorted(range(len(rel_box)), key=lambda k: rel_box[k][1], reverse=False) #topycord
            final_dist = [ distances[i] for i in top_idx]
            final_box = [ rel_box[i] for i in top_idx]
        elif index==4:
            depth_idx = sorted(range(len(distances)), key=lambda k: distances[k][0], reverse=True) #front object
            final_dist = [ distances[i] for i in depth_idx]
            final_box = [ rel_box[i] for i in depth_idx]
        elif index==5:
            depth_idx = sorted(range(len(distances)), key=lambda k: distances[k][0], reverse=False) #back object
            final_dist = [ distances[i] for i in depth_idx]
            final_box = [ rel_box[i] for i in depth_idx]
        elif (index==-1 and len(rel_box)!=0) or index==6: #no exact location is specified. return central object
            imgCenterX = imgSize[1]/2
            imgCenterY = imgSize[0]/2

            minDist = 9999
            minIdx = -1
            for i in range(len(rel_box)):
                boxCenterX = (rel_box[i][2]-rel_box[i][0])/2
                boxCenterY = (rel_box[i][3]-rel_box[i][1])/2
                curDist = math.sqrt((boxCenterX-imgCenterX)*(boxCenterX-imgCenterX) + (boxCenterY-imgCenterY)*(boxCenterY-imgCenterY))
                if (curDist<minDist):
                    minDist = curDist
                    maxIdx = i
            final_dist = distances[maxIdx]
            final_box = rel_box[maxIdx]
            return (final_dist,final_box)
        else:
            print 'GetSingleObject index is out of range'

        return (final_dist[0], final_box[0])

    def getFullResponse(self, imgPathL, imgPathR,question):
        #loop through images
        start_time=time.time()
        image = caffe.io.load_image(imgPathL)

        transformed_image = self.transformer.preprocess('data', image)
        self.ssdNet.blobs['data'].data[...] = transformed_image

        # Forward pass.
        detections = self.ssdNet.forward()['detection_out']

        # Parse the outputs.
        det_label = detections[0,0,:,1]
        det_conf = detections[0,0,:,2]
        det_xmin = detections[0,0,:,3]
        det_ymin = detections[0,0,:,4]
        det_xmax = detections[0,0,:,5]
        det_ymax = detections[0,0,:,6]

        # Get detections with confidence higher than 0.6.
        top_indices = [i for i, conf in enumerate(det_conf) if conf >= 0.6]

        top_conf = det_conf[top_indices]
        top_label_indices = det_label[top_indices].tolist()
        top_labels = self.get_labelname(self.labelmap, top_label_indices)
        top_xmin = np.reshape(det_xmin[top_indices],(len(top_indices),1,))
        top_ymin = np.reshape(det_ymin[top_indices],(len(top_indices),1,))
        top_xmax = np.reshape(det_xmax[top_indices],(len(top_indices),1,))
        top_ymax = np.reshape(det_ymax[top_indices],(len(top_indices),1,))

        bbox = np.round(np.hstack((np.hstack((top_xmin*image.shape[1],top_ymin*image.shape[0])),np.hstack((top_xmax*image.shape[1],top_ymax*image.shape[0])))))
        bbox = bbox.astype(int)

        dataFolderSeq = "path_to_folder_to_save_visualizations"
        imgNameForVis = ''.join(choice(ascii_uppercase) for i in range(12))
        self.visual_ssd_detection(image,bbox,top_conf,top_labels,dataFolderSeq,imgNameForVis,top_label_indices, True)

        #preprocessing the question
        #left,right,back,front,bottom,top"
        locationHashTable = {'left': 0,'right' : 1,'top' : 2,'bottom' : 3, 'back' : 4,'front' : 5, 'center' : 6}

        tagged_sent = pos_tag(question.split())
        properNouns = [word for word,pos in tagged_sent if pos == 'NN']

        #get relevant to the question object in the scene
        (rel_conf, rel_label, rel_box) = self.get_rel_objects(properNouns,top_labels,top_conf,bbox)

        #get segmentation of the scene
        relSegMask = np.asarray(self.getRealSegmentation(imgPathL,interComData))
        #make all the values above zero 1
        relSegMask[np.where(relSegMask>0)] = 255
        print relSegMask

        #get depth to all relevant objects
        (distances,disparity) = self.getDepthInfo(imgPathL,imgPathR,interComData,rel_box,relSegMask)

        #get single object that user is curious about
        location = -1;
        for i in range(len(properNouns)):
            if (properNouns[i] in locationHashTable):
                location = locationHashTable[properNouns[i]]

        final_dist = []
        isFound = True
        if len(rel_box)>1:
            (final_dist, final_box) = self.getSingleObject(rel_box,distances,location,image.shape)
        elif len(rel_box)==1:
            print "Single rel box is detected"
            final_dist = distances[0]
            final_box = rel_box[0]
        else:
            print "No relevant object is detected"
            isFound = False

        elapsed_time=time.time()-start_time

        print "Time elapsed %.5f \n" % (elapsed_time)

        print final_dist
        #print isFound

        if isFound == True:
            return (final_dist[0],final_dist[1]*(1-0.05),final_dist[2]*(1-0.05),isFound)
        else:
            return (0,0,0,isFound)

    def visual_ssd_detection(self,image,bbox,top_conf,top_labels,dataFolderSeq,name,top_label_indices,save):
        colors = plt.cm.hsv(np.linspace(0, 1, 81)).tolist()

        if (save==True):
            plt.ioff()
        else:
            plt.ion()

        fig, currentAxis = plt.subplots()
        currentAxis.imshow(image)

        for i in xrange(top_conf.shape[0]):
            xmin = bbox[i][0]
            ymin = bbox[i][1]
            xmax = bbox[i][2]
            ymax = bbox[i][3]
            score = top_conf[i]
            label = int(top_label_indices[i])
            label_name = top_labels[i]
            display_txt = '%s: %.2f'%(label_name, score)
            coords = (xmin, ymin), xmax-xmin+1, ymax-ymin+1
            color = colors[label]
            currentAxis.add_patch(plt.Rectangle(*coords, fill=False, edgecolor=color, linewidth=2))
            currentAxis.text(xmin, ymin, display_txt, bbox={'facecolor':color, 'alpha':0.5})

        plt.axis('off')
        plt.tight_layout()
        #plt.draw()

        if save==True:
            #plt.savefig(dataFolderSeq+'SSD_Detection/'+name)
            fig.savefig(dataFolderSeq+'SSD_Detection/'+name+'.png')
            plt.close(fig)
        else:
            plt.show()

#z = DepthQuestionAnwering()
#question = "How far is the person on the left"
#test = z.getFullResponse(caffe_root+'/examples/left.png',caffe_root+'/examples/right.png',question)
#print "Got here"
#print test