from transitions import Machine
import random
import itertools
from loggers import dbot_online_logger
from dialogue_state.domain import is_intent_rule
import json
from path_config import  *
## non_branched fsa
class buy_tv_state(object):
    pass

intents = ["buy_tv","inform_size","inform_resolution","inform_brandname"]
states=["start",'how can i help you' ]+[str(n+1)+"after_"+u for n,u in enumerate(intents)]
#print(states)

def get_states_from_transitions(transtions):
    states = []
    for t in transtions:
        states.append(t['source'])
        states.append(t['dest'])
    return list(set(states))

def load_edge2rule_by_taskname(taskname):
    edgename2condition = None
    with open(fsm_edge_rule_config.format(taskname)) as json_file:
        edgename2condition = json.load(json_file)
    return edgename2condition

def load_state2action_by_taskname(taskname):
    with open(fsm_state_action_config.format(taskname)) as json_file:
        state_action_mapper = json.load(json_file)
    return state_action_mapper

def load_transition_by_taskname(taskname):
    with open(fsm_transition_config.format(taskname)) as json_file:
        transition = json.load(json_file).get("transitions")
    return transition

def load_fsm_config_by_taskname(taskname):
    transition = load_transition_by_taskname(taskname)
    edgename2condition = load_edge2rule_by_taskname(taskname)
    state_action_mapper = load_state2action_by_taskname(taskname)
    states = get_states_from_transitions(transition)
    return transition,states,edgename2condition,state_action_mapper

tv_transition = [
    { 'trigger': 'buy_tv', 'source': 'start', 'dest': '1after_buy_tv' },
    { 'trigger': 'inform_size', 'source': '1after_buy_tv', 'dest': '2after_inform_size' },
    { 'trigger': 'inform_resolution', 'source': '2after_inform_size', 'dest': '3after_inform_resolution' },
    { 'trigger': 'inform_brandname', 'source': '3after_inform_resolution', 'dest': '4after_inform_brandname' },
    { 'trigger': 'greet', 'source': 'start', 'dest': 'how can i help you' },
    { 'trigger': 'buy_tv2', 'source': 'how can i help you' , 'dest': '1after_buy_tv' },
]
for i,state in enumerate(states):
    if state == "3after_inform_resolution" or state == "4after_inform_brandname":
        continue
    d = { 'trigger': "all_filled_"+str(i), 'source': state, 'dest': '4after_inform_brandname' }
    tv_transition.append(d)

def ask_next_slot_task_fsm(slots,start_state='start',end_state = 'end',slot_unfilled_reask = True):
    '''
    ask next unfilled slot
    :param slots:
    :param start_state:
    :param end_state:
    :param slot_unfilled_reask:
    :return:
    '''
    transition = []
    states = []
    n = len(slots)
    for i in range(n):
        d = {'trigger': 'any unfilled', 'source': "ask_next_one" + str(i - 1), 'dest': "ask_next_one" + str(i)}
        if i == 0:
            d = {'trigger': 'any unfilled', 'source': start_state , 'dest': "ask_next_one" + str(i)}

        transition.append(d)
        states.append(d['source'])
        states.append(d['dest'])
    for s in states[:]:
        d = {'trigger': 'all filled', 'source': s, 'dest': end_state}
        transition.append(d)
    states.append(end_state)
    states = list(set(states))
    states.sort()
    return transition,states

def ask_next_buy_tv():
    slots =["size", "brandname", "resolution"]
    transition,states = ask_next_slot_task_fsm(["size", "brandname", "resolution"])
    edge2condition = {
        'any unfilled':['any unfilled'],
        'all filled':[slot + " filled" for slot in slots],
    }
    state_action_map = {
        'ask_next_one0': ['action_ask_next_slot'],
        'ask_next_one1': ['action_ask_next_slot'],
        'ask_next_one2': ['action_ask_next_slot'],
        'ask_next_one3': ['action_ask_next_slot'],
        'end': ["utter_tv_summary","utter_send_tv_order"]}
    return states, None, transition, edge2condition, state_action_map

def n_edges_with_affirm_task_fsm(slots):
    filled = "filled"
    filled = "已填充"
    unfilled = "unfilled"
    unfilled = "未填充"
    chinese_affirm = "向用户确认槽位{}的值"
    english_affirm = "ask user whether the slot {} is xxx"
    transition = []
    states = []
    n = len(slots)
    for slot_index,slot in enumerate(slots):
        condition = slot + " "+filled
        if slot_index == n-1:
            dest_state = "finish"
            continue
        else:
            next_slot= slots[slot_index+1]

            dest_state ="ask_" + next_slot
        d = {'trigger': condition+"{} unfilled".format(next_slot), 'source': "ask_" + slot, 'dest': dest_state}
        d2 = {'trigger': slot + " "+filled, 'source': "ask_" + slot, 'dest': english_affirm.format(slot)}
        transition.append(d)
        transition.append(d2)
        states.append(d['source'])
        states.append(d2['source'])
        states.append(d['dest'])
        states.append(d2['dest'])

    states = list(set(states))
    states.sort()
    return transition, states

def n_edges_task_fsm(slots,start_state='start',end_state = 'end',slot_unfilled_reask = True):
    '''
    n edges design
    in one turn,the agent can move more than one node
    :param slots:
    :param start_state:
    :param end_state:
    :param slot_unfilled_reask:if a slot is unfilled,ask again
    :return:
    '''
    transition = []
    states = []
    n = len(slots)
    # slot a to slot b
    for slot_index,slot in enumerate(slots):
        condition = slot + " filled"
        if slot_index == n-1:
            dest_state = "finish"
            continue
        else:
            next_slot= slots[slot_index+1]

            dest_state ="ask_" + next_slot
        d = {'trigger': condition, 'source': "ask_" + slot, 'dest': dest_state}
        transition.append(d)
        states.append(d['source'])
        states.append(d['dest'])
    # check slot is filled or not
    for slot_index, slot in enumerate(slots):
        condition = slot + " unfilled"
        d = {'trigger': condition, 'source': "ask_" + slot, 'dest': "ask_" + slot}
        transition.append(d)
        states.append(d['source'])
        states.append(d['dest'])
    d = {'trigger': "any unfilled or intent is buy tv", 'source': start_state, 'dest': "ask_" + slots[0]}
    states.append(d['source'])
    states.append(d['dest'])
    transition.append(d)
    d = {'trigger': "all filled", 'source': "ask_" + slots[-1], 'dest':end_state }
    states.append(d['source'])
    states.append(d['dest'])

    transition.append(d)
    states = list(set(states))
    states.sort()
    return transition, states

def unfilled_fsm_policy(slots,start_state='start',end_state = 'end',add_start2nodes = True,add_nodes2end = True):
    '''
    n *(n-1) edges design
    :param slots:
    :param start_state:
    :param end_state:
    :param add_start2nodes: unfill one slot then ask
    :param add_nodes2end:  if fill all , then finish
    :return:
    '''
    transition = []
    states = []
    n = len(slots)
    perms = list(itertools.permutations([int(i) for i in range(n)], 2))
    for perm in perms:
        end_i = perm[1]
        miss_slotname = slots[end_i]
        last_miss_slotname = slots[perm[0]]
        condition = miss_slotname + " unfilled"
        d = {'trigger':condition , 'source': "ask_"+last_miss_slotname, 'dest': "ask_"+miss_slotname}
        transition.append(d)
        states.append(d['source'])
        states.append(d['dest'])
    states = list(set(states))
    if add_nodes2end:
        for source_node in states:
            d = {'trigger': "all filled", 'source': source_node, 'dest': end_state}
            transition.append(d)
    if add_start2nodes:
        for end_node in states:
            slot = end_node.split("_")[-1]
            d = {'trigger': slot+" unfilled", 'source': start_state, 'dest': end_node}
            transition.append(d)
    states += [start_state,end_state]
    return transition,states

def edge_rule_maker(slots):
    '''
    unfilled form policy ,
    make the rules for every possible edge
    :param slots:
    :return: dict
    '''
    edge2rules_dict = {}
    for slot in slots:
        edge2rules_dict[slot+" unfilled"] = [slot+" unfilled"]
    edge2rules_dict['all filled'] = [slot + " filled" for slot in slots]
    return edge2rules_dict

def n_edges_rule_maker(slots):
    '''
    unfilled form policy ,
    make the rules for every possible edge
    :param slots:
    :return: dict
    '''
    edge2rules_dict = {}
    for slot in slots:
        edge2rules_dict[slot+" filled"] = [slot+" filled"]
    edge2rules_dict["any unfilled"] = ["any unfilled"]
    edge2rules_dict['all filled'] = [slot + " filled" for slot in slots]
    return edge2rules_dict

def merge_dict(dicts):
    '''
    merge multiple dicts into one dict
    :param dicts: list[dict]
    :return: dict
    '''
    new = dict()
    for d in dicts:
        for k,v in d.items():
            new[k] = v
    return new

def simulate_form_policy(slots):
    transition = []
    states = []
    n = len(slots)
    string = "".join([str(i) for i in range(n)])
    a = list(itertools.permutations(string, n))
    #print(a)
    ##[('0', '1', '2'), ('0', '2', '1'), ('1', '0', '2'), ('1', '2', '0'), ('2', '0', '1'), ('2', '1', '0')]
    for perm in a:
        for i,slot_index_str in enumerate(perm):
            slot_index = int(slot_index_str)
            cur_slot = slots[slot_index]
            source_state = "ask_"
            slot_i_s = list(perm[:i])
            slot_i_s = [int(j) for j in slot_i_s]
            slotnames = [slots[j] for j in slot_i_s]
            #source_state+="".join(list(perm[:i]))
            source_state+="_".join(slotnames)
            if source_state == "ask_":
                dest_state = source_state+ slots[slot_index]
            else:
                dest_state = source_state+"_"+ slots[slot_index]
            states.append(source_state)
            states.append(dest_state)
            d = {'trigger': cur_slot+" unfilled " , 'source': source_state, 'dest':dest_state }
            transition.append(d)
    states = list(set(states))+["end"]
    for perm in a:
        slot_i_s = list(perm[:])
        slot_i_s = [int(j) for j in slot_i_s]
        slot_i_s = [slots[j] for j in slot_i_s]
        slot_i_s_str = "_".join(slot_i_s)
        for s in states:
            if slot_i_s_str in s:
                d = {'trigger': " all filled ", 'source': s, 'dest': "end"}
                transition.append(d)
    for state in states:
        if state!="end":
            d = {'trigger': " all filled ", 'source': state, 'dest': "end"}
            if d not in transition:
                transition.append(d)
    return transition,states

def reminder_data():
    transition ,states = unfilled_fsm_policy(["date","time","event"],end_state="end_add_new_event")
    d = {'trigger': 'intent is ask_reminder', 'source': 'start', 'dest': 'end_search_event'}
    transition.append(d)
    states += ['end_search_event']
    edgename2condition = edge_rule_maker(["date","time","event"])
    edgename2condition['intent is ask_reminder'] = ['intent is ask_reminder']
    state_action_map = {
        'ask_date': ["utter_ask_newevent_date"],
        'ask_time': ['utter_ask_newevent_time'],
        'ask_event': ["utter_ask_event"],
        'end_add_new_event': ["action_set_reminder"],
        'end_search_event':["action_report_event"]
    }
    return states, get_transition_data(transition), transition, edgename2condition, state_action_map

def n_edges_tv_data():
    form_policy, states = n_edges_task_fsm(["size", "brandname", "resolution"])
    edge2condition = n_edges_rule_maker(["size", "brandname", "resolution"])
    state_action_map = {
        'ask_brandname': ["utter_list_brand","utter_ask_brand"],
        'ask_resolution': ['utter_ask_resolution'],
        'ask_size': ["utter_list_size","utter_ask_size"],
        'end': ["utter_tv_summary","utter_send_tv_order"]}
    return states, get_transition_data(form_policy), form_policy, edge2condition, state_action_map

def new_tv_data():
    form_policy, states = unfilled_fsm_policy(["size", "brandname", "resolution"])
    edgename2condition = {'size unfilled': ['size unfilled'],
                          'brandname unfilled': ['brandname unfilled'],
                          'resolution unfilled': ['resolution unfilled'],
                          'all filled': ['size filled', 'brandname filled', 'resolution filled']}
    state_action_map = {
        'ask_brandname': ["utter_list_brand","utter_ask_brand"],
        'ask_resolution': ['utter_ask_resolution'],
        'ask_size': ["utter_list_size","utter_ask_size"],
        'end': ["utter_tv_summary","utter_send_tv_order"]}
    return states, get_transition_data(form_policy), form_policy, edgename2condition, state_action_map

