from pico2d import *
import item
import random

class character():

    PIXEL_PER_METER = (10.0 / 0.4)  # 10 pixel 40 cm
    RUN_SPEED_KMPH = 35  # Km / Hour
    RUN_SPEED_MPM = (RUN_SPEED_KMPH * 1000.0 / 60.0)
    RUN_SPEED_MPS = (RUN_SPEED_MPM / 60.0)
    RUN_SPEED_PPS = (RUN_SPEED_MPS * PIXEL_PER_METER)

    JUMP_HEIGHT = 8  # max Meter
    JUMP_HEIGHT_P = (JUMP_HEIGHT * PIXEL_PER_METER)
    JUMP_TIME = 0.8

    TIME_PER_ACTION = 0.5
    ACTION_PER_TIME = 1.0 / TIME_PER_ACTION
    FRAMES_PER_ACTION = 8

    JUMP_START = ((JUMP_HEIGHT_P * 2) / (JUMP_TIME / 2))

    GRAVITY_P = (JUMP_START / (JUMP_TIME / 2))

    RIGHT_RUN, LEFT_RUN, RIGHT_IDLE, LEFT_IDLE = 0, 1, 2, 3
    R_Z_SKILL, L_Z_SKILL, R_X_SKILL, L_X_SKILL, R_C_SKILL, L_C_SKILL, R_V_SKILL, L_V_SKILL = 4, 5, 6, 7, 8, 9, 10, 11

    LEFTSIDE, RIGHTSIDE = -1, 1

    LEFT, RIGHT = 0, 1

    PASSIVE, ACTIVE = 0, 1
    sound_skill_v = None
    sound_normal_atk = None
    sound_skill_z = None
    sound_jump = None

    def pause(self):
        self.keydown = 0
        self.leftinput = False
        self.rightinput = False
        self.change_state()

    def get_active_item_id(self):
        return self.itemlist[self.itemvalue-1][0]

    def skill_cooltime_check(self):
        return (1.5-self.c_skill_z, 0.5-self.c_skill_x, 3-self.c_skill_c, 5-self.c_skill_v,
                self.itemlist[self.itemvalue-1][1]-self.item_cooldown)

    def handle_run(self, frame_time):
        if self.seeside == -1:
            if not self.canmove[self.LEFT]:
                return
        if self.seeside == 1:
            if not self.canmove[self.RIGHT]:
                return
        self.x = clamp(20, self.x + (self.RUN_SPEED_PPS * self.seeside * frame_time), 1580)
        self.skill_x -= (self.RUN_SPEED_PPS * self.seeside * frame_time)

    def handle_idle(self, frame_time):
        pass

    def handle_jump(self, frame_time):
        self.down_spd = self.JUMP_START
        character.sound_jump.play()
        self.isground = False
        self.y += 3

    def is_down(self):
        if(self.down_spd <= 0):
            return True
        else:
            return False

    def change_state(self):
        if self.seeside == self.LEFTSIDE:
            if self.leftinput:
                self.state = self.LEFT_RUN
            else:
                self.state = self.LEFT_IDLE
        elif self.seeside == self.RIGHTSIDE:
            if self.rightinput:
                self.state = self.RIGHT_RUN
            else:
                self.state = self.RIGHT_IDLE

    def handle_z_skill(self, frame_time): # move
        if self.aniemelock:
            self.skillsec += frame_time
            if self.skillsec >= self.t_skill_z:
                self.skillsec = 0
                self.aniemelock = False
                self.change_state()
        elif self.c_skill_z >= 1.5:
            self.x = clamp(2, self.x +self.RUN_SPEED_PPS * self.seeside * 0.5, 1580)
            self.skill_x -= (self.RUN_SPEED_PPS * self.seeside * 0.5)
            character.sound_skill_z.play()
            self.ATK = True
            self.c_skill_z = 0
            self.aniemelock = True
        else:
            self.change_state()

    def handle_x_skill(self, frame_time): # atk
        if self.aniemelock:
            self.skillsec += frame_time
            if self.skillsec >= self.t_skill_x:
                self.skillsec = 0
                self.aniemelock = False
                self.change_state()
        elif self.c_skill_x >= 0.5:
            self.ATK = True
            self.c_skill_x = 0
            self.aniemelock = True
            character.sound_normal_atk.play()
        else:
            self.change_state()

    def handle_c_skill(self, frame_time): # 6hit
        if self.aniemelock:
            self.skillsec += frame_time
            if self.skillsec >= self.t_skill_c:
                self.skillsec = 0
                self.aniemelock = False
                self.change_state()
                self.atk_count2 = 0
            self.atk_count = (int)((self.atk_count + (frame_time * 10)) % 4)
            if self.atk_count == 0 and self.atk_count2 < 6:
                self.atk_count2 += 1
                character.sound_normal_atk.play()
        elif self.c_skill_c >= 3:
            self.ATK = True
            self.c_skill_c = 0
            self.aniemelock = True
        else:
            self.change_state()

    def handle_v_skill(self, frame_time): # area
        if self.aniemelock:
            self.skillsec += frame_time
            if self.skillsec >= self.t_skill_v:
                self.skillsec = 0
                self.aniemelock = False
                self.change_state()
        elif self.c_skill_v >= 5:
            self.ATK = True
            self.c_skill_v = 0
            self.aniemelock = True
            character.sound_skill_v.play()
        else:
            self.change_state()

    def use_active_item(self):
        if self.itemlist[self.itemvalue-1][0] == 0:
            return
        if self.item_cooldown >= self.itemlist[self.itemvalue-1][1]:
            if self.itemlist[self.itemvalue-1][0] == 2:
                self.immortal = True
            if self.itemlist[self.itemvalue-1][0] == 3:
                self.c_skill_z = 5
                self.c_skill_x = 5
                self.c_skill_c = 5
                self.c_skill_v = 5
            self.item_cooldown = 0

    handle_state = {
        RIGHT_RUN: handle_run,
        LEFT_RUN: handle_run,
        RIGHT_IDLE: handle_idle,
        LEFT_IDLE: handle_idle,
        L_Z_SKILL: handle_z_skill,
        R_Z_SKILL: handle_z_skill,
        L_X_SKILL: handle_x_skill,
        R_X_SKILL: handle_x_skill,
        L_C_SKILL: handle_c_skill,
        R_C_SKILL: handle_c_skill,
        L_V_SKILL: handle_v_skill,
        R_V_SKILL: handle_v_skill
    }
    def down(self):
        self.isground = False

    def hit(self):
        if self.immortal:
            return False
        else:
            return True

    def hitbox(self, type): #idle, atk, skillc, skillv
        if type == 0: return (self.canvas_x - 16, self.y - 18, self.canvas_x + 16, self. y + 18)
        elif type == 1:
            if self.seeside == -1:
               return (self.canvas_x  -32, self.y - 18, self.canvas_x -16, self.y + 18)
            else:
               return (self.canvas_x + 16, self.y - 18, self.canvas_x + 32, self.y + 18)
        elif type == 2: return (self.canvas_x - 50, self.y - 54, self.canvas_x + 50, self.y + 54)
        elif type == 3: return (self.canvas_x - 240, self.y - 20, self.canvas_x + 240, self.y + 20)

    def draw_hitbox(self):
        draw_rectangle(*self.hitbox(0))
        draw_rectangle(*self.hitbox(1))
        draw_rectangle(*self.hitbox(2))
        draw_rectangle(*self.hitbox(3))

    def damage(self, pointXY):
        self.skill_x, self.skill_y = pointXY
        if self.state in (self.L_C_SKILL, self.R_C_SKILL):
            self.anime_time = 0.5
            self.anime_state = 1
        elif self.state in (self.L_X_SKILL, self.R_X_SKILL):
            self.anime_time = 0.5
            self.anime_state = 0
        if(random.randrange(0,100) <= self.critchance):
            return (self._damage * 2)
        else:
            return self._damage

    def stage_Change(self):
        #see side left = -1, right = 1
        self.state = 0
        self.seeside = 1
        self.isground = False
        self.frame = 0
        self.down_spd = 10
        self.canmove = [True, True]
        self.l_block = None
        self.r_block = None
        self.d_block = None
        self.x, self.y = 800, 1000
        self.framesec = 0
        self.skillsec = 0
        self.anime_time = 0
        self.anime_state = 0
        self.skill_x, self.skill_y = 0, 0
        self.c_skill_z = 0
        self.c_skill_x = 0
        self.c_skill_c = 0
        self.c_skill_v = 0
        self.atk_count = 0
        self.atk_count2 = 0
        self.state = 2
        self.keydown = 0
        self.aniemelock = False
        self.leftinput = False
        self.rightinput = False
        self.ATK = False
        self.item_cooldown = 0
        self.immortal = False
        self.immortal_time = 0

    def __init__(self, itemvalue):
        #see side left = -1, right = 1
        self.state = 0
        self.seeside = 1
        self.isground = True
        self.frame = 0
        self.down_spd = 10
        self.imagesize_y = 0
        self.canmove = [True, True]
        self.l_block = None
        self.r_block = None
        self.d_block = None

        self.image_char = load_image('./src/char.png')
        self.image_skill_x = load_image('./src/char_atk.png')
        self.image_skill_c = load_image('./src/char_skill_c.png')
        self.image_skill_v = load_image('./src/char_skill_v.png')
        self.x, self.y = 800, 1000
        self.item_cooldown = 0
        self.framesec = 0
        self.skillsec = 0
        self.c_skill_z = 0
        self.c_skill_x = 0
        self.c_skill_c = 0
        self.c_skill_v = 0
        self.t_skill_z = 0.25
        self.t_skill_x = 1/15
        self.t_skill_c = 0.25
        self.t_skill_v = 0.5
        self.anime_time = 0
        self.anime_state = 0
        self.skill_x, self.skill_y = 0, 0
        self._damage = 10
        self.atk_count = 0
        self.atk_count2 = 0
        self.state = 2
        self.keydown = 0
        self.critchance = 5
        self.aniemelock = False
        self.leftinput = False
        self.rightinput = False
        self.imagesize_y = 34
        self.ATK = False
        self.itemlist = []
        self.itemvalue = itemvalue
        self.canvas_x = get_canvas_width()//2
        self.immortal = False
        self.immortal_time = 0
        for i in range(itemvalue): # PASSIVE 아이템
            self.itemlist.append([i, 0])
        self.itemlist[itemvalue-1][0] = 0

        if character.sound_skill_z == None:
            character.sound_skill_z = load_wav('./src/char_blink.wav')
            character.sound_skill_z.set_volume(50)
        if character.sound_jump == None:
            character.sound_jump = load_wav('./src/char_jump.wav')
            character.sound_jump.set_volume(50)
        if character.sound_skill_v == None:
            character.sound_skill_v = load_wav('./src/earth_skill.wav')
            character.sound_skill_v.set_volume(100)
        if character.sound_normal_atk == None:
            character.sound_normal_atk = load_wav('./src/normal_atk.wav')
            character.sound_skill_v.set_volume(32)

    def onground(self, y):
        self.isground = True
        self.down_spd = 0
        self.y = (y + (self.imagesize_y//2))

    def getitem(self, item):
        if item.type == 0:
            self.itemlist[item.id][1] = self.itemlist[item.id][1] + 1
            if item.part == item.ATKPOWER:
                self._damage += item.value
            elif item.part == item.CRIT:
                self.critchance += item.value
        else:
            self.itemlist[self.itemvalue-1][0] = item.id
            self.itemlist[self.itemvalue-1][1] = item.value
            self.item_cooldown = 0

    def cooldown(self, frame_time):
        self.c_skill_z += frame_time
        self.c_skill_x += frame_time
        self.c_skill_c += frame_time
        self.c_skill_v += frame_time

    def update(self, frame_time):
        self.item_cooldown += frame_time
        if self.anime_time > 0:
            self.anime_time -= frame_time
        if self.immortal:
            self.immortal_time += frame_time
            if self.immortal_time >= 1.5:
                self.immortal = False
                self.immortal_time = 0
        self.cooldown(frame_time)
        #if self.state <= 3:
        self.handle_state[self.state](self, frame_time)
        self.framesec += self.FRAMES_PER_ACTION * self.ACTION_PER_TIME * frame_time
        self.frame = (int)(self.framesec) % 8
        if self.isground == False:
            self.down_spd -= self.GRAVITY_P * frame_time
            self.y += self.down_spd * frame_time

    def draw(self):
        if self.anime_time > 0:
            if self.anime_state == 0:
                self.image_skill_x.clip_draw(0, (self.frame*36), 33, 36, self.skill_x, self.skill_y)
            elif self.anime_state == 1:
                self.image_skill_c.clip_draw(0, (self.frame*36), 33, 36, self.skill_x, self.skill_y)

        if self.state in (self.LEFT_IDLE, self.LEFT_RUN, self.RIGHT_IDLE, self.RIGHT_RUN):
            self.image_char.clip_draw((self.frame*33), (self.state * 36), 33, 36, self.canvas_x, self.y)
        elif self.state in (self.L_Z_SKILL, self.L_C_SKILL, self.L_X_SKILL):
            self.image_char.clip_draw((self.frame*33), 180, 33, 36, self.canvas_x, self.y)
        elif self.state in (self.R_Z_SKILL, self.R_C_SKILL, self.R_X_SKILL):
            self.image_char.clip_draw((self.frame*33), 144, 33, 36, self.canvas_x, self.y)
        elif self.state in (self.L_V_SKILL, self.R_V_SKILL):
            self.image_char.clip_draw((self.frame * 33), 144+((self.state-10) * 36), 33, 36, self.canvas_x, self.y)
            self.image_skill_v.clip_draw(0, (self.frame * 36), 240, 36, self.canvas_x, self.y)

    def handle_events(self, frame_time, event):
        if((event.type == SDL_KEYDOWN) and (self.aniemelock)):
            if event.key == SDLK_LEFT:
                self.seeside = self.LEFTSIDE
                self.leftinput = True
            elif event.key == SDLK_RIGHT:
                self.seeside = 1
                self.rightinput = True
            elif event.key == SDLK_g:
                self.use_active_item()
        elif((event.type == SDL_KEYUP) and (self.aniemelock)):
            if event.key == SDLK_LEFT:
                self.leftinput = False
                if self.rightinput == True:
                    self.seeside = 1
            elif event.key == SDLK_RIGHT:
                self.rightinput = False
                if self.leftinput == True:
                    self.seeside = self.LEFTSIDE
        elif((event.type == SDL_KEYDOWN) and not(self.aniemelock)):
            if (event.key == SDLK_z):
                self.frame = 0
                if self.seeside == self.RIGHTSIDE:
                    self.state = self.R_Z_SKILL
                else:
                    self.state = self.L_Z_SKILL
            elif (event.key == SDLK_x):
                self.frame = 0
                if self.seeside == self.RIGHTSIDE:
                    self.state = self.R_X_SKILL
                else:
                    self.state = self.L_X_SKILL
            elif (event.key == SDLK_c):
                self.frame = 0
                if self.seeside == self.RIGHTSIDE:
                    self.state = self.R_C_SKILL
                else:
                    self.state = self.L_C_SKILL
            elif (event.key == SDLK_v):
                self.frame = 0
                if self.seeside == self.RIGHTSIDE:
                    self.state = self.R_V_SKILL
                else:
                    self.state = self.L_V_SKILL
            elif event.key == SDLK_SPACE:
                if self.isground:
                    self.handle_jump(frame_time)
            elif event.key == SDLK_LEFT:
                self.leftinput = True
                self.seeside = self.LEFTSIDE
                self.frame = 0
                self.state = self.LEFT_RUN
            elif event.key == SDLK_RIGHT:
                self.rightinput = True
                self.seeside = 1
                self.frame = 0
                self.state = self.RIGHT_RUN
            elif event.key == SDLK_DOWN:
                if self.y >= 300:
                    self.down()
            elif event.key == SDLK_g:
                self.use_active_item()
        elif ((event.type == SDL_KEYUP) and not(self.aniemelock)):
            if event.key == SDLK_LEFT:
                self.leftinput = False
                if self.seeside == self.LEFTSIDE:
                    if self.rightinput:
                        self.seeside = 1
                        self.state = self.RIGHT_RUN
                    else:
                        self.state = self.LEFT_IDLE
            elif event.key == SDLK_RIGHT:
                self.rightinput = False
                if self.seeside == self.RIGHTSIDE:
                    if self.leftinput:
                        self.seeside = self.LEFTSIDE
                        self.state = self.LEFT_RUN
                    else:
                        self.state = self.RIGHT_IDLE


from logging import INFO
from typing import Dict

from flask import Flask, request
from flask.logging import create_logger

from dialogflow_fulfillment import WebhookClient

# Create Flask app and enable info level logging
app = Flask(__name__)
logger = create_logger(app)
logger.setLevel(INFO)

def handler(agent: WebhookClient) -> None:
    """Handle the webhook request."""

@app.route('/', methods=['POST'])
def webhook() -> Dict:
    """Handle webhook requests from Dialogflow."""
    # Get WebhookRequest object
    request_ = request.get_json(force=True)

    # Log request headers and body
    logger.info(f'Request headers: {dict(request.headers)}')
    logger.info(f'Request body: {request_}')

    # Handle request
    agent = WebhookClient(request_)
    agent.handle_request(handler)

    # Log WebhookResponse object
    logger.info(f'Response body: {agent.response}')

    return agent.response

if __name__ == '__main__':
    app.run(debug=True)


from __future__ import division

import torch
import torch.nn  as nn
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np
import cv2

def unique(tensor):
    """
    multiple true detections of the same class
    :param tensor:  class_confidence_max_index (10647)
    :return:
    """
    tensor_np = tensor.cpu().numpy()
    unique_np = np.unique(tensor_np)
    unique_tensor = torch.from_numpy(unique_np)

    tensor_res = tensor.new(unique_tensor.shape)
    tensor_res.copy_(unique_tensor)
    return tensor_res

def bbox_iou(box1, box2):
    b1_x1, b1_y1, b1_x2, b1_y2 = box1[:,0], box1[:,1], box1[:,2], box1[:,3]
    b2_x1, b2_y1, b2_x2, b2_y2 = box2[:, 0], box2[:, 1], box2[:, 2], box2[:, 3]

    inter_rect_x1 = torch.max(b1_x1, b2_x1)
    inter_rect_y1 = torch.max(b1_y1, b2_y1)
    inter_rect_x2 = torch.min(b1_x2, b2_x2)
    inter_rect_y2 = torch.min(b1_y2, b2_y2)

    inter_area = torch.clamp(inter_rect_x2 - inter_rect_x1 + 1, min = 0) * torch.clamp( inter_rect_y2  - inter_rect_y1 + 1, min = 0)

    b1_area = (b1_x2 - b1_x1 + 1) * (b1_y2 - b1_y1 + 1)  #  + 1 why???
    b2_area = (b2_x2 - b2_x1 + 1) * (b2_y2 - b2_y1 + 1)
    iou = inter_area / (b1_area + b2_area - inter_area)
    return iou

def predict_transform(prediction, inp_dim, anchors, num_classes, CUDA = False):
    """
    for solve 2 problems
    p1. the processing such as thresholding by a object confidence, adding grid offsets to centers, applying anchors etc.
    p2. the dimensions of the prediction maps will be different
    convert detection feature map to 2-D tensor
    (13,13,B,(5+C)) --> (13X13XB, (5+C))
    :param prediction: the original output of module
    :param inp_dim:
    :param achors:
    :param num_classes:
    :param CUDA:
    :return:
    """
    #print("prediction:" ,prediction.shape)
    batch_size = prediction.size(0)
    stride = inp_dim // prediction.size(2)  # original image -> feature map ; e.g: 416x416 -> 13x13 stride = 32

    grid_size = inp_dim // stride
    print("new", inp_dim, stride, grid_size)
    bbox_attrs = 5 + num_classes
    num_anchors = len(anchors)
    anchors = [(a[0] / stride, a[1] / stride) for a in anchors]
    #print(batch_size, bbox_attrs * anchors, )
    #print(batch_size, bbox_attrs * num_anchors, grid_size * grid_size)
    prediction = prediction.view(batch_size, bbox_attrs * num_anchors, grid_size * grid_size)  # (m, 85*3, 13*13) box1,box2 arranged in Horizontal
    prediction = prediction.transpose(1,2).contiguous()  # (m, 13*13, 85*3) contiguous means copy
    prediction = prediction.view(batch_size, grid_size * grid_size*num_anchors, bbox_attrs)  # (m, 13*13*3, 85)

    x_y_pre  = prediction[:,:,:2]  # the center of the anchor
    w_h_pre = prediction[:,:,2:4]  # the width and height of the anchor
    class_pre = prediction[:,:,5 : 5 + num_classes]  # the one-hot result of class

    anchors = [(a[0] / stride, a[1] / stride) for a in anchors]
    prediction[:, :, 0] = torch.sigmoid(prediction[:, :, 0])
    prediction[:, :, 1] = torch.sigmoid(prediction[:, :, 1])
    prediction[:, :, 4] = torch.sigmoid(prediction[:, :, 4])

    # add the grid offsets to the prediction od center coordination
    # add the center offsets
    grid = np.arange(grid_size)
    a,b = np.meshgrid(grid, grid)

    x_offset = torch.FloatTensor(a).view(-1,1)
    y_offset = torch.FloatTensor(b).view(-1,1)

    if CUDA:
        x_offset = x_offset.cuda()
        y_offset = y_offset.cuda()

    # the process of the transform can be seen in predict_transform_process.py
    x_y_offset = torch.cat((x_offset, y_offset), 1).repeat(1, num_anchors).view(-1, 2).unsqueeze(0) #[1,13*13*3,2]

    prediction[:,:,:2] += x_y_offset

    # apply the anchors to the dimensions of the bounding
    anchors = torch.FloatTensor(anchors)
    if CUDA:
        anchors = anchors.cuda()

    anchors = anchors.repeat(grid_size * grid_size, 1).unsqueeze(0)  # [1,13*13*3,2]
    prediction[:,:,2:4] = torch.exp(prediction[:,:,2:4])*anchors  # [1,13*13*3,2]

    # sigmoid activation to the class scores
    prediction[:,:,5:5 + num_classes] = torch.sigmoid((prediction[:,:,5:5 + num_classes]))

    # resize the detections map to the size of the input image
    prediction[:,:,:4] *= stride
    return prediction

def write_results(prediction, confidence, num_classes, nms_conf=0.4):
    conf_mask = (prediction[:, :, 4] > confidence).float().unsqueeze(2)
    prediction = prediction * conf_mask

    box_corner = prediction.new(prediction.shape)
    box_corner[:, :, 0] = (prediction[:, :, 0] - prediction[:, :, 2] / 2)
    box_corner[:, :, 1] = (prediction[:, :, 1] - prediction[:, :, 3] / 2)
    box_corner[:, :, 2] = (prediction[:, :, 0] + prediction[:, :, 2] / 2)
    box_corner[:, :, 3] = (prediction[:, :, 1] + prediction[:, :, 3] / 2)
    prediction[:, :, :4] = box_corner[:, :, :4]

    batch_size = prediction.size(0)

    write = False

    for ind in range(batch_size):
        image_pred = prediction[ind]
        print("image_pred", image_pred.shape)
        # image Tensor
        # #confidence threshholding
        # #NMS

        max_conf, max_conf_score = torch.max(image_pred[:, 5:5 + num_classes], 1)
        max_conf = max_conf.float().unsqueeze(1)
        max_conf_score = max_conf_score.float().unsqueeze(1)
        seq = (image_pred[:, :5], max_conf, max_conf_score)
        image_pred = torch.cat(seq, 1)

        non_zero_ind = (torch.nonzero(image_pred[:, 4]))
        try:
            image_pred_ = image_pred[non_zero_ind.squeeze(), :].view(-1, 7)
        except:
            continue

        if image_pred_.shape[0] == 0:
            continue
        print(".....image_pred_>>>>>>>", image_pred_.shape)
        print("image_pred_", image_pred_.shape)
        # Get the various classes detected in the image
        img_classes = unique(image_pred_[:, -1])  # -1 index holds the class index

        print("img_classes shape", img_classes)
        print("image_pred_", image_pred_.shape)
        for cls in img_classes:
            print("cls", cls)
            # perform NMS

            # get the detections with one particular class
            cls_mask = image_pred_ * (image_pred_[:, -1] == cls).float().unsqueeze(1)
            class_mask_ind = torch.nonzero(cls_mask[:, -2]).squeeze()
            image_pred_class = image_pred_[class_mask_ind].view(-1, 7)

            # sort the detections such that the entry with the maximum objectness
            # confidence is at the top
            conf_sort_index = torch.sort(image_pred_class[:, 4], descending=True)[1]
            image_pred_class = image_pred_class[conf_sort_index]
            idx = image_pred_class.size(0)  # Number of detections

            for i in range(idx):
                # Get the IOUs of all boxes that come after the one we are looking at
                # in the loop
                try:
                    ious = bbox_iou(image_pred_class[i].unsqueeze(0), image_pred_class[i + 1:])
                except ValueError:
                    break

                except IndexError:
                    break

                # Zero out all the detections that have IoU > treshhold
                iou_mask = (ious < nms_conf).float().unsqueeze(1)
                image_pred_class[i + 1:] *= iou_mask

                # Remove the non-zero entries
                non_zero_ind = torch.nonzero(image_pred_class[:, 4]).squeeze()
                image_pred_class = image_pred_class[non_zero_ind].view(-1, 7)

            batch_ind = image_pred_class.new(image_pred_class.size(0), 1).fill_(
                ind)  # Repeat the batch_id for as many detections of the class cls in the image
            seq = batch_ind, image_pred_class

            if not write:
                output = torch.cat(seq, 1)
                write = True
            else:
                out = torch.cat(seq, 1)
                output = torch.cat((output, out))

    try:
        return output
    except:
        return 0

# def write_results(prediction, confidence, num_classes, nms_conf = 0.4):
#     """
#
#     :param prediction:   (m, 10647, 85)
#     :param confidence:
#     :param num_classes:
#     :param nms_conf:
#     :return:
#     """
#     # prob_score
#     prob_score = prediction[:,:,4]
#     print("prediction shape : ", prediction.shape)
#     conf_mask = (prediction[:,:,4] > confidence).float().unsqueeze(2)  #  (m, 10647, 1)
#     print("conf_mask: ", conf_mask.shape)
#     prediction = prediction * conf_mask  #  (m, 10647, 85) * (m, 10647, 1)
#     print("prediction shape : ", prediction.shape)  # (m, 10647, 85)
#
#     # Non-maximum Suppression
#     # transform x,y,w,h to x1,y1,x2,y2 (bounding box)
#     box_corner = prediction.new(prediction.shape)
#     box_corner[:, :, 0] = (prediction[:, :, 0] - prediction[:, :, 2] / 2)
#     box_corner[:, :, 1] = (prediction[:, :, 1] - prediction[:, :, 3] / 2)
#     box_corner[:, :, 2] = (prediction[:, :, 0] + prediction[:, :, 2] / 2)
#     box_corner[:, :, 3] = (prediction[:, :, 1] + prediction[:, :, 3] / 2)
#     prediction[:,:,:4] = box_corner[:,:,:4]
#
#     # loop over the batch size
#     batch_size = prediction.size(0)
#     write = False  # ????
#     for ind in range(batch_size):
#
#         image_pred  = prediction[ind]
#         print("image_pred", image_pred.shape)
#         max_conf, max_conf_score = torch.max(image_pred[:, 5: 5+ num_classes], 1)
#         max_conf = max_conf.float().unsqueeze(1)
#         max_conf_score = max_conf_score.float().unsqueeze(1)
#         seq = (image_pred[:, :5], max_conf, max_conf_score)
#         image_pred = torch.cat(seq, 1) # (10647, 7) [x1,y1,x2,y2, object_confidence, class_confidence_max, class_confidence_max_index]
#
#         # gid rid of bounding box with low confidence
#         non_zero_ind = (torch.nonzero(image_pred[:,4]))
#         try:
#             image_pred_ = image_pred[non_zero_ind.squeeze(), :].view(-1,7)  # (?, 7)
#         except:
#             continue
#         if image_pred_.shape[0]== 0:
#             continue
#         print("image_pred_", image_pred_.shape)
#         img_classes = unique(image_pred_[:,-1]) # class index
#         print("img_classes shape", img_classes)
#         print("image_pred_", image_pred_.shape)
#         for cls in img_classes:
#             print("cls", cls)
#             # perform NMS for each class
#             # extract the detections of a particular class
#             cls_mask = image_pred_ * (image_pred_[:,-1] == cls).float().unsqueeze(1) #(?, 7)
#             class_mask_ind = torch.nonzero(cls_mask[:,-2]).squeeze()  #
#             image_pred_class = image_pred_[class_mask_ind].view(-1,7)
#
#             # sort the detections
#             conf_sort_index = torch.sort(image_pred_class[:,4], descending=True)[1]
#             image_pred_class = image_pred_class[conf_sort_index]
#             idx = image_pred_class.size(0)
#
#             for i in range(idx):
#                 try:
#                     ious = bbox_iou(image_pred_class[i].unsqueeze(0), image_pred_class[i+1:])
#                 except ValueError:
#                     break
#                 except IndexError:
#                     break
#
#                 iou_mask = (ious < nms_conf).float().unsqueeze(1)
#                 image_pred_class[i+1:] *= iou_mask
#
#                 # remove the non-zero entries
#                 non_zero_ind = torch.nonzero(image_pred_class[:,4]).squeeze()
#                 image_pred_class = image_pred_class[non_zero_ind].view(-1, 7)
#             batch_ind = image_pred_class.new(image_pred_class.size(0), 1).fill_(ind)
#
#             seq = batch_ind, image_pred_class
#             if not write:
#                 output = torch.cat(seq, 1)
#                 write = True
#             else:
#                 out = torch.cat(seq, 1)
#                 output = torch.cat((output, out))
#     try :
#         return output
#     except:
#         return 0

def letterbox_image(img, inp_dim):
    """
    resize the image keep aspect ratio
    :param img:
    :param inp_dim:
    :return:  the image which have the input size 416 416 3
    """
    img_w, img_h = img.shape[1], img.shape[0]   # img: HWC
    w,h = inp_dim
    new_w = int(img_w * min(w/img_w, h/img_h))
    new_h = int(img_h * min(w/img_w, h/img_h))
    resized_image = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_CUBIC)  # WHC 416 416 3
    canvas = np.full((inp_dim[1], inp_dim[0], 3), 128)  # HWC 416 416 3
    canvas[(h - new_h) // 2:(h - new_h) // 2 + new_h, (w - new_w) // 2:(w - new_w) // 2 + new_w, :] = resized_image
    return canvas

def prep_image(img, inp_dim):
    """
    prepare image
    :param img:
    :param inp_dim:
    :return:
    """
    img = letterbox_image(img, (inp_dim, inp_dim))  # ( 416,416,3)
    img = img[:,:,::-1].transpose((2,0,1)).copy()    # (416,416,3) --> (3,416,416) HWC --> CHW (BGR --> RGB)
    img = torch.from_numpy(img).float().div(255.0).unsqueeze(0)  # (3,416,416) --> (1,3,416,416) BCHW
    return img

def load_classes(filepath):
    fp = open(filepath)
    names = fp.read().split("\n")[:-1]
    return names


from psycopg2.sql import SQL, Identifier
from tqdm import tqdm

import datetime
import multiprocessing
import numpy as np
import psycopg2
import sys

config = snakemake.config
DSN = f"host=dbserver user={config['user']} dbname={config['database']}"

class Worker(object):

    def __init__(self):

        self.bth_con = psycopg2.connect(DSN)

    def __call__(self, tasks):

        query = f"""
            BEGIN;

            SET search_path TO {config['schema']};

            WITH cte_txt_by_visit AS (
                SELECT
                    visit_key,
                    regexp_replace(string_agg(retained_tokens, ' '), '\s+', ' ', 'gi') AS tokens
                FROM {config['preprocess_table']} AS txt
                INNER JOIN adm
                    ON adm.pid = txt.pid
                    AND txt.datetime BETWEEN adm.adm_datetime
                        AND adm.adm_datetime + '{snakemake.params['window']} hours'::interval
                WHERE visit_key = ANY(%s)
                GROUP BY visit_key
            ),
            cte_tokens AS (
                SELECT
                    visit_key
                    , unnest(string_to_array(tokens, ' ')) AS token
                FROM cte_txt_by_visit
            )
            INSERT INTO {config['tf_table']}
            SELECT
                visit_key
                , token
                , count(token) as tf
            FROM cte_tokens
            WHERE token <> ''
            GROUP BY visit_key, token;

            COMMIT;
        """

        with self.bth_con.cursor() as cur:
            cur.execute(query, (tasks, ))
            self.bth_con.commit()

        return None

def subprocess(tasks):

    sys.stdout = open(snakemake.log[0], "a")

    worker = Worker()
    for i, chunk in enumerate(np.array_split(tasks, 100)):
        chunk = chunk.tolist()
        worker(chunk)

    print(str(datetime.datetime.now()) + " done")

    sys.stdout.close()

    return None

def child_initialize(_worker, _config, _snakemake):

    global Worker, config, snakemake, DSN
    Worker = _worker
    config = _config
    snakemake = _snakemake
    DSN = f"host=dbserver user={config['user']} dbname={config['database']}"

if __name__ == "__main__":

    sys.stdout = open(snakemake.log[0], "a")

    print ("###\tTable Init.. \n")
    con = psycopg2.connect(DSN)
    cur = con.cursor()
    cur.execute(f"""
        BEGIN;
        DROP TABLE IF EXISTS {config['tf_table']} CASCADE;
        CREATE TABLE {config['tf_table']} (visit_key TEXT, token TEXT, tf INT);
        GRANT ALL PRIVILEGES ON {config['tf_table']} TO bth_user;
        COMMIT;
        """)
    con.close()

    print ("###\tPopulate table.. \n")
    con = psycopg2.connect(DSN)

    with con.cursor("cur1") as cur1:

        print("Setting up multiprocessing")
        cur1.itersize = 10000 # only with named cursor
        query = f"""
            SELECT DISTINCT visit_key
            FROM {config['preprocess_table']} AS txt
            INNER JOIN nlpandmed.adm
                ON adm.contact_type = 'I'
                AND adm.pid = txt.pid
                AND txt.datetime BETWEEN adm.adm_datetime
                    AND adm.adm_datetime + '{snakemake.params["window"]} hours'::interval;
        """
        cur1.execute(query)
        tasks = [el[0] for el in cur1]
        tasks = np.array_split(tasks, snakemake.threads)

    con.commit()
    con.close()

    print ("Start multiprocessing.. ")
    with multiprocessing.Pool(snakemake.threads, initializer=child_initialize,
        initargs=(Worker, config, snakemake)) as pool:
        pool.map(subprocess, tasks)

    con = psycopg2.connect(DSN)
    cur = con.cursor()

    print(str(datetime.datetime.now()) + " Creating index")
    cur.execute(f"CREATE INDEX ON {config['tf_table']} USING HASH (token);")

    print(str(datetime.datetime.now()) + " Creating view with distinct visit_keys")
    cur.execute(f"""
        BEGIN;
        SET SEARCH_PATH TO {config['schema']};

        DROP MATERIALIZED VIEW IF EXISTS {config['tf_table']}_visit_keys;

        CREATE MATERIALIZED VIEW {config['tf_table']}_visit_keys AS
        SELECT DISTINCT visit_key FROM {config['tf_table']};

        COMMIT;
    """)

    print(str(datetime.datetime.now()) + " Creating view with distinct tokens")
    cur.execute(f"""
        BEGIN;
        SET SEARCH_PATH TO {config['schema']};

        DROP MATERIALIZED VIEW IF EXISTS {config['tf_table']}_tokens;

        CREATE MATERIALIZED VIEW {config['tf_table']}_tokens AS
        SELECT DISTINCT token FROM {config['tf_table']};

        COMMIT;
    """)

    con.close()

    with open(snakemake.output[0], "w") as f:
        f.write(str(datetime.datetime.now()))

    sys.stdout.close()


import math
import os
from matplotlib import figure
import matplotlib
import numpy as np
import json
import matplotlib.pyplot as plt
import matplotlib.colors as colors
from multipath_sim.coord import conv_gzb_to_latlon, conv_latlon_to_gzb
import utm
data_dir = os.path.abspath( os.path.join(os.path.dirname(__file__), os.pardir)) + "/data/"

gps_pos_file = data_dir + "pos_data_log.json"

gzb_gps_info = {}
with open(gps_pos_file, "r") as fs:
    gzb_gps_info = json.load(fs)
gzb_true_pos = np.array(gzb_gps_info['true_pos'])
gzb_gps_pos = np.array(gzb_gps_info['gps_pos'])

grd_truth = {}
with open(data_dir+"car_ground_truth.json", "r") as fs:
    grd_truth = json.load(fs)

gps_log = {}
with open(data_dir+"hk_data_car_gps_log.json", "r") as fs:
    gps_log = json.load(fs)

# Lat/Lon bounds for the segment of the map in gazebo
lat_bound = np.array([22.3066, 22.2903])
lon_bound = np.array([114.171, 114.188])

# Applying correction in the offset to align the origin
origin_offset = np.array([77, 15])

# Get the Lat/Lon from ENU (gzb world)
#lat, lon = conv_gzb_to_latlon(gzb_true_pos[:,0], gzb_true_pos[:,1], lat_bound, lon_bound, origin_offset)
x, y = conv_latlon_to_gzb(np.array(gps_log["lat"]),np.array(gps_log["lon"]), lat_bound, lon_bound, origin_offset)

plt.figure(figsize=(8,6))
plt.plot(gzb_true_pos[:,0], gzb_true_pos[:,1],linestyle = 'solid', color = 'black', marker = None,markevery=100, markersize=5, linewidth=1)
plt.plot(gzb_gps_pos[:,0], gzb_gps_pos[:,1], linestyle = 'solid', color = 'red', marker = 'v', markevery=50 , markersize=5, linewidth=1)
plt.plot(x[:393], y[:393], color = 'blue', marker = '.', markersize=5, linewidth=1)
#plt.plot(x[:393], y[:393], '.',linestyle = 'dotted', color = 'blue', marker = 'v', markevery=100  ,markersize=5, linewidth=1)
plt.legend(["Ground Truth", "GNSS Multipath Plugin", "u Blox EKV-M8T"], fontsize = 14)
#plt.axis('off')
plt.xlabel('Easting (m)', fontsize = 14)
plt.ylabel('Northing (m)', fontsize = 14)
plt.grid()
plt.savefig('comparison_plugin_M8T.pdf')
plt.show()

# #Calculating the size of the heat map based on the lat lon values
# scale = 5
# size_x_min = (int)((np.abs(gzb_true_pos[:,0].min()))/scale)
# size_y_min = (int)((np.abs(gzb_true_pos[:,1].min()))/scale)
# size_x = (int)((np.abs(gzb_true_pos[:,0].max()))/scale + size_x_min)
# size_y = (int)((np.abs(gzb_true_pos[:,1].max()))/scale + size_y_min)

# heat_map = np.zeros((size_y+1,size_x+1))
# for i in range(gzb_true_pos.shape[0]):
#     lon = (int)(gzb_true_pos[i,1]/scale + size_y_min)
#     lat = (int)(gzb_true_pos[i,0]/scale + size_x_min)
#     #Computing the norm of the offset at each location
#     #print(lon, lat, gzb_true_pos[i,1],gzb_true_pos[i,0], max_offset[i])
#     # if max_offset[i] > 10:
#     #     max_offset[i]=10
#     # heat_map[lon,lat] = max_offset[i]

#     error = np.linalg.norm(gzb_true_pos[i,:] - gzb_gps_pos[i,:])
#     if not math.isnan(error):
#         if error > 20:
#             error = 20
#         heat_map[lon,lat] = error
#     else:
#         heat_map[lon,lat] = 20

# from matplotlib.pyplot import cm
# import matplotlib.colors
# cmap = colors.LinearSegmentedColormap.from_list("", ["green","yellow","orange","red"])
# plt.imshow(heat_map, cmap=cmap, interpolation='bilinear')
# #plt.colorbar()
# plt.gca().invert_yaxis()
# plt.axis('off')
# cb = plt.colorbar()
# cb.remove()
# #plt.savefig('ComparisonPlot.png', dpi=500)
# plt.show()
# plt.figure(figsize=(10,10))
# speed = np.zeros(500)
# for i in range (1,500):
#     speed[i]= np.sqrt((grd_truth['lat'][i] - grd_truth['lat'][i-1])**2 + (grd_truth['lon'][i] - grd_truth['lon'][i-1])**2)
# plt1 = plt.plot(speed, label = 'Speed')
# print(len(grd_truth['lat']),len(gps_log['lat']))
# error = np.zeros(500)
# for i in range (500):
#     error[i]= np.sqrt((grd_truth['lat'][i] - gps_log['lat'][i])**2 + (grd_truth['lon'][i] - gps_log['lon'][i])**2)
# plt2 = plt.gca().twinx().plot(error, color = 'r', label = 'Error in GPS position')
# lns = plt1 + plt2
# labels = [l.get_label() for l in lns]
# plt.legend(lns, labels, loc=0)
# plt.show()

# plt.figure(figsize=(10,10))
# speed = np.zeros(len(aff_pos['true_x']))
# for i in range (1,len(aff_pos['true_x'])):
#     speed[i]= np.sqrt((aff_pos['true_x'][i] - aff_pos['true_x'][i-1])**2 + (aff_pos['true_y'][i] - aff_pos['true_y'][i-1])**2)
# plt1 = plt.plot(speed, label = 'Speed')

# error = np.zeros(len(aff_pos['true_x']))
# for i in range (len(aff_pos['true_x'])):
#     error[i]= np.sqrt((aff_pos['true_x'][i] - aff_pos['aff_x'][i])**2 + (aff_pos['true_y'][i] - aff_pos['aff_y'][i])**2)
# plt2 = plt.gca().twinx().plot(error, color = 'r', label = 'Error in GPS position')
# lns = plt1 + plt2
# labels = [l.get_label() for l in lns]
# plt.legend(lns, labels, loc=0)
# plt.show()


from bs4 import BeautifulSoup
import fnmatch
import logging
import os
import requests
from xml.dom import minidom
from kodi.io_utils import determine_dirs, prompt, read_id, TAG_MOVIE, TAG_TVSHOW, FILENAME_TVSHOW, get_nfo_file, json_loads
from kodi.xml_utils import add_node, output_xml
from kodi.imdb_series import has_episodes, create_episodes_url, extract_seasons, extract_episodes, episode_to_xml, \
    extract_season_episode, determine_episodes

# logging setup
logger = logging.getLogger("kodi.imdb")

def generate_imdb(id, language="en", fanart="none", fanart_file="folder.jpg", path=None, overwrite=False, dry_run=False,
                  episodes=False, episode_pattern="*S??E??*.*",
                  season_group=".*S([0-9]?[0-9])E.*", episode_group=".*E([0-9]?[0-9]).*",
                  ua="Mozilla"):
    """
    Generates the XML for the specified IMDB ID.

    :param id: the IMDB ID to use
    :type id: str
    :param language: the preferred language for the titles
    :type language: str
    :param fanart: how to deal with fanart
    :type fanart: str
    :param fanart_file: the fanart filename to use (when downloading or re-using existing)
    :type fanart_file: str
    :param path: the current directory (used for determining episode files)
    :type path: str
    :param overwrite: whether to overwrite existing .nfo files (ie recreating them)
    :type overwrite: bool
    :param dry_run: whether to perform a 'dry-run', ie generating .nfo content but not saving them (only outputting them to stdout)
    :type dry_run: bool
    :param episodes: whether to generate episode information as well
    :type episodes: bool
    :param episode_pattern: the pattern to use for locating episode files
    :type episode_pattern: str
    :param season_group: the regular expression to extract the season (first group)
    :type season_group: str
    :param episode_group: the regular expression to extract the episode (first group)
    :type episode_group: str
    :param ua: the user agent to use, ignore if empty string or None
    :type ua: str
    :return: whether a file was generated
    :rtype: bool
    """
    id = id.strip()

    # can we skip?
    if not overwrite:
        f = get_nfo_file(path)
        if f is not None:
            logger.info(".nfo file already exists, skipping: %s" % f)
            return False

    # default movie .nfo
    xml_path = os.path.join(path, os.path.basename(path) + ".nfo")

    # generate URL
    if id.startswith("http"):
        url = id
    else:
        url = "https://www.imdb.com/title/%s/" % id
    logger.info("IMDB URL: " + url)

    # retrieve html
    headers = {"Accept-Language": language}
    if (ua is not None) and (ua != ""):
        headers['user-agent'] = ua
    r = requests.get(url, headers=headers)
    if r.status_code != 200:
        logging.critical("Failed to retrieve URL (status code %d): %s" % (r.status_code, url))

    # parse html
    soup = BeautifulSoup(r.content, "html.parser")

    doc = minidom.Document()

    widget = soup.find("div", id="star-rating-widget")
    if widget is None:
        preflang_title = None
    else:
        preflang_title = widget["data-title"]

    output_generated = False
    for script in soup.findAll("script", type="application/ld+json"):
        j = json_loads(script.text)
        logger.debug(j)

        root = add_node(doc, doc, TAG_MOVIE)
        add_node(doc, root, "title", j['name'] if preflang_title is None else preflang_title)
        add_node(doc, root, "originaltitle", j["name"])
        uniqueid = add_node(doc, root, "uniqueid", j["url"].replace("/title/", "").replace("/", ""))
        uniqueid.setAttribute("type", "imdb")
        uniqueid.setAttribute("default", "true")
        if "description" in j:
            add_node(doc, root, "plot", j["description"])
            add_node(doc, root, "outline", j["description"])
        if "datePublished" in j:
            add_node(doc, root, "premiered", j["datePublished"])
        if "director" in j and "name" in j["director"]:
            add_node(doc, root, "director", j["director"]["name"])
        if "genre" in j:
            if isinstance(j["genre"], list):
                for genre in j["genre"]:
                    add_node(doc, root, "genre", genre)
            else:
                add_node(doc, root, "genre", j["genre"])
        if "actor" in j:
            for actor in j["actor"]:
                xactor = add_node(doc, root, "actor")
                add_node(doc, xactor, "name", actor["name"])
        if "trailer" in j and "embedUrl" in j["trailer"]:
            add_node(doc, root, "trailer", "https://www.imdb.com" + j["trailer"]["embedUrl"])
        if "aggregateRating" in j and "ratingValue" in j["aggregateRating"]:
            xratings = add_node(doc, root, "ratings")
            xrating = add_node(doc, xratings, "rating")
            xrating.setAttribute("name", "imdb")
            xrating.setAttribute("max", "10")
            add_node(doc, xrating, "value", str(j["aggregateRating"]["ratingValue"]))

        # fanart
        fanart_path = os.path.join(path, fanart_file)
        fanart_act = fanart
        if fanart_act == "download-missing":
            if os.path.exists(fanart_path):
                fanart_act = "use-existing"
            else:
                fanart_act = "download"
        if fanart_act == "download":
            if "image" in j:
                logger.info("Downloading fanart: %s" % j["image"])
                if (ua is not None) and (ua != ""):
                    r = requests.get(j["image"], headers={'user-agent': ua}, stream=True)
                else:
                    r = requests.get(j["image"], stream=True)
                if r.status_code == 200:
                    with open(fanart_path, 'wb') as f:
                        for chunk in r:
                            f.write(chunk)
                    xthumb = add_node(doc, root, "thumb", fanart_file)
                    xthumb.setAttribute("aspect", "poster")
                else:
                    logger.critical("Failed to download fanart, status code: " % r.status_code)
            else:
                logger.warning("No image associated, cannot download!")
        elif fanart_act == "use-existing":
            xthumb = add_node(doc, root, "thumb", fanart_file)
            xthumb.setAttribute("aspect", "poster")
        elif fanart_act == "none":
            pass
        else:
            logger.critical("Ignoring unhandled fanart type: %s" % fanart_act)

        if has_episodes(j):
            logger.info("Has episode data")

            # use special name rather than name-based one
            xml_path = os.path.join(path, FILENAME_TVSHOW)

            # update root tag
            root.tagName = TAG_TVSHOW

            if episodes:
                # determine seasons
                url = create_episodes_url(id)
                logger.info("Default episodes URL: %s" % url)
                r = requests.get(url, headers={"Accept-Language": language, 'user-agent': ua})
                if r.status_code != 200:
                    logging.critical("Failed to retrieve URL (status code %d): %s" % (r.status_code, url))
                    continue
                soup_ep = BeautifulSoup(r.content, "html.parser")
                seasons = extract_seasons(soup_ep)
                logger.info("Available seasons: %s" % ", ".join(seasons))

                # determine seasons
                seasons_episodes = determine_episodes(path, episode_pattern=episode_pattern,
                                                      season_group=season_group, episode_group=episode_group)
                logger.info("Located seasons: %s" % ", ".join(sorted(seasons_episodes.keys())))

                # extract episodes for seasons on disk
                season_data = {}
                for season in seasons_episodes.keys():
                    season_data[season] = {}
                    url = create_episodes_url(id, season=season)
                    logger.info("Season %s URL: %s" % (season, url))
                    r = requests.get(url, headers={"Accept-Language": language, 'user-agent': ua})
                    if r.status_code != 200:
                        logging.critical("Failed to retrieve URL (status code %d): %s" % (r.status_code, url))
                        continue
                    soup_ep = BeautifulSoup(r.content, "html.parser")
                    episodes_data = extract_episodes(soup_ep, season)
                    for k in episodes_data:
                        xml = episode_to_xml(episodes_data[k])
                        season_data[season][k] = xml

                # locate files and output XML
                dirs = []
                determine_dirs(path, True, dirs)
                for d in dirs:
                    files = fnmatch.filter(os.listdir(d), episode_pattern)
                    for f in files:
                        if f.endswith(".nfo"):
                            continue
                        parts = extract_season_episode(f, season_group=season_group, episode_group=episode_group)
                        if parts is None:
                            continue
                        s, e = parts
                        if (s in season_data) and (e in season_data[s]):
                            xml_path_ep = os.path.join(d, os.path.splitext(f)[0] + ".nfo")
                            if output_xml(season_data[s][e], xml_path_ep, dry_run=dry_run, overwrite=overwrite, logger=logger):
                                output_generated = True

        # output .nfo
        if output_xml(doc, xml_path, dry_run=dry_run, overwrite=overwrite, logger=logger):
            output_generated = True

        return output_generated

def guess_imdb(title, meta_path, language="en", dry_run=False, ua="Mozilla"):
    """
    Generates .imdb files using the user's selection of IMDB search results for the title.

    :param title: the title to look for
    :type title: str
    :param meta_path: the file to store the IMDB title ID in
    :type meta_path: str
    :param language: the preferred language for the titles
    :type language: str
    :param dry_run: whether to perform a 'dry-run', ie generating .nfo content but not saving them (only outputting them to stdout)
    :type dry_run: bool
    :param ua: the user agent to use, ignore if empty string or None
    :type ua: str
    :return: whether to continue
    :rtype: bool
    """
    url = "https://www.imdb.com/find/"
    params = {'q': title}
    headers = {"Accept-Language": language}
    if (ua is not None) and (ua != ""):
        headers['user-agent'] = ua
    r = requests.get(url, params=params, headers=headers)
    if r.status_code != 200:
        logging.critical("Failed to retrieve URL (status code %d): %s" % (r.status_code, url))

    # in case we are overwriting files
    tid = None
    if os.path.exists(meta_path):
        tid = read_id(meta_path)

    # parse html
    soup = BeautifulSoup(r.content, "html.parser")

    for script in soup.findAll("script", id="__NEXT_DATA__", type="application/json"):
        j = json_loads(script.text)
        logger.debug(j)
        if ("props" not in j) or ("pageProps" not in j["props"]) or ("titleResults" not in j["props"]["pageProps"]):
            continue
        title_results = j["props"]["pageProps"]["titleResults"]
        if "results" in title_results:
            choices = []
            if len(title_results["results"]) == 0:
                print("0. No results, continue...")
            else:
                for i, result in enumerate(title_results["results"], start=1):
                    # marker for current title?
                    if (tid is not None) and (tid == result["id"]):
                        current = " <-- current"
                    else:
                        current = ""
                    print("%d. %s: %s (%s)%s" % (i, result["id"], result["titleNameText"], result["titleReleaseText"], current))
                    choices.append(str(i))
                print("0. None of the above, continue...")
            print("X. Exit")
            choices.append("0")
            choices.append("X")
            choice = prompt("Your selection (%s): ", choices=choices)
            if choice == "X":
                print("User requested exit.")
                return False
            elif choice == "0":
                return True
            else:
                tid = title_results["results"][int(choice)-1]["id"]
                if dry_run:
                    print(tid)
                else:
                    logger.info("Writing ID %s to: %s" % (tid, meta_path))
                    with open(meta_path, "w") as f:
                        f.write(tid)
                return True


import configparser
from datetime import datetime
import os
from pyspark.sql import SparkSession
from pyspark.sql import types as T
from pyspark.sql.functions import udf, col, year, month
from pyspark.sql.functions import dayofmonth, hour, weekofyear, date_format
from pyspark.sql.functions import dayofweek, monotonically_increasing_id

config = configparser.ConfigParser()
config.read('dl.cfg')

os.environ['AWS_ACCESS_KEY_ID'] = config['DEFAULT']['AWS_ACCESS_KEY_ID']
os.environ['AWS_SECRET_ACCESS_KEY'] = config['DEFAULT']['AWS_SECRET_ACCESS_KEY']

def create_spark_session():
    '''
    Returns an instance of a SparkSession
    '''
    spark = SparkSession \
        .builder \
        .config("spark.jars.packages", "org.apache.hadoop:hadoop-aws:2.7.0") \
        .getOrCreate()
    return spark

def process_song_data(spark, input_data, output_data):
    '''
    Process song_data, extracts songs and artists details
    and creates parquet structures for each

    Arguments:
    ----------
    spark -- spark session instance\n
    input_data -- origin of data to process\n
    output_data -- destination of processed data
    '''
    # get filepath to song data file
    song_data = '{}song_data/*/*/*'.format(input_data)

    # read song data file
    song_data_df = spark.read.json(song_data)

    # create partial SQL table
    song_data_df.createOrReplaceTempView("song_table")

    # extract columns to create songs table
    songs_table = spark.sql('''
        SELECT song_id, title, artist_id, year, duration
        FROM song_table
    ''')

    # replace table for later use in join action
    songs_table.createOrReplaceTempView("song_table")

    # extract columns to create artists table
    artists_table = spark.sql('''
        SELECT artist_id, artist_name, artist_location, artist_latitude,
            artist_longitude
        FROM song_table
    ''')

    # write songs table to parquet files partitioned by year and artist
    song_table.write\
        .partitionBy('year', 'artist_id')\
        .parquet('{}songs/song_table.pq'.format(output_data), mode="overwrite")

    # write artists table to parquet files
    artist_table.write\
        .parquet('{}artists/artist_table.pq'.format(output_data), mode="overwrite")

def process_log_data(spark, input_data, output_data):
    '''
    Process log_data, extract user details
    and creates parquet structures for user table,
    timestamp table and songplay table

    Arguments:
    ----------
    spark -- spark session instance\n
    input_data -- origin of data to process\n
    output_data -- destination of processed data
    '''
    # get filepath to log data file
    log_data_filepath = '{}log_data/*.json'.format(input_data)

    # read log data file
    log_data = spark.read.json(log_data_filepath)
    log_data.createOrReplaceTempView("user_log_table")

    # filter by actions for song plays
    log_data = spark.sql('''
        SELECT *
        FROM user_log_table
        WHERE page='NextSong'
    ''')

    # overwrite user_log_table after filtering
    log_data.createOrReplaceTempView("user_log_table")

    # extract columns for users table
    user_data = spark.sql('''
        SELECT DISTINCT userId, firstName, lastName, gender, level
        FROM user_log_table
    ''')

    # write users table to parquet files
    user_data.write\
        .parquet('{}users/user_data.pq'.format(output_data), mode="overwrite")

    # create timestamp column from original timestamp column
    get_timestamp = udf(
        lambda x: datetime.fromtimestamp(x / 1000.0),
        T.TimestampType()
    )
    log_data = log_data.withColumn("timestamp", get_timestamp(log_data.ts))

    # create datetime column from original timestamp column
    get_datetime = udf(
        lambda x: datetime.fromtimestamp(x / 1000.0),
        T.DateType()
    )
    log_data = log_data.dropna(how="any", subset=["ts"])
    log_data = log_data.withColumn("datetime", get_datetime(log_data.ts))

    # extract columns to create time table
    time_table = time_table = (
        log_data
        .withColumn('day', dayofmonth(log_data.datetime))
        .withColumn('week', weekofyear(log_data.datetime))
        .withColumn('month', month(log_data.datetime))
        .withColumn('year', year(log_data.datetime))
        .withColumn('weekday', dayofweek(log_data.datetime))
        .select(['ts', 'day', 'week', 'month', 'year', 'weekday'])
    )

    # write time table to parquet files partitioned by year and month
    time_table.write\
        .partitionBy('year', 'month')\
        .parquet('{}timetable/time_table.pq'.format(output_data), mode="overwrite")

    # read in song data to use for songplays table
    song_df = spark.sql('''
        SELECT *
        FROM song_table
    ''')

    # extract columns from joined song and log datasets to
    # create songplays table
    songplays_table = (
        log_data
        .withColumn('songplay_id', monotonically_increasing_id())
        .join(song_df, log_data.song == song_df.title)
        .select([
            'songplay_id', 'ts', 'userId', 'level', 'song_id',
            'artist_id', 'itemInSession', 'location', 'userAgent'
        ])
    )

    # write songplays table to parquet files partitioned by year and month
    songplays_table.write\
        .parquet('{}songplays/songplays_table.pq'.format(output_data), mode="overwrite")

def main():
    spark = create_spark_session()
    input_data = "s3a://udacity-dend/"
    output_data = "s3a://sparkify-datalake/"

    process_song_data(spark, input_data, output_data)
    process_log_data(spark, input_data, output_data)

if __name__ == "__main__":
    main()


import sys
import pandas as pd

input_file = sys.argv[1]
if not input_file.endswith('.edgelist'):
    raise ValueError('Input file must be an edgelist')

output_file = input_file.replace('.edgelist', '_no_trivial.edgelist')

df = pd.read_csv(input_file, sep='\t', header=None, names=['head', "rel", "tail"])

df = df[~df['tail'].str.contains('owl:Nothing')]
df = df[~df['head'].str.contains('owl:Thing')]
df = df.drop_duplicates().dropna()

df.to_csv(output_file, sep='\t', header=False, index=False)


from bilayer_clusters import jenks
import numpy as np

def clusters_base(arr,k): #array is pre-sorted
    breaks = jenks.jenks(arr,k)
    clusters = [0 for i in range(k)]

    i_start = 0
    i = 0

    for index in breaks[1:-1]:
        i_end = index
        clusters[i] = arr[i_start:i_end+1]
        i += 1
        i_start = i_end+1

    clusters[i] = arr[i_start:]

    return clusters

def clusters(lipids,k): #removed t dependence
    lipids = lipids[lipids[:,3].argsort()] #weird sorting thing from: https://stackoverflow.com/questions/2828059/sorting-arrays-in-numpy-by-column

    breaks = jenks.jenks(lipids[:,3],k)
    clusters = [0 for i in range(k)]

    i_start = 0
    i = 0

    for index in breaks[1:-1]:
        i_end = index
        clusters[i] = np.asarray(lipids[i_start:i_end+1])
        i += 1
        i_start = i_end+1

    clusters[i] = np.asarray(lipids[i_start:])

    return clusters


from model.player import Player
from datetime import datetime

# checks if a player is found in the global ranking
def check_player_global(player_last_name, player_first_name, player_list):
    for player in player_list:
        if player_last_name == player.last_name and player_first_name == player.first_name:
            return True
    return False

# find a player in the global ranking
def find_player_global(player_last_name, player_first_name, player_list):
    for player in player_list:
        if player_last_name == player.last_name and player_first_name == player.first_name:
            return player

# add a player in the global ranking
def add_global_player(player_last_name, player_first_name, player_list):
    for player in player_list:
        if player_last_name == player.last_name and player_first_name == player.first_name:
            return player

# look for a player in the tournament's player list
def find_player_tournament(player_last_name, player_first_name, tournament_list):
    for player_tournament in tournament_list[-1].players:
        if player_last_name == player_tournament.last_name and player_first_name == player_tournament.first_name:
            return True
    return False

# look if 2 player faced each other in previous rounds
def find_player_previous_match(players_match, round_list):
    for i in range(len(round_list)-1):
        for j in range(len(round_list[i].matchs_round)):
            if round_list[i].matchs_round[j] == (players_match[0], players_match[1]) \
                    or round_list[i].matchs_round[j] == (players_match[1], players_match[0]):
                return True
    return False

# add a player in the tournament
def add_player(tournament_list, player_list):
    if len(tournament_list) == 0:
        pass
    # checks if the tournament is full
    elif len(tournament_list[-1].players) == tournament_list[-1].max_players:
        print("Le tournoi est complet.")
        pass
    else:
        # ask if user wants to add a new player
        add_new_player = (input(
            "Voulez-vous ajouter un joueur O/N ?")).capitalize()
        # check the user input
        while add_new_player not in ["O", "N"]:
            add_new_player = (input("Entrer O ou N")).capitalize()
        while add_new_player == "O" and len(tournament_list[-1].players) < tournament_list[-1].max_players:
            last_name = (input("Nom: ")).upper()
            first_name = (input("Prénom: ")).capitalize()
            # if the player is already added to the tournament, player is not added
            if find_player_tournament(last_name, first_name, tournament_list) is True:
                print("Ce joueur est déjà dans le tournoi")
            # if player is found in the global player list, it is added to the tournament
            elif check_player_global(last_name, first_name, player_list) is True:
                tournament_list[-1].players.append(add_global_player(last_name, first_name, player_list))
                print("Le joueur ou la joueuse a été ajouté au tournoi")
            else:
                # check if the input is a date
                while True:
                    try:
                        date = datetime.strptime(input("Date de naissance (JJ/MM/AAAA): "), '%d/%m/%Y')
                        break
                    except ValueError:
                        print("Vous devez entrer une date")
                gender = (input("Genre (M/F): ")).capitalize()
                # check if gender input is correct
                while gender not in ["M", "F"]:
                    print("Veuillez choisir entre M et F")
                    gender = (input())
                # crate a new instance of player and add in the global list and tournament
                player = Player(last_name, first_name, date, gender)
                tournament_list[-1].players.append(player)
                player_list.append(player)
            # prompt if user wants to add a new player
            if len(tournament_list[-1].players) < tournament_list[-1].max_players:
                add_new_player = (input("Voulez-vous ajouter un autre joueur ou une autre joueuse ? O/N")).capitalize()
            else:
                add_new_player = "N"
    return tournament_list

# update the global ranking
def update_players_rank(player_list):
    player_list = sorted(player_list,
                         key=lambda player: player.tournament_points,
                         reverse=True)
    for player in range(len(player_list)):
        if player != 0:
            if player_list[player].tournament_points == player_list[player - 1].tournament_points:
                player_list[player].rank = player_list[player - 1].rank
            else:
                player_list[player].rank = player + 1
        else:
            player_list[player].rank = player + 1

# change the player ranking and tournament points
def change_player_rank(menu_list, player_list):
    player_last_name = (input("Quel est le nom du joueur ou de la joueuse ?")).upper()
    player_first_name = (input("Quel est le prénom du joueur ou de la joueuse ?")).capitalize()
    player = find_player_global(player_last_name, player_first_name, player_list)
    print(f"Le classement de {player.last_name} {player.first_name} "
          f"est {player.rank} et a {player.tournament_points} points")
    choice = (input("Voulez_vous changez ses informations ? O/N")).capitalize()
    if choice == "O":
        # checks the input for int value
        while True:
            try:
                player.rank = int(input("Quel est son nouveau classement ?"))
                # checks if the input is not negative
                if player.rank >= 0:
                    break
                else:
                    print("La valeur doit être positive")
            except ValueError:
                print("Veuillez choisir son nouveau classement")
        # checks if the input is a float number
        while True:
            try:
                player.tournament_points = float(
                    input("Quel est son nouveau nombre de points ?"))
                break
            except ValueError:
                print("Veuillez choisir un nombre de points")
    else:
        pass
    menu_list.pop()
    return menu_list, player_list


#!/usr/bin/env python

"""
Query every dataset (after 2008) in the simprod database to ensure that they
do not raise errors. This does _not_ check whether the result is actually
correct.

Note that this should not be an automated test, since it a) requires access to
the iceprod1 database, and b) puts quite a lot of load on the database.
"""

from icecube.weighting import weighting
import pprint

known_bad = {
	# ice model systematics with missing info in database
	20067, 20057, 20056, 20060,
}

with weighting.simprod_cursor() as cursor:
	cursor.execute("SELECT dataset_id from dataset WHERE dataset_id > 8790 ORDER BY dataset_id DESC")
	all_datasets = [i for i, in cursor.fetchall()]
	for dataset_id in all_datasets:
		try:
			weighting.from_simprod(dataset_id)
		except:
			steering = weighting.get_steering(cursor, dataset_id)
			# nwandkowsky's private simulation is outside the scope
			if 'nancy' in steering.get('TARGET', {}):
				continue
			# someone from IceTop needs to fix IceProd2-style IceTop configs
			if steering.get('category', None) == 'CORSIKA-icetop':
				continue
			# also, IceProd1 IceTop sets are weird and not really supported
			is_icetop = True
			for triggerword in ('jgonzalez', 'tamburro', 'icetop', 'topsimulator'):
				if triggerword in str(steering).lower():
					break
			else:
				is_icetop = False
			if is_icetop:
				continue
			# early iceprod2 datasets not in the iceprod1 db are also out of scope
			if 'MCPE_dataset' in steering:
				parent = int(steering['MCPE_dataset'])
			else:
				parent = steering.get('inputdataset')

			if parent in known_bad or parent not in all_datasets:
				continue
			# IceTop weighting not implemented for anything other than E^-1
			parent_steering = weighting.get_steering(cursor, parent)
			if parent_steering.get('CORSIKA::eslope', -1) != -1:
				continue

			print("Dataset {} is broken".format(dataset_id), parent)
			pprint.pprint(steering)
			pprint.pprint(weighting.get_steering(cursor, parent))
			# cursor.execute("SELECT cparameter_id, type, value FROM cparameter where dataset_id=%s", (dataset_id))
			# print(cursor.fetchall())
			raise
		print("Dataset {} is okay".format(dataset_id))


#!/usr/bin/env python
# -*- coding: utf-8 -*-
import serial,os,sys,time,tty,termios
from pylab import *
import codecs
import codecs

baudrate = 9600
delay = .08

execfile('load_font.py')
execfile('getchar.py')
execfile('simpleserial.py')

gg = _Getch()

ser=startSerial(baudrate)

while(1):
	ch = unicode(gg()	)
	sys.stdout.write(ch)
	sys.stdout.flush()
	if ch == '\x1Bc': # THIS IS BROKEN!
		system.exit(0)
	if ch in mapping:
		index = mapping.index(ch)
		if index >=0 and index < len(chars):
			chardata = chars[index]+[0,];
			for column in chardata:
				ser.write(chr(column))
				time.sleep(delay)

closeSerial(ser)


import numpy as np
import matplotlib.pyplot as plt

np.random.seed(7)

def gen_data():
    mean_1 = [-1, 4]
    cov_1 = [[1, 8], [2, 10]]  # diagonal covariance
    class_1_data = np.random.multivariate_normal(mean_1, cov_1, 1000)

    mean_2 = [2, 1]
    cov_2 = [[3, 10], [0, 1]]  # diagonal covariance
    class_2_data = np.random.multivariate_normal(mean_2, cov_2, 1000)

    return class_1_data, class_2_data

def main():
    class_1_data, class_2_data = gen_data()
    x1, y1 = class_1_data.T
    x2, y2 = class_2_data.T
    plt.scatter(x1, y1, s=7)
    plt.scatter(x2, y2, s=7)
    # plt.axis('equal')
    plt.show()

main()


import sys
import PyPDF2

def verify_pdf(pdfs):
    input = []
    for item in pdfs:
        try:
            with open(item, 'rb') as file:
                reader = PyPDF2.PdfFileReader(file)
            input.append(item)
        except FileNotFoundError:
            print(f'{item} não encontrado')
        except PyPDF2.utils.PdfReadError:
            print(f'{item} não é um PDF ou está corrompido')
        except IsADirectoryError:
            print(f'Não foi possivel acessar {item} pois é um diretório')
    return input

def pdf_merger(pdf_list):
    merger = PyPDF2.PdfFileMerger()
    for pdf in pdf_list:
        merger.append(pdf)
    merger.write('super.pdf')

input = verify_pdf(sys.argv[1:])
pdf_merger(input)


import sys
from io import StringIO
import unittest

class TestClass(unittest.TestCase):
    maxDiff = None
    def assertIO(self, input, output):
        stdout, stdin = sys.stdout, sys.stdin
        sys.stdout, sys.stdin = StringIO(), StringIO(input)
        resolve()
        sys.stdout.seek(0)
        out = sys.stdout.read()[:-1]
        sys.stdout, sys.stdin = stdout, stdin
        self.assertEqual(out, output)

    def test_Sample_Input_1(self):
        input = """WBWBWWBWBWBWWBWBWWBW"""
        output = """Do"""
        self.assertIO(input, output)

    def test_Sample_Input_2(self):
        input = """WBWWBWBWBWWBWBWWBWWB"""
        output = """Re"""
        self.assertIO(input, output)

def resolve():
  # WBWBWWBWBWBW
  # WBWBWWBWBWBWWBWBWWBWBWBW
  # WBWBWWBWBWBW

  ans_dict = ["Do", "Do", "Re", "Re", "Mi", "Fa", "Fa", "So", "So", "La", "La", "Si"]
  n = len(ans_dict)
  S = input()
  index = S.find("WBWWBWBWBW")
  index-=2
  index = index%12
  # print(index)
  print(ans_dict[(n-index)%12])

import sys
if sys.argv[-1] == './Main.py':
  resolve()

if __name__ == "__main__":
  unittest.main()