def tv_data():
    form_policy, states = simulate_form_policy(["size", "brand", "resolution"])
    #print({k["trigger"]:[] for k in form_policy})
    edgename2condition = {'size unfilled ': ['size unfilled'],
                          'brand unfilled ': ['brandname unfilled'],
                          'resolution unfilled ': ['resolution unfilled'],
                          ' all filled ': ['size filled','brandname filled','resolution filled']}
    #print({k: [] for k in states})

    state_action_map = {
        'ask_size_brand_resolution': ['utter_ask_resolution'],
        'ask_brand': ["utter_list_brand","utter_ask_brand"],
        'ask_size_brand': ["utter_list_brand","utter_ask_brand"],
        'ask_resolution': ['utter_ask_resolution'],
        'ask_resolution_brand': ["utter_list_brand","utter_ask_brand"],
        'ask_resolution_brand_size': ["utter_list_size","utter_ask_size"],
        'ask_resolution_size': ["utter_list_size","utter_ask_size"],
        'ask_size_resolution_brand': ["utter_list_brand","utter_ask_brand"],
        'ask_brand_resolution': ['utter_ask_resolution'],
        'ask_resolution_size_brand': ["utter_list_brand","utter_ask_brand"],
        'ask_brand_size': ["utter_list_size","utter_ask_size"],
        'ask_size': ["utter_list_size","utter_ask_size"],
        'ask_size_resolution': ['utter_ask_resolution'],
        'ask_brand_resolution_size': ["utter_list_size","utter_ask_size"],
        'ask_brand_size_resolution': ['utter_ask_resolution'],
        'end': ["utter_tv_summary","utter_send_tv_order"]}

    return states,get_transition_data(form_policy) ,form_policy,edgename2condition,state_action_map

#tv_data()
#form_policy ,states = simulate_form_policy(["size","brand","resolution"])
condition2edge_name = dict()

## edge name --> rule set

edgename2condition = {
    "buy_tv":["size unfilled"],
    "greet":["intent is greet"],
    "buy_tv2":["size unfilled"],
    "inform_size":["resolution unfilled"],
    "inform_resolution":["brandname unfilled"],
    "inform_brandname":["size filled","resolution filled","brandname filled"],
}
state_action_map ={
    'how can i help you':["utter_offer_help"],
    "1after_buy_tv":["utter_list_size","utter_ask_size"],
    "2after_inform_size":["utter_receive_size","utter_ask_resolution"],
    "3after_inform_resolution":["utter_receive_resolution","utter_list_brand","utter_ask_brand"],
    "4after_inform_brandname":["utter_receive_brand","utter_tv_summary","utter_send_tv_order"],
}

def export_js_code(transition):
    datas = []
    for row in transition:
        start,end,edge = row["source"],row["dest"],row["trigger"]
        code = "g.setEdge(\""+ start+"\", \""+ end+"\", {label: \""+ edge+"\"});"
        #print(code)
        datas.append(code)
        #print(code)
    return (datas)

def get_transition_data(transition):
    datas = []
    for row in transition:
        start,end,edge = row["source"],row["dest"],row["trigger"]
        code = "g.setEdge(\""+ start+"\", \""+ end+"\", {label: \""+ edge+"\"});"
        data = [start,end,edge]
        datas.append(data)
        #print(code)
    return datas
# export_js_code(form_policy)
# print(states)

def cam_rest_data():
    '''
    开发人员读取预定义  fsm配置文件
    :return:
    '''

    transition = load_transition_by_taskname('camrest')
    states = get_states_from_transitions(transition)
    edgename2condition = load_edge2rule_by_taskname('camrest')
    state_action_map = load_state2action_by_taskname('camrest')
    return states, transition, edgename2condition, state_action_map

def weather_fsm_data():
    '''
    transition ,states,state-utters mapper,
    :return:
    '''
    states = []
    transition =load_transition_by_taskname('weather')

    states = get_states_from_transitions(transition)
    edgename2condition = load_edge2rule_by_taskname('weather')
    state_action_map = load_state2action_by_taskname('weather')

    return states,get_transition_data(transition),transition,edgename2condition,state_action_map
# print("----------------")
# weather_fsm_data()
# print("----------------")
class Fsm():
    def __init__(self,task):
        self.task = task
        states, data, transition, edgename2condition, state_action_map = 0,0,0,0,0
        if self.task == "buy_tv":
            states, data, transition, edgename2condition, state_action_map = ask_next_buy_tv()
            self.start_node = 'start'
        elif self.task == "camrest":
            states, transition, edgename2condition, state_action_map = cam_rest_data()
            self.start_node = 'state_ask_area'
        elif self.task == "weather":
        #states, data, transition, edgename2condition, state_action_map = weather_fsm_data()
            states, data, transition, edgename2condition, state_action_map = weather_fsm_data()
            self.start_node = 'start'
        elif self.task == "reminder":
            states, data, transition, edgename2condition, state_action_map = reminder_data()
            self.start_node = 'start'
        else:
            states, data, transition, edgename2condition, state_action_map = new_tv_data()
        # Initialize
        self.end_node = "4after_inform_brandname"

        self.buy_tv_task = buy_tv_state()
        self.transitions = transition
        self.states = states
        self.machine = Machine(self.buy_tv_task, states=self.states, transitions=self.transitions, initial=self.start_node)
        self.edgename2condition = edgename2condition
        self.state_action_map = state_action_map

    def return_start_node(self):
        self.set_current_state(self.start_node)

    def is_start_node(self):
        return self.current_state()==self.start_node

    def is_end_node(self,node):
        if node in ["1all filled","2all filled","end","end_search_event","end_add_new_event"]:
            return True
        return False

    def current_state(self):
        return self.buy_tv_task.state

    def set_current_state(self,state):
        self.buy_tv_task.state = state

    def is_current_state_end(self):
        return self.is_end_node(self.buy_tv_task.state)
    @staticmethod
    def get_edges_from_starting_state(transitions,start):
        ends = []
        for tran in transitions:
            if tran["source"] == start:
                ends.append(tran["trigger"])
        return  list(set(ends))

    def predict(self,edge_condition):
        if not edge_condition:
            dbot_online_logger.debug("there are not any edges")
            s = self.buy_tv_task.state
            return self.state_action_map.get(s)
        self.buy_tv_task.trigger(edge_condition)
        s = self.buy_tv_task.state
        dbot_online_logger.debug("current state is {}".format(s))
        return self.state_action_map.get(s)

    def get_matched_condition_edge(self,domain):
        if self.is_current_state_end():
            self.return_start_node()
        dest = []
        ends = Fsm.get_edges_from_starting_state(self.transitions,self.buy_tv_task.state)
        print("next step you can reach ",",".join(ends))
        for key in self.edgename2condition.keys():
            if key not in ends:
                #print(key , "not in dests ")
                continue
                #print(key, " in dests ")
            ruleset = self.edgename2condition[key]
            #print(ruleset)
            if domain.rule_set_evaluate(ruleset):
                dest.append(key)
        if len(dest)>1:
            for key in dest:
                ruleset = self.edgename2condition[key]
                for rule in ruleset:
                    if is_intent_rule(rule):
                        return key
            #print("your nlu condition matches {} edges".format(len(dest)))
            return dest[0]
        elif len(dest)==1:
            return dest[0]
        #print("your nlu condition does not match any edge with condition")
        return None

# F = Fsm()
# r = F.machine.get_transitions(source="start")
# print(r)
#
#


import numpy as np
import trimesh
from scipy.spatial import cKDTree as KDTree

def compute_trimesh_chamfer(
    gt_mesh, pred_mesh, offset, scale, num_mesh_samples=30000, verbose=False
):
    """
    This function computes a symmetric chamfer distance, i.e. the sum of both chamfers.

    gt_mesh: trimesh.base.Trimesh of ground truth mesh

    pred_mesh: trimesh.base.Trimesh of output mesh from whichever autoencoding reconstruction
              method (see compute_metrics.py for more)

    """
    if gt_mesh.vertices.shape[0] == 0 or pred_mesh.vertices.shape[0] == 0:
        return np.nan

    pred_points = trimesh.sample.sample_surface(pred_mesh, num_mesh_samples)[0]
    gt_points = trimesh.sample.sample_surface(gt_mesh, num_mesh_samples)[0]

    gt_points = (gt_points - offset) / scale

    # one direction
    pred_points_kd_tree = KDTree(pred_points)
    one_distances, one_vertex_ids = pred_points_kd_tree.query(gt_points)
    gt_to_pred_chamfer = np.mean(np.square(one_distances))

    # other direction
    gt_points_kd_tree = KDTree(gt_points)
    two_distances, two_vertex_ids = gt_points_kd_tree.query(pred_points)
    pred_to_gt_chamfer = np.mean(np.square(two_distances))

    if verbose:
        print(
            gt_to_pred_chamfer + pred_to_gt_chamfer,
            gt_to_pred_chamfer,
            pred_to_gt_chamfer,
        )
    return gt_to_pred_chamfer + pred_to_gt_chamfer


import os
import csv
import numpy as np
from tokenizers import ByteLevelBPETokenizer
from transformers import BartTokenizer, BartForConditionalGeneration, BertTokenizer

path = './custom_pretrain'
os.mkdir(path)
samples = []
with open("../data/raw.csv",'r') as fp:
    reader = csv.reader(fp)
    sample = [row for row in reader]
    for i in sample:
        samples.append(i[1])
        samples.append(i[2])

with open("../data/preliminary_a_test.csv",'r') as fp:
    reader = csv.reader(fp)
    sample = [row for row in reader]
    for i in sample:
        samples.append(i[1])

with open("data.txt", 'w') as f:
    for i in samples:
        f.write(i + '\n')

def build_vocab(vocab_file = './vocab.txt'):
    init_list = [x for x in range(1300)]
    tokenizer = BartTokenizer.from_pretrained('facebook/bart-base')
    all_special_ids, all_special_tokens = zip(*sorted(zip(tokenizer.all_special_ids, tokenizer.all_special_tokens)))
    print(f'insert {all_special_ids} {all_special_tokens}')
    for i in range(len(all_special_ids)):
        init_list.insert(all_special_ids[i], all_special_tokens[i])

    with open(vocab_file, 'w') as fp:
        for i in init_list:
            print(i)
            fp.write(f'{i}\n')

tokenizer = ByteLevelBPETokenizer(lowercase=True, add_prefix_space=True)
tokenizer.train(files='./data.txt',  special_tokens=['<s>', '<pad>', '</s>', '<unk>', '<mask>', '10', '11'])
tokenizer.save_model(path)
print(tokenizer.encode(" 10").ids)
print(tokenizer.encode("10").ids)
print(tokenizer.encode(" 11").ids)
print(tokenizer.encode("11").ids)
print(tokenizer.decode([0,278,2]))
# build_vocab(vocab_file="./vocab.txt")
tokenizer = BartTokenizer.from_pretrained(path)
print(tokenizer.vocab_size)

model = BartForConditionalGeneration.from_pretrained("facebook/bart-base")
# model.encoder.resize_embeddings(tokenizer.vocab_size)
# model.decoder.resize_embeddings(tokenizer.vocab_size)
model.resize_token_embeddings(tokenizer.vocab_size)
model.save_pretrained(path)


from django.core.exceptions import ObjectDoesNotExist

from dsd.models import Alert

def should_send_alert(rule_group_id, org_unit_uid):
    try:
        return Alert.objects.get(rule_group_id=rule_group_id, org_unit_uid=org_unit_uid).should_alert
    except ObjectDoesNotExist:
        update_alert_status(rule_group_id, org_unit_uid, True)
        return True

def update_alert_status(rule_group_id, org_unit_uid, should_alert):
    alerts = Alert.objects.filter(rule_group_id=rule_group_id, org_unit_uid=org_unit_uid)
    amount = alerts.count()

    if 0 == amount:
        Alert(rule_group_id=rule_group_id, org_unit_uid=org_unit_uid, should_alert=should_alert).save()
    elif 1 == amount:
        alert = alerts[0]
        alert.should_alert = should_alert
        alert.save()
    else:
        raise RuntimeError('Should not have more than 1 alert for same organisation unit')


"""TestCase URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.9/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Add an import:  from blog import urls as blog_urls
    2. Import the include() function: from django.conf.urls import url, include
    3. Add a URL to urlpatterns:  url(r'^blog/', include(blog_urls))
"""
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.homepage),
    url(r'^registo/$', views.registo),
	url(r'^login/$', views.login),
	url(r'^autenticacao/$', views.autenticacao),
	url(r'^inicio/$', views.inicio),
	url(r'^logout$', views.logout),
	url(r'^get/(?P<topico_id>\d+)/$', views.topico),
	url(r'^novotopico/$', views.novoTopico),
]


import cv2
import numpy as np
import math
import json
import os

#from config import cfg
from utils.box_overlaps import *
from config import cfg

def process_pointcloud(point_cloud, cfg):
    # Input:
    #   (N, 4)
    # Output:
    #   voxel_dict
    scene_size = np.array(cfg.SCENE_SIZE, dtype=np.float32)
    voxel_size = np.array(cfg.VOXEL_SIZE, dtype=np.float32)
    grid_size = np.array(cfg.GRID_SIZE, dtype=np.int64)
    lidar_coord = np.array(cfg.LIDAR_COORD, dtype=np.float32)
    max_point_number = cfg.MAX_POINT_NUMBER

    if cfg.DETECT_OBJECT != "Car":
        np.random.shuffle(point_cloud)

    shifted_coord = point_cloud[:, :3] + lidar_coord
    # reverse the point cloud coordinate (X, Y, Z) -> (Z, Y, X)
    voxel_index = np.floor(
        shifted_coord[:, ::-1] / voxel_size).astype(np.int)

    bound_x = np.logical_and(
        voxel_index[:, 2] >= 0, voxel_index[:, 2] < grid_size[2])
    bound_y = np.logical_and(
        voxel_index[:, 1] >= 0, voxel_index[:, 1] < grid_size[1])
    bound_z = np.logical_and(
        voxel_index[:, 0] >= 0, voxel_index[:, 0] < grid_size[0])

    bound_box = np.logical_and(np.logical_and(bound_x, bound_y), bound_z)

    point_cloud = point_cloud[bound_box]
    voxel_index = voxel_index[bound_box]

    # [K, 3] coordinate buffer as described in the paper
    coordinate_buffer = np.unique(voxel_index, axis=0)

    K = len(coordinate_buffer)
    T = max_point_number

    # [K, 1] store number of points in each voxel grid
    number_buffer = np.zeros(shape=(K), dtype=np.int64)

    # [K, T, 7] feature buffer as described in the paper
    feature_buffer = np.zeros(shape=(K, T, 7), dtype=np.float32)

    # build a reverse index for coordinate buffer
    index_buffer = {}
    for i in range(K):
        index_buffer[tuple(coordinate_buffer[i])] = i

    for voxel, point in zip(voxel_index, point_cloud):
        index = index_buffer[tuple(voxel)]
        number = number_buffer[index]
        if number < T:
            feature_buffer[index, number, :4] = point
            number_buffer[index] += 1

    feature_buffer[:, :, -3:] = feature_buffer[:, :, :3] - \
        feature_buffer[:, :, :3].sum(axis=1, keepdims=True)/number_buffer.reshape(K, 1, 1)

    voxel_dict = {'feature_buffer': feature_buffer,
                  'coordinate_buffer': coordinate_buffer,
                  'number_buffer': number_buffer}
    return voxel_dict

# transformation matrix converts from sensorA->sensorB to sensorB->sensorA
def inv_trans(T):
    rotation = np.linalg.inv(T[0:3, 0:3])  # rotation matrix

    translation = T[0:3, 3]
    translation = -1 * np.dot(rotation, translation.T)
    translation = np.reshape(translation, (3, 1))
    Q = np.hstack((rotation, translation))

    return Q

def quat_to_rotation(quat):
    # input: (4, 1)
    # output: (4, 4)
    m = np.sum(np.multiply(quat, quat))
    q = quat.copy()
    q = np.array(q)
    n = np.dot(q, q)
    if n < np.finfo(q.dtype).eps:
        rot_matrix = np.identity(4)
        return rot_matrix
    q = q * np.sqrt(2.0 / n)
    q = np.outer(q, q)
    rot_matrix = np.array(
        [[1.0 - q[2, 2] - q[3, 3], q[1, 2] + q[3, 0], q[1, 3] - q[2, 0]],
         [q[1, 2] - q[3, 0], 1.0 - q[1, 1] - q[3, 3], q[2, 3] + q[1, 0]],
         [q[1, 3] + q[2, 0], q[2, 3] - q[1, 0], 1.0 - q[1, 1] - q[2, 2]]],
        dtype=q.dtype)
    rot_matrix = np.transpose(rot_matrix)
    # # test if it is truly a rotation matrix
    # d = np.linalg.det(rotation)
    # t = np.transpose(rotation)
    # o = np.dot(rotation, t)
    return rot_matrix

def qaut_to_angle(quat):
    w=quat[0]
    x=quat[1]
    y=quat[2]
    z=quat[3]

    roll = math.atan2(2*(w*x+y*z),1-2*(x*x+y*y))#the rol is the yaw angle!
    pitch = math.asin(2*(w*y-x*z))
    yaw = math.atan2(2*(w*z+x*y),1-2*(z*z+y*y))
    return roll, pitch, yaw

def angle_to_quat(roll, pitch, yaw):
    cy = math.cos(yaw * 0.5)
    sy = math.sin(yaw * 0.5)
    cp = math.cos(pitch * 0.5)
    sp = math.sin(pitch * 0.5)
    cr = math.cos(roll * 0.5)
    sr = math.sin(roll * 0.5)

    q = np.zeros(4)
    q[0] = cr * cp * cy + sr * sp * sy
    q[1] = sr * cp * cy - cr * sp * sy
    q[2] = cr * sp * cy + sr * cp * sy
    q[3] = cr * cp * sy - sr * sp * cy

    return q

#-- util function to load calib matrices
def load_calib(calib_dir):
    # output: 3 matrix
    with open(calib_dir, mode='r') as f:
        data = json.load(f)
    T_fromLidar = np.array(data['sensors'][1]['calib_data']['T_to_ref_COS'])
    T_fromCamera = np.array(data['sensors'][2]['calib_data']['T_to_ref_COS'])
    K = np.array(data['sensors'][2]['calib_data']['K'])

    T_toLidar = inv_trans(T_fromLidar)
    T_toCamera = inv_trans(T_fromCamera)
    return T_toLidar, T_toCamera, K

def get_class_id(classname):
    classes = {'Bus': 0, 'Car':1, 'Cyclist': 2, 'Motorcyclist': 3, 'Person': 4, 'Trailer':5, 'Truck':6, 'Towed Object': 5, 'Other Vehicle': 5}
    return classes[classname]

def load_label(label_dir):
    # output: [N,11]
    with open(label_dir, mode='r') as f:
        data = json.load(f)
    objects_info = data['objects']
    label = np.empty((len(objects_info), 11))

    for i, p in enumerate(objects_info):
        label[i,:] = np.array([p['center3d'][0], p['center3d'][1], p['center3d'][2],
                              p['dimension3d'][0], p['dimension3d'][1], p['dimension3d'][2],
                              p['orientation_quat'][0], p['orientation_quat'][1], p['orientation_quat'][2], p['orientation_quat'][3],
                              get_class_id(p['classname'])])

    return label

def lidar_to_bird_view(x, y, factor=1):
    # using the cfg.INPUT_XXX
    a = (x - cfg.X_MIN) / cfg.VOXEL_X_SIZE * factor
    b = (y - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor
    a = np.clip(a, a_max=(cfg.X_MAX - cfg.X_MIN) / cfg.VOXEL_X_SIZE * factor, a_min=0)
    b = np.clip(b, a_max=(cfg.Y_MAX - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor, a_min=0)
    return a, b

def batch_lidar_to_bird_view(points, factor=1):
    # Input:
    #   points (N, 2)
    # Outputs:
    #   points (N, 2)
    # using the cfg.INPUT_XXX
    a = (points[:, 0] - cfg.X_MIN) / cfg.VOXEL_X_SIZE * factor
    b = (points[:, 1] - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor
    a = np.clip(a, a_max=(cfg.X_MAX - cfg.X_MIN) / cfg.VOXEL_X_SIZE * factor, a_min=0)
    b = np.clip(b, a_max=(cfg.Y_MAX - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor, a_min=0)
    return np.concatenate([a[:, np.newaxis], b[:, np.newaxis]], axis=-1)

def angle_in_limit(angle):
    # To limit the angle in -pi/2 - pi/2
    limit_degree = 5
    while angle >= np.pi / 2:
        angle -= np.pi
    while angle < -np.pi / 2:
        angle += np.pi
    if abs(angle + np.pi / 2) < limit_degree / 180 * np.pi:
        angle = np.pi / 2
    return angle

def camera_to_lidar(x, y, z, T_VELO_2_CAM=None, R_RECT_0=None):
    if type(T_VELO_2_CAM) == type(None):
        T_VELO_2_CAM = np.array(cfg.MATRIX_T_VELO_2_CAM)

    if type(R_RECT_0) == type(None):
        R_RECT_0 = np.array(cfg.MATRIX_R_RECT_0)

    p = np.array([x, y, z, 1])
    p = np.matmul(np.linalg.inv(R_RECT_0), p)
    p = np.matmul(np.linalg.inv(T_VELO_2_CAM), p)
    p = p[0:3]
    return tuple(p)

def lidar_to_camera(x, y, z, T_VELO_2_CAM=None, R_RECT_0=None):
    if type(T_VELO_2_CAM) == type(None):
        T_VELO_2_CAM = np.array(cfg.MATRIX_T_VELO_2_CAM)

    if type(R_RECT_0) == type(None):
        R_RECT_0 = np.array(cfg.MATRIX_R_RECT_0)

    p = np.array([x, y, z, 1])
    p = np.matmul(T_VELO_2_CAM, p)
    #p = np.matmul(R_RECT_0, p)
    #p = p[0:3]
    return tuple(p)

def camera_to_lidar_point(points, T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 3) -> (N, 3)
    N = points.shape[0]
    points = np.hstack([points, np.ones((N, 1))]).T  # (N,4) -> (4,N)

    if type(T_VELO_2_CAM) == type(None):
        T_VELO_2_CAM = np.array(cfg.MATRIX_T_VELO_2_CAM)

    if type(R_RECT_0) == type(None):
        R_RECT_0 = np.array(cfg.MATRIX_R_RECT_0)

    points = np.matmul(np.linalg.inv(R_RECT_0), points)
    points = np.matmul(np.linalg.inv(T_VELO_2_CAM), points).T  # (4, N) -> (N, 4)
    points = points[:, 0:3]
    return points.reshape(-1, 3)

def lidar_to_camera_point(points, T_VELO_2_CAM=None):
    # (N, 3) -> (3, N)
    N = points.shape[0]
    points = np.hstack([points, np.ones((N, 1))]).T

    points = np.matmul(T_VELO_2_CAM, points)
    #points = points[:, 0:3]
    return points

def camera_to_lidar_box(boxes, T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 7) -> (N, 7) x,y,z,h,w,l,r
    ret = []
    for box in boxes:
        x, y, z, h, w, l, ry = box
        (x, y, z), h, w, l, rz = camera_to_lidar(
            x, y, z, T_VELO_2_CAM, R_RECT_0), h, w, l, -ry - np.pi / 2
        rz = angle_in_limit(rz)
        ret.append([x, y, z, h, w, l, rz])
    return np.array(ret).reshape(-1, 7)

def lidar_to_camera_box(boxes, T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 7) -> (N, 7) x,y,z,h,w,l,r
    ret = []
    for box in boxes:
        x, y, z, h, w, l, rz = box
        (x, y, z), h, w, l, ry = lidar_to_camera(
            x, y, z, T_VELO_2_CAM, R_RECT_0), h, w, l, -rz - np.pi / 2
        ry = angle_in_limit(ry)
        ret.append([x, y, z, h, w, l, ry])
    return np.array(ret).reshape(-1, 7)

def center_to_corner_box2d(boxes_center, coordinate='lidar', T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 5) -> (N, 4, 2)
    N = boxes_center.shape[0]
    boxes3d_center = np.zeros((N, 7))
    boxes3d_center[:, [0, 1, 4, 5, 6]] = boxes_center
    boxes3d_corner = center_to_corner_box3d(boxes3d_center, coordinate=coordinate, T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)

    return boxes3d_corner[:, 0:4, 0:2]

def center_to_corner_box3d(boxes_center, coordinate='lidar', T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 10) -> (N, 8, 3)
    # (N, 7) -> (N, 8, 3)

    N = boxes_center.shape[0]
    ret = np.zeros((N, 8, 3), dtype=np.float32)

    if coordinate == 'camera':
        boxes_center = camera_to_lidar_box(boxes_center, T_VELO_2_CAM, R_RECT_0)

    for i in range(N):
        box = boxes_center[i]
        translation = box[0:3]
        size = box[3:6]

        w, l, h = size[0], size[1], size[2]
        trackletBox = np.array([
            [w / 2, -w / 2, -w / 2, w / 2, w / 2, -w / 2, -w / 2, w / 2],\
            [l / 2, l / 2, -l / 2, -l / 2, l / 2, l / 2, -l / 2, -l / 2],\
            [h / 2, h / 2, h / 2, h / 2, -h / 2, -h / 2, -h / 2, -h / 2]])
        # rotate and translate 3d bounding box
        if box.shape[0] == 10:
            quaternion = box[6:10]
            rotMat = quat_to_rotation(quaternion)
        else:
            yaw = box[6]
            rotMat = np.array([
              [np.cos(yaw), -np.sin(yaw), 0.0],
              [np.sin(yaw), np.cos(yaw), 0.0],
              [0.0, 0.0, 1.0]])

        cornerPosInVelo = np.dot(rotMat, trackletBox) + np.tile(translation, (8, 1)).T
        box3d = cornerPosInVelo.transpose()
        ret[i] = box3d

    #print(f'bounding box:{ret[0]}')
    # for idx in range(len(ret)):
    #     ret[idx] = lidar_to_camera_point(ret[idx], T_VELO_2_CAM)

    return ret

def corner_to_center_box2d(boxes_corner, coordinate='lidar', T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 4, 2) -> (N, 5)  x,y,w,l,r
    N = boxes_corner.shape[0]
    boxes3d_corner = np.zeros((N, 8, 3))
    boxes3d_corner[:, 0:4, 0:2] = boxes_corner
    boxes3d_corner[:, 4:8, 0:2] = boxes_corner
    boxes3d_center = corner_to_center_box3d(boxes3d_corner, coordinate=coordinate, T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)

    return boxes3d_center[:, [0, 1, 4, 5, 6]]

def corner_to_standup_box2d(boxes_corner):
    # (N, 4, 2) -> (N, 4) x1, y1, x2, y2
    N = boxes_corner.shape[0]
    standup_boxes2d = np.zeros((N, 4))
    standup_boxes2d[:, 0] = np.min(boxes_corner[:, :, 0], axis=1)
    standup_boxes2d[:, 1] = np.min(boxes_corner[:, :, 1], axis=1)
    standup_boxes2d[:, 2] = np.max(boxes_corner[:, :, 0], axis=1)
    standup_boxes2d[:, 3] = np.max(boxes_corner[:, :, 1], axis=1)

    return standup_boxes2d

# TODO: 0/90 may be not correct
def anchor_to_standup_box2d(anchors):
    # (N, 4) -> (N, 4) x,y,w,l -> x1,y1,x2,y2
    anchor_standup = np.zeros_like(anchors)
    # r == 0
    anchor_standup[::2, 0] = anchors[::2, 0] - anchors[::2, 3] / 2
    anchor_standup[::2, 1] = anchors[::2, 1] - anchors[::2, 2] / 2
    anchor_standup[::2, 2] = anchors[::2, 0] + anchors[::2, 3] / 2
    anchor_standup[::2, 3] = anchors[::2, 1] + anchors[::2, 2] / 2
    # r == pi/2
    anchor_standup[1::2, 0] = anchors[1::2, 0] - anchors[1::2, 2] / 2
    anchor_standup[1::2, 1] = anchors[1::2, 1] - anchors[1::2, 3] / 2
    anchor_standup[1::2, 2] = anchors[1::2, 0] + anchors[1::2, 2] / 2
    anchor_standup[1::2, 3] = anchors[1::2, 1] + anchors[1::2, 3] / 2

    return anchor_standup

def corner_to_center_box3d(boxes_corner, coordinate='camera', T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 8, 3) -> (N, 10) x,y,z,h,w,l,q0-q3

    # if coordinate == 'lidar':
    #     for idx in range(len(boxes_corner)):
    #         boxes_corner[idx] = lidar_to_camera_point(boxes_corner[idx], T_VELO_2_CAM, R_RECT_0)
    ret = []
    for roi in boxes_corner:
        if cfg.CORNER2CENTER_AVG:  # average version
            roi = np.array(roi)
            h = abs(np.sum(roi[:4, 1] - roi[4:, 1]) / 4)
            w = np.sum(
                np.sqrt(np.sum((roi[0, [0, 2]] - roi[3, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[1, [0, 2]] - roi[2, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[4, [0, 2]] - roi[7, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[5, [0, 2]] - roi[6, [0, 2]])**2))
            ) / 4
            l = np.sum(
                np.sqrt(np.sum((roi[0, [0, 2]] - roi[1, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[2, [0, 2]] - roi[3, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[4, [0, 2]] - roi[5, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[6, [0, 2]] - roi[7, [0, 2]])**2))
            ) / 4
            x = np.sum(roi[:, 0], axis=0)/ 8
            y = np.sum(roi[0:4, 1], axis=0)/ 4
            z = np.sum(roi[:, 2], axis=0)/ 8
            ry = np.sum(
                math.atan2(roi[2, 0] - roi[1, 0], roi[2, 2] - roi[1, 2]) +
                math.atan2(roi[6, 0] - roi[5, 0], roi[6, 2] - roi[5, 2]) +
                math.atan2(roi[3, 0] - roi[0, 0], roi[3, 2] - roi[0, 2]) +
                math.atan2(roi[7, 0] - roi[4, 0], roi[7, 2] - roi[4, 2]) +
                math.atan2(roi[0, 2] - roi[1, 2], roi[1, 0] - roi[0, 0]) +
                math.atan2(roi[4, 2] - roi[5, 2], roi[5, 0] - roi[4, 0]) +
                math.atan2(roi[3, 2] - roi[2, 2], roi[2, 0] - roi[3, 0]) +
                math.atan2(roi[7, 2] - roi[6, 2], roi[6, 0] - roi[7, 0])
            ) / 8
            if w > l:
                w, l = l, w
                ry = angle_in_limit(ry + np.pi / 2)
        else:  # max version
            h = max(abs(roi[:4, 1] - roi[4:, 1]))
            w = np.max(
                np.sqrt(np.sum((roi[0, [0, 2]] - roi[3, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[1, [0, 2]] - roi[2, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[4, [0, 2]] - roi[7, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[5, [0, 2]] - roi[6, [0, 2]])**2))
            )
            l = np.max(
                np.sqrt(np.sum((roi[0, [0, 2]] - roi[1, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[2, [0, 2]] - roi[3, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[4, [0, 2]] - roi[5, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[6, [0, 2]] - roi[7, [0, 2]])**2))
            )
            x = np.sum(roi[:, 0], axis=0)/ 8
            y = np.sum(roi[0:4, 1], axis=0)/ 4
            z = np.sum(roi[:, 2], axis=0)/ 8
            ry = np.sum(
                math.atan2(roi[2, 0] - roi[1, 0], roi[2, 2] - roi[1, 2]) +
                math.atan2(roi[6, 0] - roi[5, 0], roi[6, 2] - roi[5, 2]) +
                math.atan2(roi[3, 0] - roi[0, 0], roi[3, 2] - roi[0, 2]) +
                math.atan2(roi[7, 0] - roi[4, 0], roi[7, 2] - roi[4, 2]) +
                math.atan2(roi[0, 2] - roi[1, 2], roi[1, 0] - roi[0, 0]) +
                math.atan2(roi[4, 2] - roi[5, 2], roi[5, 0] - roi[4, 0]) +
                math.atan2(roi[3, 2] - roi[2, 2], roi[2, 0] - roi[3, 0]) +
                math.atan2(roi[7, 2] - roi[6, 2], roi[6, 0] - roi[7, 0])
            ) / 8
            if w > l:
                w, l = l, w
                ry = angle_in_limit(ry + np.pi / 2)
        ret.append([x, y, z, h, w, l, ry])
    if coordinate == 'lidar':
        ret = camera_to_lidar_box(np.array(ret), T_VELO_2_CAM, R_RECT_0)

    return np.array(ret)

# this just for visualize and testing
def lidar_box3d_to_camera_box(boxes3d, cal_projection=False, P2 = None, T_VELO_2_CAM=None):
    # (N, 10) or (N,7) -> (N, 4)/(N, 8, 2)  x,y,z,h,w,l,q0-q3 -> x1,y1,x2,y2/8*(x, y)
    num = len(boxes3d)
    boxes2d = np.zeros((num, 4), dtype=np.int32)
    projections = np.zeros((num, 8, 2), dtype=np.float32)

    lidar_boxes3d_corner = center_to_corner_box3d(boxes3d, coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM)
    for n in range(num):
        box3d = lidar_boxes3d_corner[n]
        box3d = lidar_to_camera_point(box3d, T_VELO_2_CAM)
        #points = np.hstack((box3d, np.ones((8, 1)))).T  # (8, 4) -> (4, 8)
        points = np.matmul(P2, box3d).T
        points[:, 0] /= points[:, 2]
        points[:, 1] /= points[:, 2]

        projections[n] = points[:, 0:2]
        minx = int(np.min(points[:, 0]))
        maxx = int(np.max(points[:, 0]))
        miny = int(np.min(points[:, 1]))
        maxy = int(np.max(points[:, 1]))

        boxes2d[n, :] = minx, miny, maxx, maxy

    return projections if cal_projection else boxes2d

def lidar_to_bird_view_img(lidar, factor=1):
    # Input:
    #   lidar: (N', 4)
    # Output:
    #   birdview: (w, l, 3)
    birdview = np.zeros(
        (cfg.INPUT_HEIGHT * factor, cfg.INPUT_WIDTH * factor, 1))
    for point in lidar:
        x, y = point[0:2]
        if cfg.X_MIN < x < cfg.X_MAX and cfg.Y_MIN < y < cfg.Y_MAX:
            x, y = int((x - cfg.X_MIN) / cfg.VOXEL_X_SIZE *
                       factor), int((y - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor)
            birdview[y, x] += 1
    birdview = birdview - np.min(birdview)
    divisor = np.max(birdview) - np.min(birdview)
    # TODO: adjust this factor
    birdview = np.clip((birdview / divisor * 255) *
                       5 * factor, a_min=0, a_max=255)
    birdview = np.tile(birdview, 3).astype(np.uint8)

    return birdview

def draw_lidar_box3d_on_image(img, boxes3d, scores, gt_boxes3d=np.array([]),
                              color=(0, 255, 255), gt_color=(255, 0, 255), thickness=1, P2 = None, T_VELO_2_CAM=None):
    # Input:
    #   img: (h, w, 3)
    #   boxes3d (N, 7) [x, y, z, h, w, l, r]
    #   scores
    #   gt_boxes3d (N, 10) [x, y, z, h, w, l, q0-q3]
    img = img.copy()
    #print(f'predicted boxes3d:{boxes3d.shape},ground truth boxes3d:{gt_boxes3d.shape}')
    projections = lidar_box3d_to_camera_box(boxes3d, cal_projection=True, P2=P2, T_VELO_2_CAM=T_VELO_2_CAM)
    #print('begin to draw ground truth box.')
    gt_projections = lidar_box3d_to_camera_box(gt_boxes3d, cal_projection=True, P2=P2, T_VELO_2_CAM=T_VELO_2_CAM)
    #print(f'gt_projections:{gt_projections[0]}')
    # draw projections
    for qs in projections:
        for k in range(0, 4):
            i, j = k, (k + 1) % 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), color, thickness, cv2.LINE_AA)

            i, j = k + 4, (k + 1) % 4 + 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), color, thickness, cv2.LINE_AA)

            i, j = k, k + 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), color, thickness, cv2.LINE_AA)
    # draw gt projections
    for qs in gt_projections:
        for k in range(0, 4):
            i, j = k, (k + 1) % 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), gt_color, thickness, cv2.LINE_AA)

            i, j = k + 4, (k + 1) % 4 + 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), gt_color, thickness, cv2.LINE_AA)

            i, j = k, k + 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), gt_color, thickness, cv2.LINE_AA)

    return cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_BGR2RGB)

def draw_lidar_box3d_on_birdview(birdview, boxes3d, scores, gt_boxes3d=np.array([]),
                                 color=(0, 255, 255), gt_color=(255, 0, 255), thickness=1, factor=1, P2 = None, T_VELO_2_CAM=None, R_RECT_0=None):
    # Input:
    #   birdview: (h, w, 3)
    #   boxes3d (N, 7) [x, y, z, h, w, l, r]
    #   scores
    #   gt_boxes3d (N, 7) [x, y, z, h, w, l, r]
    img = birdview.copy()
    corner_boxes3d = center_to_corner_box3d(boxes3d, coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)
    corner_gt_boxes3d = center_to_corner_box3d(gt_boxes3d, coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)
    # draw gt
    for box in corner_gt_boxes3d:
        x0, y0 = lidar_to_bird_view(*box[0, 0:2], factor=factor)
        x1, y1 = lidar_to_bird_view(*box[1, 0:2], factor=factor)
        x2, y2 = lidar_to_bird_view(*box[2, 0:2], factor=factor)
        x3, y3 = lidar_to_bird_view(*box[3, 0:2], factor=factor)

        cv2.line(img, (int(x0), int(y0)), (int(x1), int(y1)),
                 gt_color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x1), int(y1)), (int(x2), int(y2)),
                 gt_color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x2), int(y2)), (int(x3), int(y3)),
                 gt_color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x3), int(y3)), (int(x0), int(y0)),
                 gt_color, thickness, cv2.LINE_AA)

    # draw detections
    for box in corner_boxes3d:
        x0, y0 = lidar_to_bird_view(*box[0, 0:2], factor=factor)
        x1, y1 = lidar_to_bird_view(*box[1, 0:2], factor=factor)
        x2, y2 = lidar_to_bird_view(*box[2, 0:2], factor=factor)
        x3, y3 = lidar_to_bird_view(*box[3, 0:2], factor=factor)

        cv2.line(img, (int(x0), int(y0)), (int(x1), int(y1)),
                 color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x1), int(y1)), (int(x2), int(y2)),
                 color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x2), int(y2)), (int(x3), int(y3)),
                 color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x3), int(y3)), (int(x0), int(y0)),
                 color, thickness, cv2.LINE_AA)

    return cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_BGR2RGB)

def label_to_gt_box3d(labels, cls='Car'):
    # Input:
    #   label: (N, N',11)
    #   cls: 'Car' or 'Pedestrain' or 'Cyclist'
    #   coordinate: 'camera' or 'lidar'
    # Output:
    #   (N, N', 10)
    #print(f'raw labels:{labels.shape}')
    boxes3d = []
    if cls == 'Car':
        acc_cls = ['Car']
    elif cls == 'Pedestrian':
        acc_cls = ['Pedestrian']
    elif cls == 'Cyclist':
        acc_cls = ['Cyclist']
    else: # all
        acc_cls = []

    acc_cls = [get_class_id(x) for x in acc_cls]

    for label in labels:
        boxes3d_a_label = []
        for row in label:
            #print(f'row:{row}')
            if row[10] in acc_cls or acc_cls == []:
                box3d = row[0:10]
                #print(f'boxe3d:{box3d.shape}')
                boxes3d_a_label.append(box3d)

        boxes3d.append(np.array(boxes3d_a_label).reshape(-1, 10))

    #print(f'through label to gt box3d:{boxes3d}')
    return boxes3d

def box3d_to_label(tag, batch_box3d, batch_cls, batch_score=[], coordinate='camera', P2 = None, T_VELO_2_CAM=None, R_RECT_0=None):
    # Input:
    #   (N, N', 7) x y z h w l r
    #   (N, N')
    #   cls: (N, N') 'Car' or 'Pedestrain' or 'Cyclist'
    #   coordinate(input): 'camera' or 'lidar'
    # Output:
    #   label: (N, N') N batches and N lines
    batch_label = []
    calib_dir = "{}/{}/calibration".format(cfg.DATA_DIR, 'validation')
    _, Tr, P = load_calib(os.path.join(calib_dir, tag + '.json'))
    R_RECT_0 = cfg.MATRIX_R_RECT_0

    if batch_score:
        template = '{} ' + ' '.join(['{:.4f}' for i in range(15)]) + '\n'
        for boxes, scores, clses in zip(batch_box3d, batch_score, batch_cls):
            label = []
            for box, score, cls in zip(boxes, scores, clses):
                if coordinate == 'camera':
                    box3d = box
                    box2d = lidar_box3d_to_camera_box(
                        camera_to_lidar_box(box[np.newaxis, :].astype(np.float32), T_VELO_2_CAM, R_RECT_0), cal_projection=False, P2=P2, T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)[0]
                else:
                    box3d = lidar_to_camera_box(
                        box[np.newaxis, :].astype(np.float32), Tr, R_RECT_0)[0]
                    box2d = lidar_box3d_to_camera_box(
                        box[np.newaxis, :].astype(np.float32), cal_projection=False, P2=P, T_VELO_2_CAM=Tr)[0]
                x, y, z, h, w, l, r = box3d
                box3d = [h, w, l, x, y, z, r]
                label.append(template.format(cls, 0, 0, 0, *box2d, *box3d, float(score)))
            batch_label.append(label)
    else:
        template = '{} ' + ' '.join(['{:.4f}' for i in range(14)]) + '\n'
        for boxes, clses in zip(batch_box3d, batch_cls):
            label = []
            for box, cls in zip(boxes, clses):
                if coordinate == 'camera':
                    box3d = box
                    box2d = lidar_box3d_to_camera_box(
                        camera_to_lidar_box(box[np.newaxis, :].astype(np.float32), T_VELO_2_CAM, R_RECT_0), cal_projection=False,  P2=P2, T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)[0]
                else:
                    box3d = lidar_to_camera_box(
                        box[np.newaxis, :].astype(np.float32), T_VELO_2_CAM, R_RECT_0)[0]
                    box2d = lidar_box3d_to_camera_box(
                        box[np.newaxis, :].astype(np.float32), cal_projection=False, P2=P2, T_VELO_2_CAM=T_VELO_2_CAM)[0]
                # x, y, z, h, w, l, r = box3d
                # box3d = [h, w, l, x, y, z, r]
                # label.append(template.format(cls, 0, 0, 0, *box2d, *box3d))
                label = np.concatenate([box3d, cls], axis=-1)
                #print(f'label in data aug: {label.shape}')
            batch_label.append(label)

    return np.array(batch_label)

def cal_anchors(cfg):
    # Output:
    #   anchors: (w, l, 2, 7) x y z h w l q0 q1 q2 q3
    x = np.linspace(cfg.X_MIN, cfg.X_MAX, cfg.FEATURE_WIDTH)
    y = np.linspace(cfg.Y_MIN, cfg.Y_MAX, cfg.FEATURE_HEIGHT)
    cx, cy = np.meshgrid(x, y)
    # all is (w, l, 2)cal_anchors
    cx = np.tile(cx[..., np.newaxis], 2)
    cy = np.tile(cy[..., np.newaxis], 2)
    cz = np.ones_like(cx) * cfg.ANCHOR_Z
    w = np.ones_like(cx) * cfg.ANCHOR_W
    l = np.ones_like(cx) * cfg.ANCHOR_L
    h = np.ones_like(cx) * cfg.ANCHOR_H
    r = np.ones_like(cx)
    r[..., 0] = 0  # 0
    r[..., 1] = 90 / 180 * np.pi  # 90

    # 7*(w,l,2) -> (w, l, 2, 7)
    anchors = np.stack([cx, cy, cz, h, w, l, r], axis=-1)

    return anchors

def quat_to_mat(quat):
    q = quat.copy()
    q=np.array(q)
    n = np.dot(q, q)
    if n < np.finfo(q.dtype).eps:
        rot_matrix=np.identity(4)
        return rot_matrix
    q = q * np.sqrt(2.0 / n)
    q = np.outer(q, q)
    rot_matrix = np.array(
        [[1.0 - q[2, 2] - q[3, 3], q[1, 2] + q[3, 0], q[1, 3] - q[2, 0]],
         [q[1, 2] - q[3, 0], 1.0 - q[1, 1] - q[3, 3], q[2, 3] + q[1, 0]],
         [q[1, 3] + q[2, 0], q[2, 3] - q[1, 0], 1.0 - q[1, 1] - q[2, 2]]],
        dtype=q.dtype)
    return rot_matrix

def mat_to_ang(R):
    q = np.zeros(4)
    K = np.zeros([4, 4])
    K[0, 0] = 1 / 3 * (R[0, 0] - R[1, 1] - R[2, 2])
    K[0, 1] = 1 / 3 * (R[1, 0] + R[0, 1])
    K[0, 2] = 1 / 3 * (R[2, 0] + R[0, 2])
    K[0, 3] = 1 / 3 * (R[1, 2] - R[2, 1])
    K[1, 0] = 1 / 3 * (R[1, 0] + R[0, 1])
    K[1, 1] = 1 / 3 * (R[1, 1] - R[0, 0] - R[2, 2])
    K[1, 2] = 1 / 3 * (R[2, 1] + R[1, 2])
    K[1, 3] = 1 / 3 * (R[2, 0] - R[0, 2])
    K[2, 0] = 1 / 3 * (R[2, 0] + R[0, 2])
    K[2, 1] = 1 / 3 * (R[2, 1] + R[1, 2])
    K[2, 2] = 1 / 3 * (R[2, 2] - R[0, 0] - R[1, 1])
    K[2, 3] = 1 / 3 * (R[0, 1] - R[1, 0])
    K[3, 0] = 1 / 3 * (R[1, 2] - R[2, 1])
    K[3, 1] = 1 / 3 * (R[2, 0] - R[0, 2])
    K[3, 2] = 1 / 3 * (R[0, 1] - R[1, 0])
    K[3, 3] = 1 / 3 * (R[0, 0] + R[1, 1] + R[2, 2])
    D, V = np.linalg.eig(K)
    pp = 0
    for i in range(1, 4):
        if (D[i] > D[pp]):
            pp = i
    q = V[:, pp]
    x = q[3]
    y = q[0]
    z = q[1]
    w = q[2]
    rol = math.atan2(2 * (w * x + y * z), 1 - 2 * (x * x + y * y))  # the rol is the yaw angle!
    # pith = math.asin(2*(w*y-z*z))
    # yaw = math.atan2(2*(w*z+x*y),1-2*(z*z+y*y))

    return rol

def gt_boxes3d_to_yaw(batch_boxes, T_VELO_2_CAM):
    # Input: (N, N', 10)
    # Output: (N, N', 7)
    #print(f'prepare to convert into yaw:{len(batch_boxes)}')
    batch_boxes_yaw = []
    for boxes in batch_boxes:
        boxes_yaw = []
        #print(f'boxes:{boxes}')
        for box in boxes:
            #print(f'box:{box.shape},{box}')
            center_point = np.insert(box[0:3], 3, values=1)
            #print(f'center point shape:{center_point.shape},{center_point}')
            center_point = np.matmul(T_VELO_2_CAM, center_point)
            #(f'center point shape:{center_point.shape}')

            quaternion = box[6:10]
            #print(f'quaternion: {quaternion.shape}, {quaternion}')
            rotation_mat = quat_to_mat(quaternion)
            rotation_mat = np.matmul(T_VELO_2_CAM[:,:3], rotation_mat)
            yaw = mat_to_ang(rotation_mat)

            box_yaw = np.hstack((center_point, box[3:6], yaw))
            #print(f'len of new box in yaw:{box_yaw.shape}')
            boxes_yaw.append(box_yaw)

        #print(f'boxes:{len(boxes_yaw)}')
        batch_boxes_yaw.append(np.array(boxes_yaw).reshape(-1, 7))

    #print(f'result batch boxes in yaw:{len(batch_boxes_yaw)}')

    return batch_boxes_yaw

def gt_boxes3d_to_yaw(batch_boxes):
    # Input: (N, N', 10)
    # Output: (N, N', 7)
    #print(f'prepare to convert into yaw:{len(batch_boxes)}')
    batch_boxes_yaw = []
    for boxes in batch_boxes:
        boxes_yaw = []
        #print(f'boxes:{boxes}')
        for box in boxes:
            #print(f'box:{box.shape},{box}')

            quaternion = box[6:10]
            #print(f'quaternion: {quaternion.shape}')
            _, _, yaw = qaut_to_angle(quaternion)

            box_yaw = np.hstack((box[0:6], yaw))
            #print(f'len of new box in yaw:{box_yaw.shape}')
            boxes_yaw.append(box_yaw)

        #print(f'boxes:{len(boxes_yaw)}')
        batch_boxes_yaw.append(np.array(boxes_yaw).reshape(-1, 7))

    #print(f'result batch boxes in yaw:{len(batch_boxes_yaw)}')

    return batch_boxes_yaw

def cal_rpn_target(labels, feature_map_shape, anchors, cls='Car', coordinate='lidar'):
    # Input:
    #   labels: (N, N')
    #   feature_map_shape: (w, l)
    #   anchors: (w, l, 2, 7)
    # Output:
    #   pos_equal_one (N, w, l, 2)
    #   neg_equal_one (N, w, l, 2)
    #   targets (N, w, l, 14)
    # attention: cal IoU on birdview
    batch_size = labels.shape[0]
    batch_gt_boxes3d = label_to_gt_box3d(labels, cls=cls)

    # projection gt_boxes3d from 10 dimension to 7 dimension (x y z h w l q0-3 -> x y z h w l r)
    batch_gt_boxes3d = gt_boxes3d_to_yaw(batch_gt_boxes3d)
    #print('finish converting label box to xy plane.')
    # defined in eq(1) in 2.2
    anchors_reshaped = anchors.reshape(-1, 7)
    anchors_d = np.sqrt(anchors_reshaped[:, 4]**2 + anchors_reshaped[:, 5]**2)
    pos_equal_one = np.zeros((batch_size, *feature_map_shape, 2), dtype=np.float32)
    neg_equal_one = np.zeros((batch_size, *feature_map_shape, 2), dtype=np.float32)
    targets = np.zeros((batch_size, *feature_map_shape, 14), dtype=np.float32)

    for batch_id in range(batch_size):
        # BOTTLENECK
        anchors_standup_2d = anchor_to_standup_box2d(
            anchors_reshaped[:, [0, 1, 4, 5]])
        # BOTTLENECK
        gt_standup_2d = corner_to_standup_box2d(center_to_corner_box2d(
            batch_gt_boxes3d[batch_id][:, [0, 1, 4, 5, 6]], coordinate=coordinate))

        iou = bbox_overlaps(
            np.ascontiguousarray(anchors_standup_2d).astype(np.float32),
            np.ascontiguousarray(gt_standup_2d).astype(np.float32),
        )
        # iou = cal_box3d_iou(
        #     anchors_reshaped,
        #     batch_gt_boxes3d[batch_id]
        # )

        # find anchor with highest iou(iou should also > 0)
        id_highest = np.argmax(iou.T, axis=1)
        id_highest_gt = np.arange(iou.T.shape[0])
        mask = iou.T[id_highest_gt, id_highest] > 0
        id_highest, id_highest_gt = id_highest[mask], id_highest_gt[mask]

        # find anchor iou > cfg.XXX_POS_IOU
        id_pos, id_pos_gt = np.where(iou > cfg.RPN_POS_IOU)

        # find anchor iou < cfg.XXX_NEG_IOU
        id_neg = np.where(np.sum(iou < cfg.RPN_NEG_IOU,
                                 axis=1) == iou.shape[1])[0]

        id_pos = np.concatenate([id_pos, id_highest])
        id_pos_gt = np.concatenate([id_pos_gt, id_highest_gt])

        # TODO: uniquify the array in a more scientific way
        id_pos, index = np.unique(id_pos, return_index=True)
        id_pos_gt = id_pos_gt[index]
        id_neg.sort()

        # cal the target and set the equal one
        index_x, index_y, index_z = np.unravel_index(
            id_pos, (*feature_map_shape, 2))
        pos_equal_one[batch_id, index_x, index_y, index_z] = 1

        # ATTENTION: index_z should be np.array
        targets[batch_id, index_x, index_y, np.array(index_z) * 7] = (
            batch_gt_boxes3d[batch_id][id_pos_gt, 0] - anchors_reshaped[id_pos, 0]) / anchors_d[id_pos]
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 1] = (
            batch_gt_boxes3d[batch_id][id_pos_gt, 1] - anchors_reshaped[id_pos, 1]) / anchors_d[id_pos]
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 2] = (
            batch_gt_boxes3d[batch_id][id_pos_gt, 2] - anchors_reshaped[id_pos, 2]) / cfg.ANCHOR_H
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 3] = np.log(
            batch_gt_boxes3d[batch_id][id_pos_gt, 3] / anchors_reshaped[id_pos, 3])
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 4] = np.log(
            batch_gt_boxes3d[batch_id][id_pos_gt, 4] / anchors_reshaped[id_pos, 4])
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 5] = np.log(
            batch_gt_boxes3d[batch_id][id_pos_gt, 5] / anchors_reshaped[id_pos, 5])
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 6] = (
            batch_gt_boxes3d[batch_id][id_pos_gt, 6] - anchors_reshaped[id_pos, 6])

        index_x, index_y, index_z = np.unravel_index(
            id_neg, (*feature_map_shape, 2))
        neg_equal_one[batch_id, index_x, index_y, index_z] = 1
        # to avoid a box be pos/neg in the same time
        index_x, index_y, index_z = np.unravel_index(
            id_highest, (*feature_map_shape, 2))
        neg_equal_one[batch_id, index_x, index_y, index_z] = 0

    return pos_equal_one, neg_equal_one, targets

# BOTTLENECK
def delta_to_boxes3d(deltas, anchors, coordinate='lidar'):
    # Input:
    #   deltas: (N, w, l, 14)
    #   feature_map_shape: (w, l)
    #   anchors: (w, l, 2, 7)

    # Ouput:
    #   boxes3d: (N, w*l*2, 7)
    anchors_reshaped = anchors.reshape(-1, 7)
    deltas = deltas.reshape(deltas.shape[0], -1, 7)
    anchors_d = np.sqrt(anchors_reshaped[:, 4]**2 + anchors_reshaped[:, 5]**2)
    boxes3d = np.zeros_like(deltas)
    boxes3d[..., [0, 1]] = deltas[..., [0, 1]] * \
        anchors_d[:, np.newaxis] + anchors_reshaped[..., [0, 1]]
    boxes3d[..., [2]] = deltas[..., [2]] * \
        cfg.ANCHOR_H + anchors_reshaped[..., [2]]
    boxes3d[..., [3, 4, 5]] = np.exp(
        deltas[..., [3, 4, 5]]) * anchors_reshaped[..., [3, 4, 5]]
    boxes3d[..., 6] = deltas[..., 6] + anchors_reshaped[..., 6]

    return boxes3d

def point_transform(points, tx, ty, tz, rx=0, ry=0, rz=0):
    # Input:
    #   points: (N, 3)
    #   rx/y/z: in radians
    # Output:
    #   points: (N, 3)
    N = points.shape[0]
    points = np.hstack([points, np.ones((N, 1))])

    mat1 = np.eye(4)
    mat1[3, 0:3] = tx, ty, tz
    points = np.matmul(points, mat1)

    if rx != 0:
        mat = np.zeros((4, 4))
        mat[0, 0] = 1
        mat[3, 3] = 1
        mat[1, 1] = np.cos(rx)
        mat[1, 2] = -np.sin(rx)
        mat[2, 1] = np.sin(rx)
        mat[2, 2] = np.cos(rx)
        points = np.matmul(points, mat)

    if ry != 0:
        mat = np.zeros((4, 4))
        mat[1, 1] = 1
        mat[3, 3] = 1
        mat[0, 0] = np.cos(ry)
        mat[0, 2] = np.sin(ry)
        mat[2, 0] = -np.sin(ry)
        mat[2, 2] = np.cos(ry)
        points = np.matmul(points, mat)

    if rz != 0:
        mat = np.zeros((4, 4))
        mat[2, 2] = 1
        mat[3, 3] = 1
        mat[0, 0] = np.cos(rz)
        mat[0, 1] = -np.sin(rz)
        mat[1, 0] = np.sin(rz)
        mat[1, 1] = np.cos(rz)
        points = np.matmul(points, mat)

    return points[:, 0:3]

def box_transform(boxes, tx, ty, tz, r=0, coordinate='lidar'):
    # Input:
    #   boxes: (N, 10) x y z h w l q0-3
    # Output:
    #   boxes: (N, 10) x y z h w l q0-3

    boxes_corner = center_to_corner_box3d(boxes, coordinate=coordinate)  # (N, 8, 3)
    for idx in range(len(boxes_corner)):
        if coordinate == 'lidar':
            boxes_corner[idx] = point_transform(
                boxes_corner[idx], tx, ty, tz, rz=r)
        else:
            boxes_corner[idx] = point_transform(
                boxes_corner[idx], tx, ty, tz, ry=r)

    return corner_to_center_box3d(boxes_corner, coordinate=coordinate)

def rotate_label(label, rx=0, ry=0, rz=0):

    if rx != 0:
        mat = np.zeros((3, 3))
        mat[0, 0] = 1
        mat[1, 1] = np.cos(rx)
        mat[1, 2] = -np.sin(rx)
        mat[2, 1] = np.sin(rx)
        mat[2, 2] = np.cos(rx)

    if ry != 0:
        mat = np.zeros((3, 3))
        mat[1, 1] = 1
        mat[0, 0] = np.cos(ry)
        mat[0, 2] = np.sin(ry)
        mat[2, 0] = -np.sin(ry)
        mat[2, 2] = np.cos(ry)

    if rz != 0:
        mat = np.zeros((3, 3))
        mat[2, 2] = 1
        mat[0, 0] = np.cos(rz)
        mat[0, 1] = -np.sin(rz)
        mat[1, 0] = np.sin(rz)
        mat[1, 1] = np.cos(rz)

    for i,row in enumerate(label):
      translation = row[0:3]
      quaternion = row[6:10]
      roll, pitch, yaw = qaut_to_angle(quaternion)
      rot_quaternion = angle_to_quat(roll, pitch, yaw-rz)
      row[6:10] = rot_quaternion
      row[0:3] = np.matmul(translation, mat)
    return label

def cal_iou2d(box1, box2, T_VELO_2_CAM=None, R_RECT_0=None):
    # Input:
    #   box1/2: x, y, w, l, r
    # Output :
    #   iou
    buf1 = np.zeros((cfg.INPUT_HEIGHT, cfg.INPUT_WIDTH, 3))
    buf2 = np.zeros((cfg.INPUT_HEIGHT, cfg.INPUT_WIDTH, 3))
    tmp = center_to_corner_box2d(np.array([box1, box2]), coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)
    box1_corner = batch_lidar_to_bird_view(tmp[0]).astype(np.int32)
    box2_corner = batch_lidar_to_bird_view(tmp[1]).astype(np.int32)
    buf1 = cv2.fillConvexPoly(buf1, box1_corner, color=(1,1,1))[..., 0]
    buf2 = cv2.fillConvexPoly(buf2, box2_corner, color=(1,1,1))[..., 0]
    indiv = np.sum(np.absolute(buf1-buf2))
    share = np.sum((buf1 + buf2) == 2)
    if indiv == 0:
        return 0.0 # when target is out of bound
    return share / (indiv + share)

def cal_z_intersect(cz1, h1, cz2, h2):
    b1z1, b1z2 = cz1 - h1 / 2, cz1 + h1 / 2
    b2z1, b2z2 = cz2 - h2 / 2, cz2 + h2 / 2
    if b1z1 > b2z2 or b2z1 > b1z2:
        return 0
    elif b2z1 <= b1z1 <= b2z2:
        if b1z2 <= b2z2:
            return h1 / h2
        else:
            return (b2z2 - b1z1) / (b1z2 - b2z1)
    elif b1z1 < b2z1 < b1z2:
        if b2z2 <= b1z2:
            return h2 / h1
        else:
            return (b1z2 - b2z1) / (b2z2 - b1z1)

def cal_iou3d(box1, box2, T_VELO_2_CAM=None, R_RECT_0=None):
    # Input:
    #   box1/2: x, y, z, h, w, l, r
    # Output:
    #   iou
    buf1 = np.zeros((cfg.INPUT_HEIGHT, cfg.INPUT_WIDTH, 3))
    buf2 = np.zeros((cfg.INPUT_HEIGHT, cfg.INPUT_WIDTH, 3))
    tmp = center_to_corner_box2d(np.array([box1[[0,1,4,5,6]], box2[[0,1,4,5,6]]]), coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)
    box1_corner = batch_lidar_to_bird_view(tmp[0]).astype(np.int32)
    box2_corner = batch_lidar_to_bird_view(tmp[1]).astype(np.int32)
    buf1 = cv2.fillConvexPoly(buf1, box1_corner, color=(1,1,1))[..., 0]
    buf2 = cv2.fillConvexPoly(buf2, box2_corner, color=(1,1,1))[..., 0]
    share = np.sum((buf1 + buf2) == 2)
    area1 = np.sum(buf1)
    area2 = np.sum(buf2)

    z1, h1, z2, h2 = box1[2], box1[3], box2[2], box2[3]
    z_intersect = cal_z_intersect(z1, h1, z2, h2)

    return share * z_intersect / (area1 * h1 + area2 * h2 - share * z_intersect)

def cal_box3d_iou(boxes3d, gt_boxes3d, cal_3d=0, T_VELO_2_CAM=None, R_RECT_0=None):
    # Inputs:
    #   boxes3d: (N1, 7) x,y,z,h,w,l,r
    #   gt_boxed3d: (N2, 7) x,y,z,h,w,l,r
    # Outputs:
    #   iou: (N1, N2)
    N1 = len(boxes3d)
    N2 = len(gt_boxes3d)
    output = np.zeros((N1, N2), dtype=np.float32)

    for idx in range(N1):
        for idy in range(N2):
            if cal_3d:
                output[idx, idy] = float(
                    cal_iou3d(boxes3d[idx], gt_boxes3d[idy]), T_VELO_2_CAM, R_RECT_0)
            else:
                output[idx, idy] = float(
                    cal_iou2d(boxes3d[idx, [0, 1, 4, 5, 6]], gt_boxes3d[idy, [0, 1, 4, 5, 6]], T_VELO_2_CAM, R_RECT_0))

    return output

def cal_box2d_iou(boxes2d, gt_boxes2d, T_VELO_2_CAM=None, R_RECT_0=None):
    # Inputs:
    #   boxes2d: (N1, 5) x,y,w,l,r
    #   gt_boxes2d: (N2, 5) x,y,w,l,r
    # Outputs:
    #   iou: (N1, N2)
    N1 = len(boxes2d)
    N2 = len(gt_boxes2d)
    output = np.zeros((N1, N2), dtype=np.float32)
    for idx in range(N1):
        for idy in range(N2):
            output[idx, idy] = cal_iou2d(boxes2d[idx], gt_boxes2d[idy], T_VELO_2_CAM, R_RECT_0)

    return output


# -*- coding: utf-8 -*-

from setuptools import setup
from os.path import join, dirname
from setuptools import find_packages

def parse_requirements(filename):
    """ load requirements from a pip requirements file """
    lineiter = (line.strip() for line in open(filename, encoding='utf-8'))
    return [line for line in lineiter if line and not line.startswith("#")]

with open(join(dirname(__file__), './VERSION.txt'), 'rb') as f:
    version = f.read().decode('ascii').strip()

setup(
    name='HtscUtils',  # 模块名称
    version=version,  # 版本号
    description='Spider Utiles for Scrapy',  # 描述
    packages=find_packages(exclude=[]),
    author='st',
    author_email='K1131219@test.htsc.com.cn',
    license='',
    package_data={'': ['*.*']},
    url='#',
    install_requires=parse_requirements("requirements.txt"),  # 所需的运行依赖环境（包）
    zip_safe=False,
    classifiers=[
        'Programming Language :: Python',
        'Operating System :: Microsoft :: Windows',
        'Operating System :: Unix',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
    ],
)


"""create tables

Revision ID: 190753d7eabf
Revises:
Create Date: 2023-07-04 16:32:23.448688

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '190753d7eabf'
down_revision = None
branch_labels = None
depends_on = None

def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('current_user_language',
    sa.Column('user_id', sa.Integer(), nullable=False),
    sa.Column('current_lang_code', sa.String(length=2), nullable=False),
    sa.PrimaryKeyConstraint('user_id')
    )
    op.create_table('user_history',
    sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('user_id', sa.Integer(), nullable=False),
    sa.Column('source_lang_code', sa.String(length=2), nullable=False),
    sa.Column('text', sa.String(length=10000), nullable=False),
    sa.Column('target_lang_code', sa.String(length=2), nullable=False),
    sa.Column('translated_text', sa.String(length=10000), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###

def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('user_history')
    op.drop_table('current_user_language')
    # ### end Alembic commands ###


import logging
import neomodel  # type: ignore
from typing import List, Optional
from pydantic import BaseSettings
from fastapi import FastAPI, Depends
from fastapi.responses import RedirectResponse
from fastapi_users import FastAPIUsers
from fastapi_users.authentication import JWTAuthentication
import arcade.models.graph as graph
import arcade.models.api as models

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Setup environment settings
class APISettings(BaseSettings):
    neo4j_url: str
    jwt_secret: str

settings = APISettings()

# Setup the database connection
neomodel.config.DATABASE_URL = settings.neo4j_url

# Tell fastpai_users how to interact with user data
user_db = graph.FastAPIUserDBAdapter(models.UserDB)

api_desc = ('The Advanced Research Collaboration and Application Development '
            'Environment (ARCADE) provides a unified and coherent API for '
            'accessing, analyzing, and extending a diverse set of derived '
            'data products concerning anthropogenic space objects.')
app = FastAPI(title='ARCADE', description=api_desc)

jwt_authentication = JWTAuthentication(
    secret=settings.jwt_secret,
    lifetime_seconds=3600,
    tokenUrl="auth/jwt/login"
)

# Setup fastapi_users to handle user CRUD, authentication, and authorization
fastapi_users = FastAPIUsers(
    user_db,
    [jwt_authentication],
    models.User,
    models.UserCreate,
    models.UserUpdate,
    models.UserDB
)

# Add the router for users to get JWTs
app.include_router(
    fastapi_users.get_auth_router(jwt_authentication),
    prefix="/auth/jwt",
    tags=["Authentication"]
)

# Add the router that provides the user registration endpoint
app.include_router(
    fastapi_users.get_register_router(),
    prefix="/auth",
    tags=["Authentication"],
    include_in_schema=False
)

# Helper functions that gets the user based on the JWT passed to the endpoint
current_active_user = fastapi_users.current_user(active=True)
current_super_user = fastapi_users.current_user(active=True, superuser=True)

@app.get('/',
         response_class=RedirectResponse,
         include_in_schema=False)
async def redirect_to_project() -> str:
    """Redirects the root path to the project website"""
    return 'https://ibm.github.io/arcade'

@app.get('/user_reports',
         response_model=List[models.UserReport],
         include_in_schema=False)
async def get_user_reports(user: models.User = Depends(current_super_user)
                           ) -> List[models.UserReport]:
    """Returns summary statistics for all users"""
    report_query = """MATCH (u:User)-[r:accessed]->()
                      RETURN u.email AS email, count(r) AS access_count"""
    raw_report, _ = neomodel.db.cypher_query(report_query)
    report_rows = [models.UserReport(email=row[0], access_count=row[1])
                   for row in raw_report]
    return report_rows

@app.get('/asos',
         response_model=List[models.ASO],
         tags=['ARCADE Endpoints'])
async def get_asos(user: models.User = Depends(current_active_user)
                   ) -> List[models.ASO]:
    """Returns information on all the anthropogenic space objects (ASOs) that
    ARCADE knows about.
    """
    aso_nodes = graph.SpaceObject.nodes.all()
    asos = [models.ASO.from_orm(n) for n in aso_nodes]
    return asos

@app.get('/asos/{aso_id}',
         response_model=models.ASO,
         tags=['ARCADE Endpoints'])
async def get_aso(aso_id: str,
                  user: models.User = Depends(current_active_user)
                  ) -> Optional[models.ASO]:
    """Returns information about the ASO matching the passed ASO ID."""
    aso_node = graph.SpaceObject.find_one(aso_id=aso_id)
    if not aso_node:
        return None
    aso = models.ASO.from_orm(aso_node)
    return aso

@app.get('/ephemeris/{aso_id}',
         response_model=List[models.OrbitEphemerisMessage],
         tags=['ARCADE Endpoints'])
async def get_ephemeris(aso_id: str,
                        user: models.User = Depends(current_active_user)
                        ) -> List[models.OrbitEphemerisMessage]:
    """Provides the most up-to-date ephemeris data for the given ASO"""
    oem_nodes = graph.SpaceObject.get_latest_oems(aso_id)
    if not oem_nodes:
        return []
    user_node = graph.User.find_one(uid=str(user.id))
    if not user_node:
        return []

    oems = []
    for oem_node in oem_nodes:
        if not user_node.can_access(oem_node):
            continue
        oem = models.OrbitEphemerisMessage.from_orm(oem_node)
        user_node.accessed.connect(oem_node, {'endpoint': '/ephemeris'})
        oems.append(oem)

    return oems

@app.get('/interpolate/{aso_id}',
         response_model=List[models.OrbitEphemerisMessage],
         tags=['ARCADE Endpoints'])
async def get_interpolation(aso_id: str,
                            step_size: float = 60.0,
                            user: models.User = Depends(current_active_user)
                            ) -> List[models.OrbitEphemerisMessage]:
    """Interpolates the ephemeris data for given ASP based on the step
    size (seconds)."""
    oem_nodes = graph.SpaceObject.get_latest_oems(aso_id)
    if not oem_nodes:
        return []
    user_node = graph.User.find_one(uid=str(user.id))
    if not user_node:
        return []

    interp_oems = []
    for oem_node in oem_nodes:
        if not user_node.can_access(oem_node):
            continue
        oem = models.OrbitEphemerisMessage.from_orm(oem_node)
        interp_oem = oem.interpolate(step_size=step_size)
        user_node.accessed.connect(oem_node, {'endpoint': '/interpolate'})
        interp_oems.append(interp_oem)

    return interp_oems

@app.get('/compliance/{aso_id}',
         response_model=models.UNCompliance,
         tags=['ARCADE Endpoints'])
async def get_compliance(aso_id: str,
                         user: models.User = Depends(current_active_user)
                         ) -> Optional[models.UNCompliance]:
    """Returns whether the ASO is compliant in registering with the United
    Nations."""
    aso_node = graph.SpaceObject.find_one(aso_id=aso_id)
    if not aso_node:
        return None
    compliance_nodes = aso_node.compliance.all()
    if compliance_nodes:
        compliance_node = compliance_nodes[0]
    else:
        return None
    user_node = graph.User.find_one(uid=str(user.id))
    if not user_node or not user_node.can_access(compliance_node):
        return None
    compliance = models.UNCompliance(aso_id=aso_id,
                                     is_compliant=compliance_node.is_compliant)
    user_node.accessed.connect(compliance_node, {'endpoint': '/compliance'})
    return compliance


import asyncio
import traceback
from typing import List

from aardwolf import logger
from aardwolf.keyboard.layoutmanager import KeyboardLayoutManager
from aardwolf.keyboard import KeyboardLayout, VK_MODIFIERS

class DuckyExecutorBase:
	def __init__(self, keyboard_layout:KeyboardLayout, key_sender, send_as_char = False):
		self.keyboard_layout = keyboard_layout
		self.key_sender = key_sender
		self.send_as_char = send_as_char
		self.default_delay = 100
		self.default_chardelay = 50/1000
		self.aliases = {
			'do_default_delay' : 'do_defaultdelay',
			'do_windows' : 'do_gui',
			'do_app' : 'do_menu',
			'do_ctrl' : 'do_control',
			'do_uparrow' : 'do_up',
			'do_leftarrow' : 'do_left',
			'do_rightarrow' : 'do_right',
			'do_break' : 'do_pause',
			'do_delete' : 'do_del',
			'do_escape' : 'do_esc',
			'do_downarrow' : 'do_down',
			'do_delete' : 'do_del',
		}

		self.cmd_to_vk = {
			'backspace' : 'VK_BACK',
			'tab' : 'VK_TAB',
			'space' : 'VK_SPACE',
			'scrollock' : 'VK_SCROLL',
			'printscreen' : 'VK_SNAPSHOT',
			'pagedown' : 'VK_NEXT',
			'pageup' : 'VK_PRIOR',
			'numlock' : 'VK_NUMLOCK',
			'insert' : 'VK_INSERT',
			'home' : 'VK_HOME',
			'esc' : 'VK_ESCAPE',
			'escape' : 'VK_ESCAPE',
			'end' : 'VK_END',
			'del' : 'VK_DELETE',
			'delete' : 'VK_DELETE',
			'capslock' : 'VK_CAPITAL',
			'pause' : 'VK_PAUSE',
			'break' : 'VK_PAUSE',
			'right' : 'VK_RIGHT',
			'rightarrow' : 'VK_RIGHT',
			'left' : 'VK_LEFT',
			'leftarrow' : 'VK_LEFT',
			'down' : 'VK_DOWN',
			'downarrow' : 'VK_DOWN',
			'up' : 'VK_UP',
			'uparrow' : 'VK_UP',
			'gui': 'VK_LWIN',
			'windows': 'VK_LWIN',
			'enter' : 'VK_RETURN',
			'shift' : 'VK_LSHIFT',
			'alt' : 'VK_LMENU',
			'f1' : 'VK_F1',
			'f2' : 'VK_F2',
			'f3' : 'VK_F3',
			'f4' : 'VK_F4',
			'f5' : 'VK_F5',
			'f6' : 'VK_F6',
			'f7' : 'VK_F7',
			'f8' : 'VK_F8',
			'f9' : 'VK_F9',
			'f10' : 'VK_F10',
			'f11' : 'VK_F11',
			'f12' : 'VK_F12',

		}

	def get_function(self, cmdname):
		if cmdname.startswith('do_') is False:
			cmdname = 'do_' + cmdname

		if cmdname in self.aliases:
			cmdname = self.aliases[cmdname]

		return getattr(self, cmdname)

	async def do_enter(self):
		await self.keydispatch('VK_RETURN')
		#await asyncio.sleep(self.default_delay)

	async def do_function(self, data):
		await self.keydispatch('VK_F%s' % data[0])

	async def do_rem(self, data:List[str]):
		data = ' '.join(data)
		logger.debug(data)

	async def do_defaultdelay(self, delay:int):
		delay = ' '.join(delay)
		self.default_delay = int(delay) / 1000 #protocol allows delay to be set in 10 miliseconds interval

	async def do_defaultchardelay(self, delay:int):
		delay = ' '.join(delay)
		self.default_chardelay = int(delay) / 1000

	async def do_delay(self, delay:int):
		pass
		delay = ' '.join(delay)
		delay = int(delay) / 1000
		if delay > 1:
			return
		await asyncio.sleep(delay)

	async def do_string(self, data:str):
		data = ' '.join(data)
		if self.send_as_char is True:
			for c in data:
				await self.key_sender(c, True, True)
				await asyncio.sleep(self.default_chardelay)
		else:
			for c in data:
				await self.keydispatch(c)

	async def do_gui(self, data = []):
		if len(data) > 0:
			data.insert(0, 'VK_LWIN')
			await self.multi_key_press(data)
		else:
			await self.keydispatch('VK_LWIN')
		#await asyncio.sleep(self.default_delay)

	async def do_menu(self):
		await self.keydispatch('VK_RMENU')
		#await asyncio.sleep(self.default_delay)

	async def do_shift(self, data = []):
		print('SHIFT + %s' % repr(data))
		if len(data) > 0:
			data.insert(0, 'VK_LSHIFT')
			await self.multi_key_press(data)
		else:
			await self.keydispatch('VK_LSHIFT')

	async def do_control(self, data = []):
		print('CTRL + %s' % repr(data))
		if len(data) > 0:
			data.insert(0, 'VK_LCONTROL')
			await self.multi_key_press(data)
		else:
			await self.keydispatch('VK_LCONTROL')

	async def do_alt(self, data = []):
		print('ALT + %s' % repr(data))
		if len(data) > 0:
			data.insert(0, 'VK_LMENU')
			await self.multi_key_press(data)
		else:
			await self.keydispatch('VK_LMENU')

	async def do_up(self):
		await self.keydispatch('VK_UP')
		#await asyncio.sleep(self.default_delay)

	async def do_down(self):
		await self.keydispatch('VK_DOWN')
		#await asyncio.sleep(self.default_delay)

	async def do_left(self):
		await self.keydispatch('VK_LEFT')
		#await asyncio.sleep(self.default_delay)

	async def do_right(self):
		await self.keydispatch('VK_RIGHT')
		#await asyncio.sleep(self.default_delay)

	async def do_pause(self):
		await self.keydispatch('VK_PAUSE')
		#await asyncio.sleep(self.default_delay)

	async def do_capslock(self):
		await self.keydispatch('VK_CAPITAL')
		#await asyncio.sleep(self.default_delay)

	async def do_del(self):
		await self.keydispatch('VK_DELETE')
		#await asyncio.sleep(self.default_delay)

	async def do_end(self):
		await self.keydispatch('VK_END')
		#await asyncio.sleep(self.default_delay)

	async def do_esc(self):
		await self.keydispatch('VK_ESCAPE')
		#await asyncio.sleep(self.default_delay)

	async def do_home(self):
		await self.keydispatch('VK_HOME')
		#await asyncio.sleep(self.default_delay)

	async def do_insert(self):
		await self.keydispatch('VK_INSERT')
		#await asyncio.sleep(self.default_delay)

	async def do_numlock(self):
		await self.keydispatch('VK_NUMLOCK')
		#await asyncio.sleep(self.default_delay)

	async def do_pageup(self):
		await self.keydispatch('VK_PRIOR')
		#await asyncio.sleep(self.default_delay)

	async def do_pagedown(self):
		await self.keydispatch('VK_NEXT')
		#await asyncio.sleep(self.default_delay)

	async def do_printscreen(self):
		await self.keydispatch('VK_SNAPSHOT')
		#await asyncio.sleep(self.default_delay)

	async def do_scrollock(self):
		await self.keydispatch('VK_SCROLL')
		#await asyncio.sleep(self.default_delay)

	async def do_space(self):
		await self.keydispatch('VK_SPACE')
		#await asyncio.sleep(self.default_delay)

	async def do_tab(self):
		await self.keydispatch('VK_TAB')
		#await asyncio.sleep(self.default_delay)

	async def do_backspace(self):
		await self.keydispatch('VK_BACK')
		#await asyncio.sleep(self.default_delay)

	async def multi_key_press(self, keys):
		codes = []
		#print(keys)
		for x in keys:
			#print(x)
			if len(x) == 1:
				scancode, mo = self.keyboard_layout.char_to_scancode(x.lower())
				if mo != VK_MODIFIERS(0) and mo != VK_MODIFIERS.VK_CAPITAL|VK_MODIFIERS.VK_SHIFT:
					print(mo)
					raise Exception('This is not supported!!!!')
				codes.append(scancode)
			else:
				if x.lower() in self.cmd_to_vk:
					codes.append(self.keyboard_layout.vk_to_scancode(self.cmd_to_vk[x.lower()]))
				else:
					codes.append(self.keyboard_layout.vk_to_scancode(x))

		for code in codes:
			await asyncio.sleep(self.default_chardelay)
			await self.key_sender(code, True)

		for code in codes[::-1]:
			await asyncio.sleep(self.default_chardelay)
			await self.key_sender(code, False)

	async def keydispatch(self, key, modifiers = VK_MODIFIERS(0)):
		if key in '0123456789':
			key = 'VK_%s' % key
		if len(key) == 1:
			try:
				scancode, mo = self.keyboard_layout.char_to_scancode(key)
				#print('key     : %s' % key)
				#print('scancode: %s' % scancode)
				#print('mo      : %s' % mo)

			except KeyError:
				# this is bad...
				await asyncio.sleep(self.default_chardelay)
				await self.key_sender(key, True, True)
				return

			if mo == VK_MODIFIERS(0):
				await asyncio.sleep(self.default_chardelay)
				await self.key_sender(scancode, True)
				await asyncio.sleep(self.default_chardelay)
				await self.key_sender(scancode, False)
			else:
				#print('key: %s' % key)
				#print('sc : %s' % scancode)
				#print('mo : %s' % repr(mo))
				#input()
				modcodes = []
				afs = [flag for flag in VK_MODIFIERS if flag in mo]
				for af in afs:
					if af == VK_MODIFIERS.VK_CAPITAL:
						af = VK_MODIFIERS.VK_SHIFT
					modname = af.name
					if modname in ['VK_SHIFT', 'VK_CONTROL', 'VK_MENU']:
						modname = modname[:3] + 'L' + modname[3:]
					sc = self.keyboard_layout.vk_to_scancode(modname)
					modcodes.append(sc)
				for mod in modcodes:
					await asyncio.sleep(self.default_chardelay)
					await self.key_sender(mod, True)

				await asyncio.sleep(self.default_chardelay)
				await self.key_sender(scancode, True)
				await asyncio.sleep(self.default_chardelay)
				await self.key_sender(scancode, False)
				for mod in modcodes[::-1]:
					await asyncio.sleep(self.default_chardelay)
					await self.key_sender(mod, False)

		else:
			scancode = self.keyboard_layout.vk_to_scancode(key)
			await asyncio.sleep(self.default_chardelay)
			await self.key_sender(scancode, True)
			await asyncio.sleep(self.default_chardelay)
			await self.key_sender(scancode, False)

class DuckyReaderBase:
	def __init__(self, executor):
		self.executor = executor

	async def execute_line(self, line):
		try:
			line = line.strip()
			if line == '':
				return
			if line.find(' ') == -1:
				keyword = line.lower()
				data = []
			else:
				keyword, *data = line.split(' ')
				keyword = keyword.lower()

			if keyword.startswith('#') is True:
				return

			if keyword.startswith('rem'):
				if keyword[3:] != '':
					data.insert(0, keyword[3:])
				keyword = 'rem'

			if keyword.find('-') != -1:
				keyword, *temp = keyword.split('-')
				keyword = keyword.lower()
				data = temp + data

			if keyword.startswith('f') is True and (len(keyword) == 2 or len(keyword) == 3):
				keyid = int(keyword[1:])
				keyword = 'function'
				data.insert(0, str(keyid))
			fnct = self.executor.get_function(keyword)
			if len(data) > 0:
				await fnct(data)
			else:
				await fnct()

		except Exception as e:
			print('ERROR! Line: %s' % repr(line))
			raise e

	@staticmethod
	def from_file(filepath, executor):
		reader = DuckyReaderFile(filepath, executor)
		return reader

class DuckyReaderFile(DuckyReaderBase):
	def __init__(self, filepath, executor):
		DuckyReaderBase.__init__(self, executor)
		self.filepath = filepath
		self.file = open(filepath, 'r')

	async def parse(self):
		for line in self.file:
			line = line.strip()
			if len(line) == 0:
				continue
			await self.execute_line(line)

async def key_sender(scancode, is_pressed, as_char = False):
	#print(scancode)
	#print(is_pressed)
	return

async def amain():
	layout = KeyboardLayoutManager().get_layout_by_shortname('enus')
	from pathlib import Path
	try:
		for path in Path('/home/webdev/Desktop/usbrubberducky-payloads/payloads/').rglob('*'):
			print('Now processing %s' % path)
			if path.is_dir():
				continue
			if path.name.endswith('.md') is True or path.name.endswith('.bat') is True:
				continue
			executor = DuckyExecutorBase(layout, key_sender)
			reader = DuckyReaderFile.from_file(path, executor)
			await reader.parse()
	except Exception as e:
		traceback.print_exc()

def main():
	asyncio.run(amain())

if __name__ == '__main__':
	main()


from __code import config
import getpass
import glob
import os
import platform
from ipywidgets import widgets
from IPython.core.display import display
from IPython.core.display import HTML

from __code._utilities.time import get_current_time_in_special_file_name_format
from __code import LOGGER_FILE
from __code._utilities.file import append_to_file

list_instrument_per_facility = {'HFIR': ['CG1D'],
                                'SNS': ['SNAP', 'VENUS']}

class System:

    working_dir = ''
    start_path = ''

    @classmethod
    def select_working_dir(cls, debugger_folder='', system_folder='',
                           facility='HFIR',
                           instrument='CG1D',
                           notebook="N/A"):

        try:

            debugging = config.debugging
            if debugging:
                print("** Using Debugging Mode! **")

            display(HTML("""

                       """))

            full_list_instruments = cls.get_full_list_instrument()
            full_list_instruments.sort()
            start_path = cls.get_start_path(debugger_folder=debugger_folder,
                                            system_folder=system_folder,
                                            instrument=full_list_instruments[0])

            cls.start_path = start_path

            select_instrument_ui = widgets.HBox([widgets.Label("Select Instrument",
                                                      layout=widgets.Layout(width='20%')),
                                        widgets.Select(options=full_list_instruments,
                                                       value=full_list_instruments[0],
                                                       layout=widgets.Layout(width='20%'))])
            cls.instrument_ui = select_instrument_ui.children[1]
            cls.instrument_ui.observe(cls.check_instrument_input, names='value')

            help_ui = widgets.Button(description="HELP",
                                     button_style='info')
            help_ui.on_click(cls.select_ipts_help)

            top_hbox = widgets.HBox([widgets.Label("IPTS-"),
                                     widgets.Text(value="",
                                                  layout=widgets.Layout(width='10%')),
                                     widgets.Label("DOES NOT EXIST!",
                                                   layout=widgets.Layout(width='20%'))])
            cls.result_label = top_hbox.children[2]
            cls.ipts_number = top_hbox.children[1]
            cls.result_label.add_class("result_label")
            or_label = widgets.Label("OR")

            list_and_default_folders = cls.get_list_folders(start_path=start_path)
            user_list_folders = list_and_default_folders['user_list_folders']
            default_value = list_and_default_folders['default_value']

            bottom_hbox = widgets.HBox([widgets.Label("Select Folder",
                                               layout=widgets.Layout(width="20%")),
                                 widgets.Select(options=user_list_folders,
                                                value=default_value,
                                                layout=widgets.Layout(height='300px')),
                                 ])
            cls.user_list_folders = user_list_folders
            box = widgets.VBox([select_instrument_ui, top_hbox, or_label, bottom_hbox, help_ui])
            display(box)

            cls.working_dir_ui = bottom_hbox.children[1]
            cls.manual_ipts_entry_ui = top_hbox.children[1]
            cls.manual_ipts_entry_ui.observe(cls.check_ipts_input, names='value')

            cls.result_label.value = ""

        except:
            cls.working_dir = os.path.expanduser("~")
            display(HTML('working dir set to -> ' + cls.working_dir +
                         ''))

        cls.log_use(notebook=notebook)

    @classmethod
    def log_use(cls, notebook="N/A"):
        if os.path.exists(os.path.dirname(LOGGER_FILE)):
            # no dot log usage if notebooks are run locally
            username = getpass.getuser()
            date = get_current_time_in_special_file_name_format()
            data = [f"{date}: {username} started using {notebook}"]
            append_to_file(data=data, output_file_name=LOGGER_FILE)

    @classmethod
    def get_full_list_instrument(cls):

        list_instrument = []
        for _key in list_instrument_per_facility.keys():
            _facility_list_instrument = list_instrument_per_facility[_key]
            for _instr in _facility_list_instrument:
                list_instrument.append(_instr)
        return list_instrument

    @classmethod
    def get_list_folders(cls, start_path=''):
        debugging = config.debugging

        if debugging:
            instrument = cls.get_instrument_selected()
            computer_name = cls.get_computer_name()
            start_path = config.debugger_instrument_folder[computer_name][instrument]
            cls.start_path = start_path

        list_folders = sorted(glob.glob(os.path.join(start_path, '*')))
        short_list_folders = [os.path.basename(_folder) for _folder in list_folders if os.path.isdir(_folder)]
        # short_list_folders = sorted(short_list_folders)

        # if user mode, only display folder user can access
        default_value = ''
        if not debugging:
            user_list_folders = [os.path.basename(_folder) for _folder in list_folders if os.access(_folder, os.R_OK)]
            if len(user_list_folders) > 0:
                default_value = user_list_folders[0]
        else:  # debugging
            user_list_folders = short_list_folders
            default_value = config.project_folder
            if not (default_value in user_list_folders):
                if len(user_list_folders) > 0:
                    default_value = user_list_folders[0]

        return {'user_list_folders': user_list_folders,
                'default_value': default_value}

    @classmethod
    def get_facility_from_instrument(cls, instrument='CG1D'):

        for _facility in list_instrument_per_facility:
            list_instrument = list_instrument_per_facility[_facility]
            if instrument in list_instrument:
                return _facility

        return 'HFIR'

    @classmethod
    def get_instrument_selected(cls):
        return cls.instrument_ui.value

    @classmethod
    def get_computer_name(cls):
        return platform.node()

    @classmethod
    def get_facility_selected(cls):
        return cls.get_facility_from_instrument(instrument=cls.get_instrument_selected())

    @classmethod
    def get_start_path(cls, debugger_folder='', system_folder='', instrument=''):

        facility = cls.get_facility_from_instrument(instrument=instrument)

        username = getpass.getuser()

        debugging = config.debugging
        debugger_username = config.debugger_username

        found_a_folder = False
        if debugger_folder == '':
            for _folder in config.debugger_folder:
                if os.path.exists(_folder):
                    debugger_folder = _folder
                    found_a_folder = True
                    break

        if not found_a_folder:
            debugger_folder = './'

        if debugging and (username == debugger_username):
            # print("** Using Debugging Mode! **")

            # check that in debugging mode, on analysis machine, default folder exists
            import socket

            if socket.gethostname() == config.analysis_machine:
                if not os.path.exists(debugger_folder):
                    debugging = False

            start_path = debugger_folder
        else:
            if system_folder == '':
                start_path = "/{}/{}/".format(facility, instrument)
            else:
                start_path = system_folder
            import warnings
            warnings.filterwarnings('ignore')

        return start_path

    @classmethod
    def select_ipts_help(cls, value):
        import webbrowser
        webbrowser.open("https://neutronimaging.pages.ornl.gov/tutorial/notebooks/select_ipts/")

    @classmethod
    def check_instrument_input(cls, value_dict):
        instrument = value_dict['new']

        start_path = cls.get_start_path(instrument=instrument)
        cls.start_path = start_path
        list_and_default_folders = cls.get_list_folders(start_path=start_path)

        user_list_folders = list_and_default_folders['user_list_folders']
        default_value = list_and_default_folders['default_value']

        cls.working_dir_ui.options = user_list_folders
        cls.working_dir_ui.value = default_value

        cls.ipts_number.value = ''
        cls.result_label.value = ''

    @classmethod
    def check_ipts_input(cls, value):
        ipts = value['new']
        full_ipts = 'IPTS-{}'.format(ipts)
        if os.path.exists(os.path.join(cls.start_path, full_ipts)):
            # display(HTML("""
            #
            #            """))
            cls.result_label.value = "OK"
            #select IPTS folder defined
            cls.working_dir_ui.value = full_ipts

        else:
            # display(HTML("""
            #
            #            """))
            cls.result_label.value = "DOES NOT EXIST!"

    @classmethod
    def get_working_dir(cls):
        if cls.working_dir:
            return cls.working_dir
        else:
            return os.path.join(cls.start_path, cls.working_dir_ui.value)