#!/usr/bin/env python3
"""
    Autoencoders
"""
import tensorflow.keras as keras

def sampling(args):
    z_mean, z_log_sigma = args
    # print('--------------',keras.backend.int_shape(z_mean))
    s1 = keras.backend.shape(z_mean)[0]
    s2 = keras.backend.int_shape(z_mean)[1]
    epsilon = keras.backend.random_normal(shape=(s1, s2))
    # shape=(keras.backend.int_shape(z_mean))),
    #                       mean=0., stddev=0.1)
    return z_mean + keras.backend.exp(z_log_sigma / 2) * epsilon

def autoencoder(input_dims, hidden_layers, latent_dims):
    """ creates a convolutional autoencoder
        input_dims is an integer containing the dimensions of the model input
        hidden_layers is a list containing the number of filters for each
            convolutional layer in the encoder, respectively
        the hidden layers should be reversed for the decoder
        latent_dims is a tuple of integers containing the dimensions
            of the latent space representation
        Each convolution in the encoder should use a kernel size of (3, 3)
            with same padding and relu activation, followed by max pooling
            of size (2, 2)
        Each convolution in the decoder, except for the last two, should use a
            filter size of (3, 3) with same padding and relu activation,
            followed by upsampling of size (2, 2)
        The second to last convolution should instead use valid padding
        The last convolution should have the same number of filters as number
            of channels in input_dims with sigmoid activation and no upsampling
        Returns: encoder, decoder, auto
            encoder is the encoder model
            decoder is the decoder model
            auto is the full autoencoder model
    """
    # print('...............................................')
    inputs = keras.Input(shape=(input_dims,))
    layer_enc = keras.layers.Dense(hidden_layers[0], activation='relu')(inputs)
    hl = len(hidden_layers) - 1
    for hl in range(1, len(hidden_layers)):
        # print(hl)
        layer_enc = keras.layers.Dense(hidden_layers[hl],
                                       activation='relu')(layer_enc)
    # print(hl)
    # latent_enc = keras.layers.Dense(latent_dims,
    #                                 activation='relu')(layer_enc)
    latent_enc = layer_enc
    z_mean = keras.layers.Dense(latent_dims)(latent_enc)
    z_log_sigma = keras.layers.Dense(latent_dims)(latent_enc)

    # epsilon = keras.backend.random_normal(shape=\
    # (keras.backend.shape(z_mean)[0], latent_dims),
    #                         mean=0., stddev=0.1)
    # z = z_mean + keras.backend.exp(z_log_sigma) * epsilon
    # print(type(z))
    # z = keras.layers.Layer(z)(latent_enc)

    z = keras.layers.Lambda(sampling)([z_mean, z_log_sigma])
    # print("zzzzz", type(z))
    # z = latent_enc

    encoded_input = keras.Input(shape=(latent_dims,))
    latent = keras.layers.Dense(hidden_layers[hl],
                                activation='relu')(encoded_input)

    c = 1
    for hl in range(len(hidden_layers) - 2, -1, -1):
        decod = keras.layers.Dense(hidden_layers[hl],
                                   activation='relu')(latent if c else decod)
        c = 0
    decoded = keras.layers.Dense(input_dims,
                                 activation='sigmoid')(latent if c else decod)

    encoder = keras.Model(inputs, [z, z_mean, z_log_sigma])
    decoder = keras.Model(encoded_input, decoded)
    outputs = decoder(encoder(inputs)[2])
    def vae_loss(inputs, outputs):
        ''' loss '''
        reconstruction_loss = keras.losses.binary_crossentropy(inputs, outputs)
        reconstruction_loss *= input_dims
        kl_loss = 1 + z_log_sigma - keras.backend.square(z_mean) -\
            keras.backend.exp(z_log_sigma)
        kl_loss = keras.backend.sum(kl_loss, axis=-1)
        kl_loss *= -0.5
        return keras.backend.mean(reconstruction_loss + kl_loss)
    # vae_loss = keras.backend.mean(reconstruction_loss + kl_loss)
    vae = keras.Model(inputs, outputs)
    # vae.add_loss(lambda: keras.backend.mean(reconstruction_loss + kl_loss))
    # vae.run_eagerly = True
    #adam = keras.optimizers.Adam(0.1)
    vae.compile(optimizer="Adam", loss=vae_loss) #, run_eagerly=False) #, experimental_run_tf_function=False) # 'binary_crossentropy')
    # print(7777777777)
    return encoder, decoder, vae


from django.test import TestCase

from chores import model_views, queries

from .utils import (create_away_date, create_chore, create_random_user,
                    create_tag)

class TestQueryChores(TestCase):

    def test_invalid_user(self):
        with self.assertRaises(ValueError) as cm:
            queries.query_chores(None, None)

        self.assertEqual(str(cm.exception), "Invalid user provided")

    def test_valid_user(self):
        user1 = create_random_user()
        user2 = create_random_user()

        chore1 = create_chore(user1)
        chore2 = create_chore(user1)

        self.assertEqual(queries.query_chores(user1, None), [
                         model_views.Chore(chore1), model_views.Chore(chore2)])
        self.assertEqual(queries.query_chores(user2, None), [])

    def test_chore_tag_filter(self):
        user = create_random_user()

        tag1 = create_tag(user)
        chore1 = create_chore(user, tag=tag1)
        chore2 = create_chore(user, tag=tag1)

        tag2 = create_tag(user)
        chore3 = create_chore(user, tag=tag2)

        self.assertEqual(queries.query_chores(user, tag1.id), [
                         model_views.Chore(chore1), model_views.Chore(chore2)])
        self.assertEqual(queries.query_chores(user, tag2.id),
                         [model_views.Chore(chore3)])

class TestQueryTags(TestCase):

    def test_invalid_user(self):
        with self.assertRaises(ValueError) as cm:
            queries.query_tags(None)

        self.assertEqual(str(cm.exception), "Invalid user provided")

    def test_valid_user(self):
        user1 = create_random_user()
        user2 = create_random_user()

        tag1 = create_tag(user1)
        tag2 = create_tag(user1)

        self.assertEqual(queries.query_tags(user1), [
                         model_views.Tag(tag1), model_views.Tag(tag2)])
        self.assertEqual(queries.query_tags(user2), [])

class TestQueryAwayDates(TestCase):

    def test_invalid_user(self):
        with self.assertRaises(ValueError) as cm:
            queries.query_away_dates(None)

        self.assertEqual(str(cm.exception), "Invalid user provided")

    def test_valid_user(self):
        user1 = create_random_user()
        user2 = create_random_user()

        away_date1 = create_away_date(user1)
        away_date2 = create_away_date(user1)

        self.assertEqual(queries.query_away_dates(user1), [
                         model_views.AwayDate(away_date1), model_views.AwayDate(away_date2)])
        self.assertEqual(queries.query_away_dates(user2), [])


import pygame

class Sound:
	def __init__(self):
		pygame.mixer.init()
		self.mute = False

	def mute_toggle(self):
		self.mute = not self.mute
		if self.mute:
			pygame.mixer.music.pause()
		else:
			pygame.mixer.music.unpause()

	# def vol_mute_max(self, x):
	# 	if self.mute:
	# 		x.set_volume(0.0)
	# 	else:
	# 		x.set_volume(1.0)

	def bullet_hit_wall_sound(self):
		if not self.mute:
			x = pygame.mixer.Sound("sounds/hitwall.wav")
			x.set_volume(0.05)
			x.play()

	def fire_sound(self):
		if not self.mute:
			x = pygame.mixer.Sound("sounds/boom1.wav")
			# self.vol_mute_max(x)
			x.set_volume(0.1)
			x.play()

	def crash_sound(self):
		if not self.mute:
			x = pygame.mixer.Sound("sounds/explosion.wav")
			# self.vol_mute_max(x)
			x.set_volume(0.1)
			x.play()

	def damage_sound(self):
		# pygame.mixer.Sound("sounds/damage.mp3").play()
		if not self.mute:
			pygame.mixer.music.load("sounds/damage.mp3")
			pygame.mixer.music.play()

	def menu_music(self):
		# pygame.mixer.Sound("sounds/menu.mp3").play()
		if not self.mute:
			pygame.mixer.music.load("sounds/menu.mp3")
			pygame.mixer.music.set_volume(0.3)
			pygame.mixer.music.play()

game_sound = Sound()


from models.credit_card import Card
from models.person import Person
from flask import request, jsonify
from app import app, db

@app.route('/card/<id>', methods=['GET'])
def get_card(id):
    card = Card.query.get(id)

    result = {
        'id': card.id,
        'person_cpf': card.person_cpf,
        'card_number': card.card_number,
        'card_company': card.card_company,
        'limit': card.limit,
        'card_statement': card.card_statement
    }

    return jsonify(result)

@app.route('/card', methods=['POST'])
def insert_card():
    person_cpf = request.json['person_cpf']
    card_number = request.json['card_number']
    card_company = request.json['card_company']
    limit = request.json['limit']
    card_statement = request.json['card_statement']

    card = Card(person_cpf, card_number, card_company, limit, card_statement)
    db.session.add(card)
    db.session.commit()

    return "Cartão inserido com sucesso!"

@app.route('/card', methods=['PUT'])
def update_card():

    id = request.json['id']

    card = Card.query.get(id)

    card.person_cpf = request.json['person_cpf']
    card.card_number = request.json['card_number']
    card.card_company = request.json['card_company']
    card.limit = request.json['limit']
    card.card_statement = request.json['card_statement']

    db.session.commit()

    return "Cartão atualizado com sucesso!"

@app.route('/card/<id>', methods=['DELETE'])
def delete_card(id):
    card = Card.query.get(id)

    db.session.delete(card)
    db.session.commit()

    return "Cartão deletado com sucesso!"

@app.route('/person/card/<person_cpf>', methods=['GET'])
def get_person_cards(person_cpf):
    person = Person.query.get(person_cpf)

    cards = person_cards(person.cards)

    return jsonify(cards)

def person_cards(cards):
    result = []

    for card in cards:
        data = {}

        data['id'] = card.id
        data['person_cpf'] = card.person_cpf
        data['card_number'] = card.card_number
        data['card_company'] = card.card_company
        data['limit'] = card.limit
        data['card_statement'] = card.card_statement

        result.append(data)
    return result


'''
Copyright (C) 2015 Andreas Esau
andreasesau@gmail.com

Created by Andreas Esau

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''

import bpy
import bmesh
import json
from bpy.props import FloatProperty, IntProperty, BoolProperty, StringProperty, CollectionProperty, FloatVectorProperty, EnumProperty, IntVectorProperty
from collections import OrderedDict
from .. functions import *
import math
from mathutils import Vector,Matrix, Quaternion, Euler
import shutil

json_data = OrderedDict({
            "info":"Generated with COA Tools",
            "frameRate": 24,
            "name": "Project Name",
            "version": "5.5",
            "compatibleVersion": "5.5",
            "armature": []
            })

armature_data = OrderedDict({
            "type":"Armature",
            "frameRate":24,
            "name":"Armature",
            "aabb":{"width":0,"y":0,"height":0,"x":0},
            "bone":[],
            "slot":[],
            "skin":[{"name":"","slot":[]}],
            "ik":[],
            "animation":[],
            "defaultActions":[{"gotoAndPlay":""}]
           })

animation_data = OrderedDict({
            "duration": 0,
            "playTimes": 0,
            "name": "Anim Name",
            "bone": [],
            "frame": [],
            "slot": [],
            "ffd": [],
            })

skin = OrderedDict({
        "name":"",
        "slot":[],
        })

atlas_data = {}

display = [
            OrderedDict({
                "edges": [],
                "uvs": [],
                "type": "mesh",
                "vertices": [],
                "transform": {
                    "x": -2
                },
                "userEdges": [],
                "width": 480,
                "triangles": [],
                "height": 480,
                "name": "output_file",
                "path": ""
            })
        ]

bone_default_pos = {}
bone_default_rot = {}
vert_coords_default = {}
tex_pathes = {}
img_names = {} ### exported image names
tmp_slots_data = {}

def setup_armature_data(root_bone):
    armature_data = OrderedDict({
                "type":"Armature",
                "frameRate":24,
                "name":"Armature",
                "aabb":{"width":0,"y":0,"height":0,"x":0},
                "bone":[{"name":root_bone.name}],
                "slot":[],
                "skin":[{"name":"","slot":[]}],
                "ik":[],
                "animation":[],
                "defaultActions":[{"gotoAndPlay":""}]
               })
    return armature_data

def setup_json_project(project_name):
    json_data = OrderedDict({
                "info":"Generated with COA Tools",
                "frameRate": 24,
                "name": project_name,
                "version": "5.5",
                "compatibleVersion": "5.5",
                "armature": []
                })
    return json_data

def get_sprite_image_data(sprite_data):
    #mat = sprite.active_material
    mat = sprite_data.materials[0]
    tex = [slot.texture for slot in mat.texture_slots if slot != None and slot.texture.type == "IMAGE"][0]
    img = tex.image if tex.image != None else None

    return mat, tex, img

def copy_textures(self,sprites,texture_dir_path):
    global img_names
    img_names = {}
    for sprite in sprites:
        if sprite.type == "MESH":
            imgs = []
            if sprite.coa_type == "MESH":
                if len(sprite.data.materials) > 0:
                    mat, tex, img = get_sprite_image_data(sprite.data)
                    imgs.append({"img":img,"key":sprite.data.name})
            elif sprite.coa_type == "SLOT":
                for slot in sprite.coa_slot:
                    if len(slot.mesh.materials) > 0:
                        mat, tex, img = get_sprite_image_data(slot.mesh)
                        imgs.append({"img":img,"key":slot.mesh.name})

            for data in imgs:
                img = data["img"]
                key = data["key"]

                src_path = bpy.path.abspath(img.filepath)
                img_name = os.path.basename(src_path)
                dst_path = os.path.join(texture_dir_path,img_name)

                img_names[key] = img_name[:img_name.rfind(".")]

                if self.scene.coa_export_image_mode == "IMAGES":
                    if os.path.isfile(src_path):
                        shutil.copyfile(src_path,dst_path)
                    else:
                        img.save_render(dst_path)

def remove_base_sprite(obj):
    bpy.context.scene.objects.active = obj
    obj.hide = False
    bpy.ops.object.mode_set(mode="EDIT")
    bm = bmesh.from_edit_mesh(obj.data)
    bm.verts.ensure_lookup_table()
    verts = []

    if "coa_base_sprite" in obj.vertex_groups and obj.data.coa_hide_base_sprite:
        v_group_idx = obj.vertex_groups["coa_base_sprite"].index
        for i,vert in enumerate(obj.data.vertices):
            for g in vert.groups:
                if g.group == v_group_idx:
                    verts.append(bm.verts[i])
                    break

    bpy.ops.mesh.reveal()
    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')

    bmesh.ops.delete(bm,geom=verts,context=1)
    bm = bmesh.update_edit_mesh(obj.data)
    bpy.ops.object.mode_set(mode="OBJECT")

##### get mesh data like vertices, edges, triangles and uvs   ##### Start

def get_mixed_vertex_data(obj):
    shapes = obj.data.shape_keys
    verts = []
    index = int(obj.active_shape_key_index)
    shape_key = obj.shape_key_add("tmp_mixed_mesh",from_mix=True)
    for vert in shape_key.data:
        coord = obj.matrix_world * vert.co
        verts.append([vert.co[0],vert.co[1],vert.co[2]])
    obj.shape_key_remove(shape_key)
    obj.active_shape_key_index = index
    return verts

### get vertices information
def convert_vertex_data_to_pixel_space(verts):
    data = []
    for vert in verts:
        for i,coord in enumerate(vert):
            if i in [0,2]:
                multiplier = 1
                if i == 2:
                    multiplier = -1
                value = round(multiplier*coord*100,2)
                data.append(value)
    return data

### get edge information
def get_edge_data(bm,only_outer_edges=True):
    edges = []
    for edge in bm.edges:
            if (edge.is_boundary and only_outer_edges) or (not edge.is_boundary and not only_outer_edges):
                for i,vert in enumerate(edge.verts):
                    edges.append(vert.index)

    return edges

### get triangle information
def get_triangle_data(bm):
    triangles = []
    for face in bm.faces:
        for i,vert in enumerate(face.verts):
            triangles.append(vert.index)
    return triangles

### get mesh vertex corrseponding uv vertex
def uv_from_vert_first(uv_layer, v):
    for l in v.link_loops:
        uv_data = l[uv_layer]
        return uv_data.uv
    return None

### get uv information
def get_uv_data(bm):
    uvs = []
    uv_layer = bm.loops.layers.uv.active
    for vert in bm.verts:
        uv_first = uv_from_vert_first(uv_layer,vert)
        for i,val in enumerate(uv_first):
            if i == 1:
                value = round(-val + 1,5)
                uvs.append(value)
            else:
                value = round(val,5)
                uvs.append(value)
    return uvs

def get_modulate_color(sprite):
    color = sprite.coa_modulate_color
    alpha = sprite.coa_alpha
    color_data = {"rM":int(100*color[0]),"gM":int(100*color[1]),"bM":int(100*color[2]),"aM":int(100*alpha)}
    return color_data

##### get mesh data like vertices, edges, triangles and uvs   ##### End

### get slot data
def get_slot_data(self,sprites):
    slot_data = []
    for sprite in sprites:
        if sprite.type == "MESH":
            slot = OrderedDict()
            slot["name"] = sprite.name
            slot["parent"] = self.sprite_object.name #sprite.parent.name
            slot_data.append(slot)

            if len(sprite.coa_slot) > 0:
                slot["displayIndex"] = sprite.coa_slot_index

            color = get_modulate_color(sprite)
            if color["rM"] != 100 or color["gM"] != 100 or color["bM"] != 100 or color["aM"] != 100:
                slot["color"] = color
    return slot_data

def normalize_weights(obj, armature, threshold):
    for vert in obj.data.vertices:
        weight_total = 0.0
        groups = []
        for group in vert.groups:
            vgroup = obj.vertex_groups[group.group]
            group_name = vgroup.name
            if (group.weight > threshold and armature == None) or (group.weight > threshold and armature != None and group_name in armature.data.bones):
                groups.append(group)

        for group in groups:
            vgroup = obj.vertex_groups[group.group]
            weight_total += group.weight
        for group in groups:
            group.weight = 1*(group.weight/weight_total)

### get skin data
def get_skin_slot(self,sprite,armature,scale,slot_data=None):
    context = bpy.context
    sprite_name = str(sprite.name)
    sprite_data_name = sprite.data.name if slot_data == None else slot_data.name
    ### make a sprite duplicate and make it active
    if slot_data == None:
        sprite_data = sprite.data.copy()
    else:
        sprite_data = slot_data.copy()
    sprite = sprite.copy()
    sprite.data = sprite_data
    self.scene.objects.link(sprite)
    self.scene.objects.active = sprite

    ### normalize weights
    normalize_weights(sprite, armature, 0.0)
    for area in context.screen.areas:
            if area.type == "VIEW_3D":
                for region in area.regions:
                    override = context.copy()
                    override["object"] = sprite
                    override["edit_object"] = sprite
                    #bpy.ops.object.vertex_group_clean(override, group_select_mode='BONE_DEFORM', keep_single=True)
                    bpy.ops.object.vertex_group_clean(override, group_select_mode='ALL', keep_single=True)
    ###

    global tmp_sprites
    tmp_slots_data[sprite_data_name] = {"data":sprite_data,"object":sprite,"name":sprite_data_name}

    ### get sprite material, texture and img data
    mat, tex, img = get_sprite_image_data(sprite_data)
    tex_path = os.path.join(self.scene.coa_project_name+"_texture" , img_names[sprite_data_name])
    tex_pathes[sprite_name] = tex_path
    ### delete basesprite mesh in sprite duplicate
    remove_base_sprite(sprite)

    ### get display data of a sprite
    bpy.ops.object.mode_set(mode="EDIT")
    bm = bmesh.from_edit_mesh(sprite.data)

    ### generate display data dictionary
    display_data = OrderedDict()

    ### get general skin information
    display_data["type"] = "mesh"
    display_data["name"] = sprite_data_name#sprite_name
    if self.scene.coa_export_image_mode == "IMAGES":
        display_data["path"] = img_names[sprite_data_name]

    if self.scene.coa_export_image_mode == "IMAGES":
        display_data["width"] = int(img.size[0])
        display_data["height"] = int(img.size[1])
    elif self.scene.coa_export_image_mode == "ATLAS":
        display_data["width"] = atlas_data[sprite_data_name]["width"]
        display_data["height"] = atlas_data[sprite_data_name]["height"]

    verts = get_mixed_vertex_data(sprite)
    vert_coords_default[sprite_name] = verts
    display_data["vertices"] = convert_vertex_data_to_pixel_space(verts)

    bm = bmesh.from_edit_mesh(sprite.data)
    display_data["userEdges"] = get_edge_data(bm,only_outer_edges=False)
    display_data["edges"] = get_edge_data(bm)
    display_data["triangles"] = get_triangle_data(bm)
    display_data["uvs"] = get_uv_data(bm)

    if armature != None:
        armature.data.pose_position = "REST"
        bpy.context.scene.update()

        weights, bones = get_bone_weight_data(self,sprite,armature)

        display_data["weights"] = weights

        display_data["bonePose"] = []
        for bone in bones:
            mat = get_bone_matrix(armature,bone["bone"],relative=False)
            display_data["bonePose"].append(bone["index"]+1)
            display_data["bonePose"].append(round(mat[0][0],5))
            display_data["bonePose"].append(round(mat[0][1],5))
            display_data["bonePose"].append(round(mat[1][0],5))
            display_data["bonePose"].append(round(mat[1][1],5))
            display_data["bonePose"].append(round(mat[1][3] * scale ,5))#pos x
            display_data["bonePose"].append(round(-mat[0][3] *scale ,5))#pos y
        armature.data.pose_position = "POSE"
        bpy.context.scene.update()

        w = sprite.matrix_local[0][0]
        x = sprite.matrix_local[0][2]
        y = sprite.matrix_local[2][0]
        z = sprite.matrix_local[2][2]
        display_data["slotPose"] = [w,x,y,z, sprite.matrix_local.to_translation()[0]*scale, -sprite.matrix_local.to_translation()[2]*scale]

    bpy.ops.object.mode_set(mode="OBJECT")

    #bpy.data.objects.remove(sprite,do_unlink=True)
    return display_data

def get_skin_data(self,sprites,armature,scale):
    context = bpy.context

    global tex_pathes
    tex_pathes = {}

    ### skin data array
    skin_data = [{"slot":[]}]

    for sprite in sprites:
        ### create slot data
        slot_data = OrderedDict()
        slot_data["name"] = sprite.name
        slot_data["display"] = []

        if sprite.type == "MESH":
            if sprite.coa_type == "MESH":
                data2 = get_skin_slot(self,sprite,armature,scale)
                slot_data["display"].append(data2)
            elif sprite.coa_type == "SLOT":
                for slot in sprite.coa_slot:
                    data2 = get_skin_slot(self,sprite,armature,scale,slot_data=slot.mesh)
                    slot_data["display"].append(data2)

            skin_data[0]["slot"].append(slot_data)
    return skin_data

### get bone data
def create_cleaned_armature_copy(self,armature,sprites):
    context = bpy.context
    if armature != None:
        scene = bpy.context.scene

        armature_data = armature.data.copy()
        armature_copy = armature.copy()
        armature_copy.data = armature_data
        armature_copy.name = "COA_EXPORT_ARMATURE"

        scene.objects.link(armature_copy)

        delete_non_deform_bones(self,armature_copy,sprites)
        create_copy_transform_constraints(self,armature,armature_copy)

        ### store armature rest position. Relevant for later animation calculations
        scale = 1/get_addon_prefs(context).sprite_import_export_scale

        armature_copy.data.pose_position = "REST"
        armature.data.pose_position = "REST"
        bpy.context.scene.frame_set(bpy.context.scene.frame_current)
        for bone in armature_copy.data.bones:
            transformations = {}
            transformations["bone_pos"] = get_bone_pos(armature_copy,bone,scale)
            transformations["bone_rot"] = get_bone_angle(armature_copy,bone)
            transformations["bone_scale"] = get_bone_scale(armature_copy,bone)

            self.armature_restpose[bone.name] = transformations
        armature_copy.data.pose_position = "POSE"
        armature.data.pose_position = "POSE"
        return armature_copy
    return None

def bone_is_deform_bone(self,bone,sprites):
    for sprite in sprites:
        if sprite.type == "MESH":
            init_mesh = sprite.data
            meshes = []
            ### get a list of all meshes in sprite -> slot objects containt multiple meshes
            if sprite.coa_type == "MESH":
                meshes.append(sprite.data)
            elif sprite.coa_type == "SLOT":
                for slot in sprite.coa_slot:
                    meshes.append(slot.mesh)

            ### check all meshes if bone is deforming that mesh
            for mesh in meshes:
                sprite.data = mesh
                if sprite.parent_bone == bone.name:
                    sprite.data = init_mesh
                    return True
                if not bone.name in sprite.vertex_groups:
                    break
                else:
                    v_group = sprite.vertex_groups[bone.name]
                    for vert in sprite.data.vertices:
                        try:
                            weight = v_group.weight(vert.index)
                            if weight > 0:
                                sprite.data = init_mesh
                                return True
                        except:
                            pass
            sprite.data = init_mesh
    return False

### is used for export armature. All non deform bones will be deleted and deform bone animations baked in later process
def delete_non_deform_bones(self,armature,sprites):
    context = bpy.context
    context.scene.objects.active = armature

    bpy.ops.object.mode_set(mode="EDIT")

    for bone in armature.data.bones:
        pbone = armature.pose.bones[bone.name]
        ebone = armature.data.edit_bones[bone.name]

        if pbone.is_in_ik_chain or len(pbone.constraints) > 0:
            ebone.parent = None

        is_deform_bone = bone_is_deform_bone(self,bone,sprites)
        is_driver = bone_is_driver(bone,sprites)
        is_const_target = bone_is_constraint_target(bone,armature)
        has_children = len(bone.children) > 0

        if (not is_deform_bone and is_driver) or (not is_deform_bone and is_const_target) or (not is_deform_bone and not has_children) or (not is_deform_bone and not bone.use_deform):
            armature.data.edit_bones.remove(armature.data.edit_bones[bone.name])

    bpy.ops.object.mode_set(mode="OBJECT")

def get_driver_bone_target(driver):
    targets = []
    for v in driver.driver.variables:
        if v.targets[0].bone_target != "":
            targets.append(v.targets[0].bone_target)
    return targets

def bone_is_constraint_target(bone,armature):
    for pbone in armature.pose.bones:
        for const in pbone.constraints:
            if hasattr(const,"subtarget") and const.subtarget == bone.name:
                return True
    return False

def bone_is_driver(bone,sprites):
    for sprite in sprites:
        if sprite.type == "MESH":
            meshes = []
            if sprite.coa_type == "MESH":
                meshes.append(sprite.data)
            elif sprite.coa_type == "SLOT":
                for slot in sprite.coa_slot:
                    meshes.append(slot.mesh)

            all_bone_targets = []
            if sprite.animation_data != None:
                for driver in sprite.animation_data.drivers:
                    bone_targets = get_driver_bone_target(driver)
                    all_bone_targets += bone_targets
            for mesh in meshes:
                if mesh.animation_data != None:
                    for driver in mesh.animation_data.drivers:
                        bone_targets = get_driver_bone_target(driver)
                        all_bone_targets += bone_targets
                if mesh.shape_keys != None and mesh.shape_keys.animation_data != None:
                    for driver in mesh.shape_keys.animation_data.drivers:
                        bone_targets = get_driver_bone_target(driver)
                        all_bone_targets += bone_targets
                if bone.name in all_bone_targets:
                    return True
    return False

def create_copy_transform_constraints(self,armature_from, armature_to):
    for bone in armature_to.pose.bones:
        for const in bone.constraints:
            bone.constraints.remove(const)
        if bone.name in armature_from.pose.bones:
            const = bone.constraints.new("COPY_TRANSFORMS")
            const.target = armature_from
            const.subtarget = bone.name
            const.target_space = "POSE"
            const.owner_space = "POSE"

def get_bone_matrix(armature,bone,relative=True):
    pose_bone = armature.pose.bones[bone.name]

    m = Matrix() ### inverted posebone origin matrix
    m.row[0] = [0,0,1,0]
    m.row[1] = [1,0,0,0]
    m.row[2] = [0,1,0,0]
    m.row[3] = [0,0,0,1]

    if bone.parent == None:
        mat_bone_space = m * pose_bone.matrix
    else:
        if relative:
            #if bone.use_inherit_rotation and bone.use_inherit_scale:
            mat_bone_space = pose_bone.parent.matrix.inverted() * pose_bone.matrix
        else:
            mat_bone_space = m * pose_bone.matrix

    #### remap matrix
    loc, rot, scale = mat_bone_space.decompose()

    #if not bone.use_inherit_scale:
    #    scale = (m * pose_bone.matrix).decompose()[2]

    loc_mat = Matrix.Translation(loc)

    rot_mat = rot.inverted().to_matrix().to_4x4()

    scale_mat = Matrix()
    scale_mat[0][0] = scale[1]
    scale_mat[1][1] = scale[0]
    scale_mat[2][2] = scale[2]
    mat_bone_space = loc_mat * rot_mat * scale_mat
    return mat_bone_space

def get_bone_pos(armature,bone,scale):
    loc, rot, sca = get_bone_matrix(armature,bone).decompose()

    pos_2d = Vector((loc[1],-loc[0])) * scale # flip x and y and negate x to fit dragonbones coordinate system
    return pos_2d

def get_bone_angle(armature,bone,relative=True):
    loc, rot, scale = get_bone_matrix(armature,bone,relative).decompose()
    rot_x = round(math.degrees(rot.to_euler().x),2)
    rot_y = round(math.degrees(rot.to_euler().y),2)
    rot_z = -round(math.degrees(rot.to_euler().z),2)
#    if get_bone_scale(armature,bone)[0] < 0:
#        rot_z = - rot_z
    return rot_z

def get_bone_scale(armature,bone):
    mat = get_bone_matrix(armature,bone)
    loc, rot, scale = get_bone_matrix(armature,bone).decompose()
#    scale = Vector(((scale[0]),abs(scale[1]),abs(scale[2])))
    return scale

def get_bone_data(self,armature,sprite_object,scale):

    bone_data = []
    bone_data.append(OrderedDict({"name":self.sprite_object.name})) ### append root

    for bone in armature.data.bones:
        data = {}
        data["name"] = bone.name
        data["transform"] = {}

        ### get bone position
        pos = get_bone_pos(armature,bone,scale)
        bone_default_pos[bone.name] = Vector(pos)
        if pos != Vector((0,0)):
            data["transform"]["x"] = pos[0]
            data["transform"]["y"] = pos[1]

        ### get bone angle
        angle = get_bone_angle(armature,bone)
        bone_default_rot[bone.name] = angle
        if angle != 0:
            data["transform"]["skX"] = angle
            data["transform"]["skY"] = angle

        ### get bone scale
        sca = get_bone_scale(armature,bone)
        if sca != Vector((1.0,1.0,1.0)):
            data["transform"]["scX"] = sca[0]
            data["transform"]["scY"] = sca[1]

        if int(bone.use_inherit_rotation) != 1:
            data["inheritRotation"] = int(bone.use_inherit_rotation)
        if int(bone.use_inherit_scale) != 1:
            data["inheritScale"] = int(bone.use_inherit_scale)
        if bone.parent != None:
            data["parent"] = bone.parent.name
        else:
            data["parent"] = sprite_object.name
        data["length"] = int((bone.head - bone.tail).length*scale)

        bone_data.append(data)

    return bone_data

def get_bone_index(self,armature,bone_name):
    armature_bones = []
    for bone in armature.data.bones:
        armature_bones.append(bone)

    for i,bone in enumerate(armature_bones):
        if bone_name == bone.name:
            return i

### get weight data
def get_bone_weight_data(self,obj,armature):
    data = []
    bone_names = []
    bones = []
    for vert in obj.data.vertices:
        groups = []
        for group in vert.groups:
            group_name = obj.vertex_groups[group.group].name
            if group_name in armature.data.bones:
                groups.append({"group":group,"group_name":group_name})

        data.append(len(groups))
        for group in groups:
            b_index = get_bone_index(self,armature,group["group_name"])
            bone_index = b_index+1
            data.append(bone_index)
            bone_weight = round(group["group"].weight,6)
            data.append(bone_weight)

            if group["group_name"] not in bone_names:
                bone_names.append(group["group_name"])

    armature_bones = []
    for bone in armature.data.bones:
        armature_bones.append(bone)

    for i,bone in enumerate(armature_bones):
        if bone.name in bone_names:
            bone = armature.data.bones[bone.name]
            bones.append({"index":i,"bone":bone})
    return data, bones

### Export Animations
### get objs and bones that are keyed on given frame
def bone_key_on_frame(bone,frame,action,type="LOCATION"): ### LOCATION, ROTATION, SCALE, ANY
    type = "."+type.lower()
    if action != None:
        for fcurve in action.fcurves:
            if bone.name in fcurve.data_path and (type in fcurve.data_path or type == ".any"):
                for keyframe in fcurve.keyframe_points:
                    if keyframe.co[0] == frame:
                        return True
    return False

def property_key_on_frame(obj,prop_names,frame,type="PROPERTY"):
    if type == "SHAPEKEY":
        obj = obj.shape_keys

    if obj != None and obj.animation_data != None:
    ### check if property has a key set
        action = obj.animation_data.action
        if action != None:
            for fcurve in action.fcurves:
                for prop_name in prop_names:
                    if prop_name in fcurve.data_path:
                        for keyframe in fcurve.keyframe_points:
                            if keyframe.co[0] == frame:
                                return True
    ### check if property has a bone driver and bone has a key set
        for driver in obj.animation_data.drivers:
            for prop_name in prop_names:
                if prop_name in driver.data_path:
                    for var in driver.driver.variables:
                        armature = var.targets[0].id
                        if armature != None:
                            bone_target = var.targets[0].bone_target
                            if bone_target in armature.data.bones:
                                bone = armature.data.bones[bone_target]
                                pbone = armature.pose.bones[bone_target]
                                key_on_frame = bone_key_on_frame(bone,frame,armature.animation_data.action,type="ANY")
                                if key_on_frame:
                                    return key_on_frame
                                for const in pbone.constraints:
                                    if const.type == "ACTION":
                                        bone = armature.data.bones[const.subtarget] if const.subtarget in armature.data.bones else None
                                        if bone != None:
                                            key_on_frame = bone_key_on_frame(bone,frame,armature.animation_data.action,type="ANY")
                                        if key_on_frame:
                                            return key_on_frame
    return False

def get_animation_data(self,sprite_object,armature,armature_orig):
    context = bpy.context
    scale = 1/get_addon_prefs(context).sprite_import_export_scale
    anims = sprite_object.coa_anim_collections

    animations = []

    for anim_index,anim in enumerate(anims):
        if anim.name not in ["NO ACTION","Restpose"]:
            sprite_object.coa_anim_collections_index = anim_index ### set animation

            anim_data = animation_data.copy()
            anim_data["duration"] = anim.frame_end
            anim_data["playTimes"] = 0
            anim_data["name"] = anim.name
            anim_data["bone"] = []
            anim_data["slot"] = []
            anim_data["zOrder"] = {}
            anim_data["ffd"] = []

            ### append all slots to list
            slot_keyframe_duration = {}
            ffd_keyframe_duration = {}
            ffd_last_frame_values = {}
            for slot in self.sprites:
                if slot.type == "MESH":
                    anim_data["slot"].append({"name":slot.name,"colorFrame":[],"displayFrame":[]})
                    slot_keyframe_duration[slot.name] = {"color_duration":0,"display_duration":0}

                    if slot.coa_type == "MESH":
                        anim_data["ffd"].append({"name":slot.data.name,"slot":slot.name,"frame":[]})
                        ffd_keyframe_duration[slot.data.name] = {"ffd_duration":0}
                        ffd_last_frame_values[slot.data.name] = None
                    elif slot.coa_type == "SLOT":
                        for slot2 in slot.coa_slot:
                            anim_data["ffd"].append({"name":slot2.mesh.name,"slot":slot.name,"frame":[]})
                            ffd_keyframe_duration[slot2.mesh.name] = {"ffd_duration":0}
                            ffd_last_frame_values[slot2.mesh.name] = None

            ### check if slot has animation data. if so, store for later usage
            SHAPEKEY_ANIMATION = {}
            for i in range(anim.frame_end+1):
                frame = anim.frame_end-i
                for slot in self.sprites:
                    if slot.type == "MESH":

                        slot_data = []
                        if slot.coa_type == "MESH":
                            slot_data = [tmp_slots_data[slot.data.name]]
                        elif slot.coa_type == "SLOT":
                            for slot2 in slot.coa_slot:
                                slot_data.append(tmp_slots_data[slot2.mesh.name])

                for item in slot_data:
                    data = item["data"]
                    data_name = item["name"]

                    key_blocks = []
                    if data.shape_keys != None:
                        for key in data.shape_keys.key_blocks:
                            key_blocks.append(key.name)
                    if property_key_on_frame(data,key_blocks,frame,type="SHAPEKEY"):
                        SHAPEKEY_ANIMATION[slot.name] = True
                        break

            ### append all bones to list
            bone_keyframe_duration = {}
            if armature != None:
                for bone in armature.data.bones:
                    anim_data["bone"].append({"name":bone.name,"translateFrame":[],"rotateFrame":[],"scaleFrame":[]})
                    bone_keyframe_duration[bone.name] = {"scale_duration":0,"rot_duration":0,"pos_duration":0,"last_scale":None, "last_rot":None, "last_pos":None}

            for i in range(anim.frame_end+1):
                frame = anim.frame_end-i
                context.scene.frame_set(frame)

                #### HANDLE SLOT ANIMATION
                j = 0
                for slot in self.sprites:
                    if slot.type == "MESH":
                        slot_keyframe_duration[slot.name]["color_duration"] += 1
                        slot_keyframe_duration[slot.name]["display_duration"] += 1

                        if property_key_on_frame(slot,["coa_alpha","coa_modulate_color"],frame):

                            keyframe_data = {}
                            keyframe_data["duration"] = slot_keyframe_duration[slot.name]["color_duration"]
                            keyframe_data["tweenEasing"] = 0
                            keyframe_data["value"] = get_modulate_color(slot)

                            anim_data["slot"][j]["colorFrame"].insert(0,keyframe_data)
                            slot_keyframe_duration[slot.name]["color_duration"] = 0

                        if property_key_on_frame(slot,["coa_slot_index"],frame) or frame in [0,anim.frame_end]:
                            keyframe_data = {}
                            keyframe_data["duration"] = slot_keyframe_duration[slot.name]["display_duration"]
                            keyframe_data["value"] = slot.coa_slot_index

                            anim_data["slot"][j]["displayFrame"].insert(0,keyframe_data)
                            slot_keyframe_duration[slot.name]["display_duration"] = 0

                        j += 1
                #### HANDLE BONE ANIMATION
                if armature != None:
                    for j,bone in enumerate(armature.data.bones):
                        bone_orig = armature_orig.data.bones[bone.name]
                        pose_bone_orig = armature_orig.pose.bones[bone.name]
                        const_len = len(pose_bone_orig.constraints)
                        in_ik_chain = pose_bone_orig.is_in_ik_chain

                        bone_keyframe_duration[bone.name]["scale_duration"] += 1
                        bone_keyframe_duration[bone.name]["rot_duration"] += 1
                        bone_keyframe_duration[bone.name]["pos_duration"] += 1

                        bake_anim = self.scene.coa_export_bake_anim and frame%self.scene.coa_export_bake_steps==0

                        ### bone position
                        if bone_key_on_frame(bone_orig,frame,armature_orig.animation_data.action,type="LOCATION") or frame in [0,anim.frame_end] or const_len > 0 or in_ik_chain or bake_anim:
                            bone_pos = get_bone_pos(armature,bone,scale) - self.armature_restpose[bone.name]["bone_pos"]

                            keyframe_data = {}
                            keyframe_data["duration"] = bone_keyframe_duration[bone.name]["pos_duration"]
                            keyframe_data["curve"] = [.5,0,.5,1] if bake_anim == False else [0,0,1,1]
                            keyframe_data["x"] = round(bone_pos[0],2)
                            keyframe_data["y"] = round(bone_pos[1],2)

                            if (frame in [0,anim.frame_end]) or (bone_keyframe_duration[bone.name]["last_pos"] != [keyframe_data["x"], keyframe_data["y"]]):

                                ### if previous keyframe differs and keyframe duration is greater 1 add an extra keyframe inbetween
                                if bone_keyframe_duration[bone.name]["pos_duration"] > 1 and (bone_keyframe_duration[bone.name]["last_pos"] != [keyframe_data["x"], keyframe_data["y"]]):
                                    keyframe_data_last = {}
                                    keyframe_data_last["duration"] = bone_keyframe_duration[bone.name]["pos_duration"]-1
                                    keyframe_data_last["curve"] = [.5,0,.5,1] if bake_anim == False else [0,0,1,1]
                                    keyframe_data_last["x"] = bone_keyframe_duration[bone.name]["last_pos"][0]
                                    keyframe_data_last["y"] = bone_keyframe_duration[bone.name]["last_pos"][1]
                                    anim_data["bone"][j]["translateFrame"].insert(0,keyframe_data_last)

                                    keyframe_data["duration"] = 1

                                anim_data["bone"][j]["translateFrame"].insert(0,keyframe_data)
                                bone_keyframe_duration[bone.name]["pos_duration"] = 0
                                bone_keyframe_duration[bone.name]["last_pos"] = [keyframe_data["x"], keyframe_data["y"]]

                        ### bone rotation
                        if bone_key_on_frame(bone_orig,frame,armature_orig.animation_data.action,type="ROTATION") or frame in [0,anim.frame_end] or const_len > 0 or in_ik_chain or bake_anim:
                            bone_rot = get_bone_angle(armature,bone) - self.armature_restpose[bone.name]["bone_rot"]

                            keyframe_data = {}
                            keyframe_data["duration"] = bone_keyframe_duration[bone.name]["rot_duration"]
                            keyframe_data["curve"] = [.5,0,.5,1] if bake_anim == False else [0,0,1,1]
                            keyframe_data["rotate"] = bone_rot

                            if (frame in [0,anim.frame_end]) or (bone_keyframe_duration[bone.name]["last_rot"] != keyframe_data["rotate"]):

                                ### if previous keyframe differs and keyframe duration is greater 1 add an extra keyframe inbetween
                                if bone_keyframe_duration[bone.name]["rot_duration"] > 1 and (bone_keyframe_duration[bone.name]["last_rot"] != keyframe_data["rotate"]):
                                    keyframe_data_last = {}
                                    keyframe_data_last["duration"] = bone_keyframe_duration[bone.name]["rot_duration"]-1
                                    keyframe_data_last["curve"] = [.5,0,.5,1] if bake_anim == False else [0,0,1,1]
                                    keyframe_data_last["rotate"] = bone_keyframe_duration[bone.name]["last_rot"]
                                    anim_data["bone"][j]["rotateFrame"].insert(0,keyframe_data_last)

                                    keyframe_data["duration"] = 1

                                anim_data["bone"][j]["rotateFrame"].insert(0,keyframe_data)
                                bone_keyframe_duration[bone.name]["rot_duration"] = 0
                                bone_keyframe_duration[bone.name]["last_rot"] = keyframe_data["rotate"]

                        ### bone scale
                        if bone_key_on_frame(bone_orig,frame,armature_orig.animation_data.action,type="SCALE") or frame in [0,anim.frame_end] or const_len > 0 or in_ik_chain or bake_anim:

                            bone_scale = get_bone_scale(armature,bone)

                            keyframe_data = {}
                            keyframe_data["duration"] = bone_keyframe_duration[bone.name]["scale_duration"]
                            keyframe_data["curve"] = [.5,0,.5,1] if bake_anim == False else [0,0,1,1]
                            keyframe_data["x"] = round(bone_scale[0],2)
                            keyframe_data["y"] = round(bone_scale[1],2)

                            if (frame in [0,anim.frame_end]) or (bone_keyframe_duration[bone.name]["last_scale"] != [keyframe_data["x"], keyframe_data["y"]]):

                                ### if previous keyframe differs and keyframe duration is greater 1 add an extra keyframe inbetween
                                if bone_keyframe_duration[bone.name]["scale_duration"] > 1 and (bone_keyframe_duration[bone.name]["last_scale"] != [keyframe_data["x"], keyframe_data["y"]]):
                                    keyframe_data_last = {}
                                    keyframe_data_last["duration"] = bone_keyframe_duration[bone.name]["scale_duration"]-1
                                    keyframe_data_last["curve"] = [.5,0,.5,1] if bake_anim == False else [0,0,1,1]
                                    keyframe_data_last["x"] = bone_keyframe_duration[bone.name]["last_scale"][0]
                                    keyframe_data_last["y"] = bone_keyframe_duration[bone.name]["last_scale"][1]
                                    anim_data["bone"][j]["scaleFrame"].insert(0,keyframe_data_last)

                                    keyframe_data["duration"] = 1

#                                if bone_keyframe_duration[bone.name]["last_scale"] != None and ((bone_keyframe_duration[bone.name]["last_scale"][1] < 0 and keyframe_data["y"] > 0) or (bone_keyframe_duration[bone.name]["last_scale"][1] > 0 and keyframe_data["y"] < 0)):
#                                    keyframe_data["curve"] = []

                                anim_data["bone"][j]["scaleFrame"].insert(0,keyframe_data)
                                bone_keyframe_duration[bone.name]["scale_duration"] = 0
                                bone_keyframe_duration[bone.name]["last_scale"] = [keyframe_data["x"], keyframe_data["y"]]

                #### HANDLE FFD Transformations (Blender Shapekeys)
                j = 0
                for slot in self.sprites:
                    if slot.type == "MESH":

                        slot_data = []
                        if slot.coa_type == "MESH":
                            slot_data = [tmp_slots_data[slot.data.name]]
                        elif slot.coa_type == "SLOT":
                            for slot2 in slot.coa_slot:
                                slot_data.append(tmp_slots_data[slot2.mesh.name])

                        for item in slot_data:
                            data = item["data"]
                            data_name = item["name"]

                            bake_anim = self.scene.coa_export_bake_anim and frame%self.scene.coa_export_bake_steps==0

                            if data.shape_keys != None:
                                ffd_keyframe_duration[data_name]["ffd_duration"] += 1

                                key_blocks = []
                                for key in data.shape_keys.key_blocks:
                                    key_blocks.append(key.name)
                                if property_key_on_frame(data,key_blocks,frame,type="SHAPEKEY") or (frame in [0,anim.frame_end] and data_name in SHAPEKEY_ANIMATION) or bake_anim:
                                    ffd_data = {}
                                    ffd_data["duration"] = ffd_keyframe_duration[data_name]["ffd_duration"]
                                    ffd_data["curve"] = [.5,0,.5,1] if bake_anim == False else [0,0,1,1]
#                                    if bake_anim == False:
#                                        ffd_data["curve"] = [.5,0,.5,1]
#                                    else:
#                                        ffd_data["tweenEasing"] = 0

                                    verts = get_mixed_vertex_data(item["object"])
                                    verts_relative = []
                                    for i,co in enumerate(verts):
                                        verts_relative.append(Vector(co) - Vector(vert_coords_default[slot.name][i]))

                                    ffd_data["vertices"] = convert_vertex_data_to_pixel_space(verts_relative)

                                    if (frame in [0, anim.frame_end]) or (ffd_last_frame_values[data_name] != ffd_data["vertices"]):
                                        ### if previous keyframe differs and keyframe duration is greater 1 add an extra keyframe inbetween
                                        if ffd_data["duration"] > 1 and (ffd_last_frame_values[data_name] != ffd_data["vertices"]):
                                            ffd_data_last = {}
                                            ffd_data_last["duration"] = ffd_keyframe_duration[data_name]["ffd_duration"]-1
                                            ffd_data_last["curve"] = [.5,0,.5,1]
                                            ffd_data_last["vertices"] = ffd_last_frame_values[data_name]

                                            anim_data["ffd"][j]["frame"].insert(0,ffd_data_last)
                                            ffd_data["duration"] = 1

                                        anim_data["ffd"][j]["frame"].insert(0,ffd_data)
                                        ffd_keyframe_duration[data_name]["ffd_duration"] = 0
                                        ffd_last_frame_values[data_name] = ffd_data["vertices"]
                            j += 1

            ### cleanup animation data
            delete_keys = []
            for key in anim_data:
                data = anim_data[key]
                if type(data) == list:
                    if len(data) == 0:
                        delete_keys.append(key)
                    else:
                        for item in data:
                            delete_keys2 = []
                            if type(item) == dict:
                                for key2 in item:
                                    data2 = item[key2]
                                    if type(data2) == list:
                                        if len(data2) == 0:
                                            #del item[key2]
                                            delete_keys2.append(key2)
                            for key2 in delete_keys2:
                                del item[key2]
            for key in delete_keys:
                del anim_data[key]

            animations.append(anim_data)
    return animations

class DragonBonesExport(bpy.types.Operator):
    bl_idname = "coa_tools.export_dragon_bones"
    bl_label = "Dragonbones Export"
    bl_description = ""
    bl_options = {"REGISTER"}

    reduce_size = BoolProperty(default=False)

    sprite_object = None
    armature = None
    sprites = None
    scale = 0.0

    armature_restpose = {}

    json_data = {}

    @classmethod
    def poll(cls, context):
        return True

    def draw(self,context):
        layout = self.layout
        col = layout.column()
        col.prop(self,"bake_anim",text="Bake Animation")
        if self.bake_anim:
            col.prop(self,"bake_interval",text="Bake Interval")
        col.prop(self,"reduce_size",text="Reduce Export Size")

        if self.generate_atlas:
            box = col.box()
        else:
            box = col
        box.prop(self,"generate_atlas",text="Generate Texture Atlas")
        if self.generate_atlas:
            row = box.row()
            #row.prop(self,"atlas_size",text="Atlas Size",expand=True)
            if self.atlas_size == "MANUAL":
                row = box.row()
                row.prop(self,"atlas_dimension",text="")
            row = box.row()
            row.prop(self,"unwrap_method",text="Unwrap Method",expand=True)
            row = box.row()
            row.prop(self,"island_margin",text="Sprite Margin")

    def get_init_state(self,context):
        self.selected_objects = context.selected_objects[:]
        self.active_object = context.active_object
        self.sprite_object = get_sprite_object(context.active_object)

        self.animation_index = self.sprite_object.coa_anim_collections_index
        self.frame_current = context.scene.frame_current

    def set_init_state(self,context):
        for obj in context.scene.objects:
            if obj in self.selected_objects:
                obj.select = True
            else:
                obj.select = False
        context.scene.objects.active = self.active_object

        if len(self.sprite_object.coa_anim_collections) > 0:
            self.sprite_object.coa_anim_collections_index = self.animation_index
        context.scene.frame_current = self.frame_current

    def execute(self, context):
        global tmp_slots_data
        tmp_slots_data = {}

        self.get_init_state(context)
        self.scene = context.scene

        ### set animation mode to action
        coa_nla_mode = str(self.scene.coa_nla_mode)
        self.scene.coa_nla_mode = "ACTION"

        ### get sprite object, sprites and armature
        self.scale = 1/get_addon_prefs(context).sprite_import_export_scale
        self.sprite_object = get_sprite_object(context.active_object)
        self.armature_orig = get_armature(self.sprite_object)

        self.sprites = get_children(context,self.sprite_object,[])
        self.sprites = sorted(self.sprites, key=lambda obj: obj.location[1], reverse=True) ### sort objects based on the z depth. needed for draw order

        self.armature = create_cleaned_armature_copy(self,self.armature_orig,self.sprites) ### create a cleaned copy of the armature that contains only deform bones and which has applied copy transform constraints

        ### get export, project and json path
        export_path = bpy.path.abspath(self.scene.coa_export_path)
        texture_dir_path = os.path.join(export_path , self.scene.coa_project_name+"_texture")
        json_path = os.path.join(export_path,self.scene.coa_project_name+"_ske.json")

        ### check if export dir exists
        if not os.path.exists(export_path):
            self.report({"WARNING"},"Export Path does not exists. Set a valid Export Path.")
            return{'CANCELLED'}

        ### export texture atlas
        if self.scene.coa_export_image_mode == "ATLAS":
            sprites = [sprite for sprite in self.sprites if sprite.type == "MESH"]
            generate_texture_atlas(self, sprites, self.scene.coa_project_name, export_path, img_width=self.scene.coa_atlas_resolution_x, img_height=self.scene.coa_atlas_resolution_y)

        ### create texture directory
        if self.scene.coa_export_image_mode == "IMAGES":
            if os.path.exists(texture_dir_path):
                shutil.rmtree(texture_dir_path)
            os.makedirs(texture_dir_path)

        ### copy all textures to texture directory
        copy_textures(self,self.sprites,texture_dir_path)

        ### create json data
        if self.armature != None:
            self.armature.data.pose_position = "REST"
            self.armature_orig.data.pose_position = "REST"

        self.json_data = setup_json_project(self.scene.coa_project_name) ### create base template
        self.json_data["armature"] =  [setup_armature_data(self.sprite_object)] ### create base armature
        self.json_data["armature"][0]["slot"] = get_slot_data(self,self.sprites)
        self.json_data["armature"][0]["skin"] = get_skin_data(self,self.sprites,self.armature,self.scale)
        self.json_data["armature"][0]["bone"] = get_bone_data(self,self.armature,self.sprite_object,self.scale) if self.armature != None else [{"name":self.sprite_object.name}]

        if self.armature != None:
            self.armature.data.pose_position = "POSE"
            self.armature_orig.data.pose_position = "POSE"
        self.json_data["armature"][0]["animation"] = get_animation_data(self,self.sprite_object,self.armature,self.armature_orig)

        ### write and store json file
        self.reduce_size = True
        if self.reduce_size:
            json_file = json.dumps(self.json_data,separators=(',',':'))
        else:
            json_file = json.dumps(self.json_data, indent="\t", sort_keys=False)

        text_file = open(json_path, "w")
        text_file.write(json_file)
        text_file.close()

        ### cleanup scene
        self.set_init_state(context) ### restore initial object selection
        if self.armature != None:
            bpy.data.objects.remove(self.armature) ### delete copied armature

        for key in tmp_slots_data:
            bpy.data.objects.remove(tmp_slots_data[key]["object"],do_unlink=True)

        self.scene.coa_nla_mode = coa_nla_mode

        self.report({"INFO"},"Export successful.")
        return {"FINISHED"}

class DragonbonesExportPanel(bpy.types.Panel):
    bl_idname = "dragonbones_export_panel"
    bl_label = "Dragonbones Export Panel"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "render"

    bpy.types.Scene.coa_project_name = bpy.props.StringProperty(default="New Project", name="Project name")
    bpy.types.Scene.coa_export_path = bpy.props.StringProperty(default="", name="Export Path",subtype="DIR_PATH")
    bpy.types.Scene.coa_export_image_mode = bpy.props.EnumProperty(default="ATLAS", name="Image Mode",items=(("ATLAS","Atlas","Atlas"),("IMAGES","Images","Images")))
    bpy.types.Scene.coa_atlas_resolution_x = bpy.props.IntProperty(default=1024,name="Resolution X",min=8,subtype="PIXEL")
    bpy.types.Scene.coa_atlas_resolution_y = bpy.props.IntProperty(default=1024, name="Resolution Y",min=8,subtype="PIXEL")
    bpy.types.Scene.coa_export_bake_anim = bpy.props.BoolProperty(default=False, name="Bake Animation")
    bpy.types.Scene.coa_export_bake_steps = bpy.props.IntProperty(default=1, min=1, name="Bake Steps",description="Set key every x Frame.")

    def draw(self, context):

        layout = self.layout
        self.scene = context.scene

        col = layout.column()
        col.prop(self.scene,"coa_project_name",text="Project Name")
        col.prop(self.scene,"coa_export_path",text="Export Path")

        row = col.row()
        row.prop(self.scene,"coa_export_image_mode",expand=True)
        if self.scene.coa_export_image_mode == "ATLAS":
            subcol = col.column(align=True)
            subcol.label(text="Texture Atlas Size:")
            subcol.prop(self.scene,"coa_atlas_resolution_x",text="X")
            subcol.prop(self.scene,"coa_atlas_resolution_y",text="Y")
        col.prop(self.scene,"coa_export_bake_anim")
        if self.scene.coa_export_bake_anim:
            col.prop(self.scene,"coa_export_bake_steps")
        op = col.operator("coa_tools.export_dragon_bones")

def generate_texture_atlas(self, sprites, atlas_name, img_path, img_width=512, img_height=1024):
    global atlas_data
    atlas_data = {}

    context = bpy.context

    ### deselect all objects
    for obj in context.scene.objects:
        obj.select = False

    ### get a list of all sprites and containing slots
    slots = []
    for sprite in sprites:
        if sprite.type == "MESH":
            if sprite.coa_type == "MESH":
                slots.append({"sprite":sprite,"slot":sprite.data})
            elif sprite.coa_type == "SLOT":
                for i,slot in enumerate(sprite.coa_slot):
                    slots.append({"sprite":sprite,"slot":slot.mesh})

    ### loop over all slots and create an object with slot assigned
    for slot in slots:
        dupli_sprite = slot["sprite"].copy()
        dupli_sprite.data = slot["slot"].copy()
        context.scene.objects.link(dupli_sprite)
        dupli_sprite.hide = False
        dupli_sprite.select = True
        context.scene.objects.active = dupli_sprite

        ### delete shapekeys
        if dupli_sprite.data.shape_keys != None:
            shapekeys = dupli_sprite.data.shape_keys.key_blocks
            for i in range(len(shapekeys)):
                shapekeys = dupli_sprite.data.shape_keys.key_blocks
                dupli_sprite.shape_key_remove(shapekeys[len(shapekeys)-1])
        ### apply/delete modifieres
        for modifier in dupli_sprite.modifiers:
            if modifier.name == "coa_base_sprite" and modifier.type == "MASK":
                if len(dupli_sprite.data.vertices) > 4:
                    modifier.invert_vertex_group = False
                    override = bpy.context.copy()
                    override["object"] = dupli_sprite
                    override["active_object"] = dupli_sprite
                    bpy.ops.object.modifier_apply(override, apply_as="DATA", modifier=modifier.name)
        for modifier in dupli_sprite.modifiers:
            dupli_sprite.modifiers.remove(modifier)

        ### delete vertex_groups
        for group in dupli_sprite.vertex_groups:
            dupli_sprite.vertex_groups.remove(group)

        ### assign mesh as vertex group
        dupli_sprite.vertex_groups.new(name=slot["slot"].name)
        bpy.ops.object.mode_set(mode="EDIT")
        bpy.ops.mesh.reveal()
        bpy.ops.mesh.select_all(action='SELECT')
        for area in context.screen.areas:
            if area.type == "VIEW_3D":
                for region in area.regions:
                    if region.type == "WINDOW":
                        override = context.copy()
                        override["area"] = area
                        override["edit_object"] = dupli_sprite
                        override["active_object"] = dupli_sprite
                        override["object"] = dupli_sprite
                        override["region"] = region
                        bpy.ops.object.vertex_group_assign(override)
                        break

        bpy.ops.object.mode_set(mode="OBJECT")

    ### join all slots into one
    bpy.ops.object.join()

    ### rename new object
    tex_atlas_obj = context.active_object
    tex_atlas_obj.name = "coa_tex_atlas"

    ### create atlas image
    img_atlas = bpy.data.images.new(atlas_name, width=img_width, height=img_height)
    img_atlas.generated_color = [0,0,0,0]

    ### assign img atlas to uvs
    for v in tex_atlas_obj.data.uv_textures.active.data:
        v.image = img_atlas

    ### create new uv map atlas
    coa_uv_atlas = tex_atlas_obj.data.uv_textures.new(name="COA_UV_ATLAS")
    coa_uv_atlas = tex_atlas_obj.data.uv_textures[coa_uv_atlas.name]
    tex_atlas_obj.data.uv_textures.active = coa_uv_atlas
    tex_atlas_obj.hide_render = False

    ### generate one texture atlas for all sprites
    bpy.ops.object.mode_set(mode="EDIT")
    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.mesh.reveal()

    for area in context.screen.areas:
        if area.type == "VIEW_3D":
            for region in area.regions:
                if region.type == "WINDOW":
                    override = context.copy()
                    override["area"] = area
                    override["edit_object"] = context.edit_object
                    override["region"] = region
                    bpy.ops.uv.project_from_view(override, camera_bounds=False, correct_aspect=True, scale_to_bounds=False)
                    break

    bpy.ops.uv.select_all(action='SELECT')
    for area in context.screen.areas:
        if area.type == "VIEW_3D":
            for region in area.regions:
                if region.type == "WINDOW":
                    override = context.copy()
                    override["area"] = area
                    override["edit_object"] = context.edit_object
                    override["region"] = region
                    bpy.ops.uv.pack_islands(rotate=False, margin=.01)
                    break
    bpy.ops.object.mode_set(mode="OBJECT")

    ### assign img atlas to uvs
    for v in tex_atlas_obj.data.uv_textures.active.data:
        v.image = img_atlas
    ### bake uv atlas
    context.scene.render.bake_type = "TEXTURE"
    context.scene.render.use_bake_selected_to_active = False
    context.scene.render.use_bake_clear = False
    context.scene.render.bake_margin = 0

    bpy.ops.object.bake_image()

    ### get uv coordinates
    bpy.ops.object.mode_set(mode="EDIT")

    bm = bmesh.from_edit_mesh(tex_atlas_obj.data)
    uv_layer = bm.loops.layers.uv["COA_UV_ATLAS"]

    sprite_data = []

    for group in tex_atlas_obj.vertex_groups:
        for area in context.screen.areas:
            if area.type == "VIEW_3D":
                for region in area.regions:
                    if region.type == "WINDOW":
                        override = context.copy()
                        override["area"] = area
                        override["edit_object"] = tex_atlas_obj
                        override["active_object"] = tex_atlas_obj
                        override["object"] = tex_atlas_obj
                        override["region"] = region
                        bpy.ops.object.vertex_group_set_active(override, group=group.name)
                        bpy.ops.mesh.select_all(action='DESELECT')
                        bpy.ops.object.vertex_group_select(override)
                        break
        bmesh.update_edit_mesh(tex_atlas_obj.data)
        x = 1.0
        y = 1.0
        width = 0.0
        height = 0.0
        for vert in bm.verts:
            if vert.select:
                uv = uv_from_vert_first(uv_layer,vert)
                x = min(uv[0],x)
                y = min(1-uv[1],y)
                width = max(uv[0],width)
                height = max(1-uv[1],height)
        width = width - x
        height = height - y

        x_px = int(img_width*x)
        y_px = int(img_height*y)
        width_px = abs(int(img_width*width))
        height_px = abs(int(img_height*height))

        sprite = {}
        sprite["name"] = group.name
        sprite["x"] = x_px
        sprite["y"] = y_px
        sprite["width"] = width_px
        sprite["height"] = height_px
        sprite_data.append(sprite)
        atlas_data[group.name] = {"width":width_px,"height":height_px}

    bpy.ops.object.mode_set(mode="OBJECT")
    ### collect sprite atlas data
    texture_atlas = {}
    texture_atlas["width"] = img_width
    texture_atlas["height"] = img_height
    texture_atlas["imagePath"] = atlas_name + "_tex.png"
    texture_atlas["name"] = self.scene.coa_project_name
    texture_atlas["SubTexture"] = sprite_data

    if self.reduce_size:
        json_file = json.dumps(texture_atlas,separators=(',',':'))
    else:
        json_file = json.dumps(texture_atlas, indent="\t", sort_keys=False)

    json_path = os.path.join(img_path,atlas_name+"_tex.json")
    text_file = open(json_path, "w")
    text_file.write(json_file)
    text_file.close()

    texture_path = os.path.join(img_path,atlas_name+"_tex.png")
    img_atlas.save_render(texture_path)

    bpy.data.objects.remove(tex_atlas_obj, do_unlink=True)


# -*- coding: utf-8 -*-
"""
Created on Fri Mar  6 14:03:49 2020

@author: Richie Bao-Chicago.IIT(driverless city project)
data:IIT(driverless city project)
reference:https://github.com/d2l-ai/d2l-zh  《动手学深度学习》(PyTorch版)
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
import scipy.io as sio
import math
from decimal import *
from shapely.geometry import Point,MultiPoint
import networkx as nx
import operator
import torch
from sklearn import preprocessing
from tqdm import tqdm
import pickle

getcontext().prec = 28
np.set_printoptions(precision=28)

'''LandmarkMap 读取MatLab的.fig数据'''
LandmarkMap_fn=r"F:\data_02_Chicago\data_driverless City\IIT_data\LandmarkMap.fig"
def readMatLabFig_LandmarkMap(LandmarkMap_fn):
    LandmarkMap=loadmat(LandmarkMap_fn, squeeze_me=True, struct_as_record=False)
    y=loadmat(LandmarkMap_fn)
    print(sorted(LandmarkMap.keys()))

    LandmarkMap_dic={} #提取.fig值
    for object_idx in range(LandmarkMap['hgS_070000'].children.children.shape[0]):
        # print(object_idx)
        try:
            X=LandmarkMap['hgS_070000'].children.children[object_idx].properties.XData #good
            Y=LandmarkMap['hgS_070000'].children.children[object_idx].properties.YData
            LandmarkMap_dic[object_idx]=(X,Y)
        except:
            pass

    # print(LandmarkMap_dic)
    fig= plt.figure(figsize=(20,130))
    colors=['#7f7f7f','#d62728','#1f77b4','','','']
    markers=['.','+','o','','','']
    dotSizes=[200,3000,3000,0,0,0]
    linewidths=[2,10,10,0,0,0]
    i=0
    for key in LandmarkMap_dic.keys():
        plt.scatter(LandmarkMap_dic[key][0], LandmarkMap_dic[key][1],s=dotSizes[i],marker=markers[i], color=colors[i],linewidth=linewidths[i])
        i+=1
    plt.tick_params(axis='both',labelsize=80)
    plt.show()
    return LandmarkMap_dic

''''PHmi读取激光雷达导航评估值'''
PHMI_fn=r"F:\data_02_Chicago\data_driverless City\IIT_data\PHMI.fig"
def readMatLabFig_PHMI(PHMI_fn,LandmarkMap_dic):
    PHMI=loadmat(PHMI_fn, squeeze_me=True, struct_as_record=False)
    x=loadmat(PHMI_fn)
    print(sorted(PHMI.keys()))

    PHMI_dic={} #提取MatLab的.fig值
    for object_idx in range(PHMI['hgS_070000'].children.children.shape[0]):
        # print(object_idx)
        try:
            X=PHMI['hgS_070000'].children.children[object_idx].properties.XData #good
            Y=PHMI['hgS_070000'].children.children[object_idx].properties.YData
            Z=PHMI['hgS_070000'].children.children[object_idx].properties.ZData
            PHMI_dic[object_idx]=(X,Y,Z)
        except:
            pass

    # print(PHMI2_dic)
    fig= plt.figure(figsize=(20,130)) #figsize=(20,130)
    colors=['#7f7f7f','#d62728','#1f77b4','','','']
    markers=['.','+','o','','','']
    dotSizes=[200,3000,3000,0,0,0]
    linewidths=[2,10,10,0,0,0]

    ScalePhmi=math.pow(10,1)

    plt.plot(PHMI_dic[0][0], PHMI_dic[0][1],marker=markers[0], color=colors[0],linewidth=linewidths[0])

    ref=math.pow(10,-5)

    #for display clearly
    PHmiValue=PHMI_dic[1][2]
    # print(PHmiValue)
    # replaceValue=np.extract(PHmiValue<ref,PHmiValue)*-math.pow(10,5)
    # print(replaceValue)
    # PHmiValue[PHmiValue<ref]=replaceValue
    plt.plot(PHmiValue*ScalePhmi, PHMI_dic[0][1],marker=markers[0], color=colors[1],linewidth=1)

    # plt.plot(PHMI_dic[1][2]*ScalePhmi, PHMI_dic[0][1],marker=markers[0], color=colors[1],linewidth=1)
    plt.axvline(x=ref*ScalePhmi)

    plt.scatter(LandmarkMap_dic[1][0], LandmarkMap_dic[1][1],marker=markers[1], s=dotSizes[1],color=colors[2],linewidth=10)

    plt.tick_params(axis='both',labelsize=80)
    plt.show()

    return PHMI_dic

'''车载激光雷达导航的扫描区域影响landmarks提取 scanning circle buffer, extract the landmarks in the buffer at earch location'''
def scanCircleBuffer(locations,landmarks,radius):
    landmarks_pts=[Point(coordi[0],coordi[1]) for coordi in np.stack((landmarks[0], landmarks[1]), axis=-1)]
    # print(len(landmarks_pts))
    # print(landmarks_pts)
    locations_pts=[Point(coordi[0],coordi[1]) for coordi in np.stack((locations[0], locations[1]), axis=-1)]
    # print(len(locations_pts))
    scanCircleBuffer=[pt.buffer(radius) for pt in locations_pts]
    # print(scanCircleBuffer)
    targetPts={} #landmarks特征点
    targetPts_idx={} #建立location位置与其扫描范围内landmarks位置坐标的对应索引值
    for locBuffer_idx in range(len(scanCircleBuffer)):
        temp=[]
        temp_idx=[]
        for LM_idx in range(len(landmarks_pts)):
           if scanCircleBuffer[locBuffer_idx].contains(landmarks_pts[LM_idx]):
               temp.append(landmarks_pts[LM_idx])
               temp_idx.append(LM_idx)
        targetPts[locBuffer_idx]=temp
        targetPts_idx[locBuffer_idx]=temp_idx
    print("locations points:%d, target points:%d"%(len(locations_pts),len(targetPts.keys())))
    return targetPts,locations_pts,targetPts_idx

#展平函数
flatten_lst=lambda lst: [m for n_lst in lst for m in flatten_lst(n_lst)] if type(lst) is list else [lst]

'''#建立无人车位置点及对应扫描区范围特征点的网络结构，使用Networkx库 build the location network'''
def location_landmarks_network(targetPts_idx,locations,landmarks):
    LMs=np.stack((landmarks[0], landmarks[1]), axis=-1)
    LCs=np.stack((locations[0], locations[1]), axis=-1)
    ptsMerge=np.vstack((LMs,LCs))
    print("LMs shape:%s, LCs shaoe:%s, ptsMerge shape:%s"%(LMs.shape, LCs.shape,ptsMerge.shape))
    targetPts_idx_adj={}
    for key in targetPts_idx.keys():
        targetPts_idx_adj[key+LMs.shape[0]]=targetPts_idx[key]
    edges=[[(key,i) for i in targetPts_idx_adj[key]] for key in targetPts_idx_adj.keys()]
    edges=flatten_lst(edges)

    G=nx.Graph()
    G.position={}
    # G.targetPtsNum={}
    i=0
    for pts in ptsMerge:
        G.add_node(i)
        G.position[i]=(pts[0],pts[1])
        # G.targetPtsNum[LM]=(len(targetPts[key]))
        i+=1

    G.add_edges_from(edges)

    plt.figure(figsize=(20,130))
    nx.draw(G,G.position,linewidths=1,edge_color='gray')
    plt.show()

    return G

#应用numpy.histogram2d转换位置点及对应扫描区特征点为图片栅格数据
def colorMesh_phmi(landmarks,locations,targetPts_idx,Phmi):
    patternValuesDic={}
    for key in targetPts_idx.keys():
        patternValuesDic[key]=[0.5]*len(targetPts_idx[key])
        patternValuesDic[key].append(0.9)

    patternDic={}
    for key in patternValuesDic.keys():
        patternDic[key]={"x":landmarks[0][targetPts_idx[key]],
                         "y":landmarks[1][targetPts_idx[key]],
                         "z":patternValuesDic[key]
                         }
        patternDic[key]["x"]=np.append(patternDic[key]["x"],locations[0][key])
        patternDic[key]["y"]=np.append(patternDic[key]["y"],locations[1][key])

    histogram2dDic={}
    binNumber=(25,25) #32
    for key in patternDic.keys():
        zi, yi, xi = np.histogram2d(patternDic[key]["y"], patternDic[key]["x"], bins=binNumber, weights=patternDic[key]["z"], normed=False)
        counts, _, _ = np.histogram2d(patternDic[key]["y"], patternDic[key]["x"],bins=binNumber)
        # print(counts)
        histogram2dDic[key]={"xi":xi,"yi":yi,"zi":zi,"count":counts,"ziCount":zi / counts,"ziMasked":np.ma.masked_invalid(zi)}

    for key in histogram2dDic.keys():
        xi=histogram2dDic[key]["xi"]
        yi=histogram2dDic[key]["yi"]
        zi=histogram2dDic[key]["ziMasked"]

        x=patternDic[key]["x"]
        y=patternDic[key]["y"]
        z=patternDic[key]["z"]
        # print(x,y,z)
        #plot it
        fig, ax = plt.subplots(figsize=(25,20))
        ax.pcolormesh(xi, yi, zi, edgecolors='black')
        scat = ax.scatter(x, y, c=z, s=30)
        fig.colorbar(scat)
        ax.margins(0.05)

        plt.title("PHmi_%d:%f"%(key,Phmi[key]))
        plt.show()

        if key==20:
            break

    return histogram2dDic,patternDic

#显示评估值Phmi的数值分布
def singleBoxplot(array):
    import plotly.express as px
    import pandas as pd
    df=pd.DataFrame(array,columns=["value"])
    fig = px.box(df, y="value",points="all")
    fig.show()

# pyorth 深度学习 迭代数据集建立。迁移https://github.com/d2l-ai/d2l-zh  《动手学深度学习》(PyTorch版)
def data_iter(batch_size, features, labels):
    import random
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  # 样本的读取顺序是随机的
    for i in range(0, num_examples, batch_size):
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # 最后一次可能不足一个batch
        yield  features.index_select(0, j), labels.index_select(0, j)

'''输出类别配置'''
# pytorch output值确定，用Percentile百分位数分类连续数值用作输出类别
def labelsPercentile(data):
    percentileNumber=[25,50,75]
    percentileValue=[np.percentile(data,i) for i in percentileNumber]
    for i in range(len(percentileValue)):
        if pow(10,-5)<percentileValue[i]:
            percentileValue.insert(i,pow(10,-5))
            break

    data_label=["e","d","c","b","a"]
    labels=[]
    for i in data:
        if i<=percentileValue[0]:
            labels.append(0)
        elif percentileValue[0]<i<=percentileValue[1]:
            labels.append(1)
        elif percentileValue[1]<i<=percentileValue[2]:
            labels.append(2)
        elif percentileValue[2]<i<=percentileValue[3]:
            labels.append(3)
        elif i>percentileValue[3]:
            labels.append(4)

    return labels

#pytorch output值确定，用Percentile百分位数分类连续数值用作输出类别——升级版
def labelsPercentile_upgrade(data):
    percentileNumber=[0,1,10,20, 30, 40,50,60, 70, 80, 90,100] #可以设置任意百分位切分值
    percentileValue=[np.percentile(data,i) for i in percentileNumber]
    print(percentileValue)

    bunchRange=list(zip(percentileValue, percentileValue[1:] + percentileValue[:1]))
    bunchIdx=list(range(len(bunchRange)))
    print(bunchRange,bunchIdx)
    valRange=[]

    i=0
    for val in data:
        val_Idx=[bunchIdx[k] for k in range(len(bunchRange)) if val>=bunchRange[k][0] and val<=bunchRange[k][1]]
        valRange.append(val_Idx[0])
        # print(val,val_Idx)
        # if i==0:
        #     break

    # print(valRange)
    print("bunchIdx amount:",len(bunchIdx),"\n",bunchIdx)
    return valRange

#输出类别设置，math.pow(10,-5)为评估标准值
def labels2Values(data):
    data[data>math.pow(10,-5)]=1
    data[data<=math.pow(10,-5)]=0
    print(data)

    return data

#pytorch output值确定，均分方式分类连续数值用作输出类别
def labelsPercentile_extent(data):
    # print(min(data))
    diff=max(data)-min(data)
    bunch=50.
    bunchList=[]
    i=0
    while 1:
        val=min(data)+diff/bunch*i
        if val<=max(data)+diff/bunch:
            bunchList.append(val)
            i+=1
        else:
            break
    bunchRange=list(zip(bunchList, bunchList[1:] + bunchList[:1]))
    bunchIdx=list(range(len(bunchRange)))
    # print(bunchRange,bunchIdx)
    valRange=[]

    i=0
    for val in data:

        val_Idx=[bunchIdx[k] for k in range(len(bunchRange)) if val>=bunchRange[k][0] and val<=bunchRange[k][1]]
        valRange.append(val_Idx[0])
        # print(val,val_Idx)
        # if i==0:
        #     break

    # print(valRange)
    print("bunchIdx amount:",len(bunchIdx),"\n",bunchIdx)
    return valRange

#提取返回值，以字典形式
def relationPts(landmarks,locations,targetPts_idx,Phmi):
    ptsVectors={}
    for key in targetPts_idx.keys():
        ptsVectors[key]={"vector":[(landmarks[0][i]-locations[0][key],landmarks[1][i]-locations[1][key]) for i in targetPts_idx[key]],
                         "distance":[math.sqrt((landmarks[0][i]-locations[0][key])**2+(landmarks[1][i]-locations[1][key])**2) for i in targetPts_idx[key]],
                         "quantity":len(targetPts_idx[key]),
                         "Phmi":Phmi[key]
                                   }

    return ptsVectors

#数据特征栅格——A。location无人车位置点与激光雷达扫描范围landmarks数据特征，将位置点作为栅格中心，图片栅格格式
def centricRaster(ptsCenter,radius,cellsize):
    pass
    Xi=list(np.arange(ptsCenter[0]-radius-cellsize/2,ptsCenter[0]+radius+cellsize,cellsize))
    Yi=list(np.arange(ptsCenter[1]-radius-cellsize/2,ptsCenter[1]+radius+cellsize,cellsize))
    XYi=np.array([(x,y) for x in Xi for y in Yi]).reshape(len(Xi),len(Yi),2)

    XRangei=list(zip(Xi, Xi[1:] + Xi[:1]))
    XRangei.pop(-1)

    YRangei=list(zip(Yi, Yi[1:] + Yi[:1]))
    YRangei.pop(-1)
    XYRangei=np.array([(x,y) for x in XRangei for y in  YRangei]).reshape(len( XRangei),len(YRangei),2,2)
    idxRange=np.array([1 for x in XRangei for y in  YRangei]).reshape(len( XRangei),len(YRangei),-1)

    z=idxRange
    featureLabel=np.array(range(z.shape[0]*z.shape[1])).reshape(z.shape[:-1])

    fig, ax = plt.subplots(figsize=(20,20))
    scat = ax.scatter(XYi[:,:,0], XYi[:,:,1], c="r", s=30)
    plt.show()

    return XYi,XYRangei,idxRange,featureLabel

#数据特征栅格——A。接函数 centricRaster()。
def featureRaster(landmarks,locations,targetPts_idx,Phmi,radius,cellsize):
    featureDic={}
    for key in tqdm(targetPts_idx.keys()):
        XYi,XYRangei,idxRange,featureLabel=centricRaster([locations[0][key],locations[1][key]],radius,cellsize)
        # fearureMinusValue=np.full(featureLabel.shape,-1)
        # print(XYi.shape,XYRangei.shape)

        relativeCellCoords=np.concatenate([v.reshape(-1,1) for v in np.where(idxRange)],axis=1) #返回数组值索引（2 维度），作为坐标位置
        rCellCoordsMatrix=relativeCellCoords.reshape(idxRange.shape[:-1]+(3,))
        idxList=[]
        labelList=[]
        for i in targetPts_idx[key]:
            valX=landmarks[0][i]
            valY=landmarks[1][i]
            a_bool=np.logical_and(XYRangei[:,:,0,0]<=valX,valX<=XYRangei[:,:,0,1])
            b_bool=np.logical_and(XYRangei[:,:,1,0]<=valY,valY<=XYRangei[:,:,1,1])
            c_bool=np.logical_and(a_bool,b_bool)
            idxList.append(rCellCoordsMatrix[c_bool].tolist()[0])
            labelList.append(featureLabel[c_bool].tolist()[0])

        # fearureMinusValueExtract=fearureMinusValue[]
        featureDic[key]={"idx":idxList,
                         "label":labelList,
                         "Phmi":Phmi[key],
                         # "featureValue":
                         }

        if key==1:
            break

    return featureDic

'''栅格图像显示'''
from IPython import display
from matplotlib import pyplot as plt
def use_svg_display():
    """Use svg format to display plot in jupyter"""
    display.set_matplotlib_formats('svg')
def colorMeshShow(histogram2dDic_part,patternDic_part,Phmi_part,condi):
    use_svg_display()
    xiArray=np.array([histogram2dDic_part[key]["xi"].tolist() for key in histogram2dDic_part.keys()])
    yiArray=np.array([histogram2dDic_part[key]["yi"].tolist() for key in histogram2dDic_part.keys()])
    ziArray=np.array([histogram2dDic_part[key]["ziMasked"].tolist() for key in histogram2dDic_part.keys()])

    xArray=np.array([patternDic_part[key]["x"].tolist() for key in patternDic_part.keys()])
    yArray=np.array([patternDic_part[key]["y"].tolist() for key in patternDic_part.keys()])
    zArray=np.array([patternDic_part[key]["z"] for key in patternDic_part.keys()])

    # print(xiArray)

    width=int(round(math.sqrt(len(xArray)),2))
    # print("+"*50)
    # print(width)
    fig, axs = plt.subplots(width, width, figsize=(10, 10),constrained_layout=True)
    for ax, xi, yi, zi, x, y,z,titleV,key in zip(axs.flat, xiArray,yiArray,ziArray,xArray, yArray,zArray,Phmi_part,condi):

        ax.pcolormesh(xi, yi, zi, edgecolors='black')
        scat = ax.scatter(x, y, c=z, s=30)
        fig.colorbar(scat)
        ax.margins(0.05)

        ax.set_title("PHmi_%d:%f"%(key,titleV))
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)

    plt.show()

if __name__ == "__main__":
    LandmarkMap_dic=readMatLabFig_LandmarkMap(LandmarkMap_fn)
    PHMI_dic=readMatLabFig_PHMI(PHMI_fn,LandmarkMap_dic)

    locations=PHMI_dic[0]
    landmarks=LandmarkMap_dic[1]
    radius=25
    targetPts,locations_pts,targetPts_idx=scanCircleBuffer(locations,landmarks,radius)
    Phmi=PHMI_dic[1][2]
    singleBoxplot(Phmi)
    # G=location_landmarks_network(targetPts_idx,locations,landmarks)

    #b = (f >= 1.0 ? 255 : (f <= 0.0 ? 0 : (int)floor(f * 256.0)))
    # data=colorMesh_phmi(landmarks,locations,targetPts_idx,Phmi)

    histogram2dDic,patternDic=colorMesh_phmi(landmarks,locations,targetPts_idx,Phmi)
    condi={i for i in range(20)}
    histogram2dDic_part={key: histogram2dDic[key] for key in histogram2dDic.keys() & condi}
    patternDic_part={key:patternDic[key] for key in patternDic.keys() & condi}
    Phmi_part=[Phmi[i] for i in condi]
    colorMeshShow(histogram2dDic_part,patternDic_part,Phmi_part,condi)

    # features=torch.tensor(np.asarray([data[val]["zi"] for val in data.keys()]).reshape(len(data.keys()),-1))
    # LE=preprocessing.LabelEncoder()
    # lables=torch.tensor(LE.fit_transform(labelsPercentile(Phmi)))

    # features=torch.tensor(np.asarray([data[val]["zi"] for val in data.keys()]))
    # labels=torch.tensor(labelsPercentile(Phmi))
    # valRange=labelsPercentile_extent(Phmi)
    # labelsExtent=torch.tensor(labelsPercentile_extent(Phmi))

    # labelsUpgrade=labelsPercentile_upgrade(Phmi)
    # label2V=labels2Values(Phmi)
    # (unique, counts) = np.unique(label2V, return_counts=True)
    # print((unique, counts))
    # batch_size=100
    # data_iter(batch_size, features, labels)
    # for X, y in data_iter(batch_size, features, labels):
    #     print(X.shape, y.shape)
    #     break

    # ptsVectors=relationPts(landmarks,locations,targetPts_idx,Phmi)
    # ptsVectorsFn=r"C:\Users\richi\omen-richiebao\omen-code\Chicago_code\data\ptsVectors.pkl"
    # with open(ptsVectorsFn, 'wb') as handle:
    #     pickle.dump(ptsVectors, handle, protocol=pickle.HIGHEST_PROTOCOL)

    # ptsCenter=[0,0]
    # cellsize=1
    # XYi,XYRangei,idxRange,featureLabel=centricRaster(ptsCenter,radius,cellsize)
    # featureDic=featureRaster(landmarks,locations,targetPts_idx,Phmi,radius,cellsize)

    # featureDicFn=r"C:\Users\richi\omen-richiebao\omen-code\Chicago_code\data\phmiFeature.pkl"
    # with open(featureDicFn, 'wb') as handle:
    #     pickle.dump(featureDic, handle, protocol=pickle.HIGHEST_PROTOCOL)

    # with open(featureDicFn, 'rb') as handle:
    #     b = pickle.load(handle)

    # phmi_labelFn=r"C:\Users\richi\omen-richiebao\omen-code\Chicago_code\data\phmi_label.pkl"
    # with open(phmi_labelFn, 'wb') as handle:
    #     pickle.dump(featureLabel, handle, protocol=pickle.HIGHEST_PROTOCOL)


"""crudproject1 URL Configuration

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',views.add_show,name='addandshow'),
    path('delete/<int:id>/',views.delete_data,name='deletedata'),
    path('<int:id>/',views.update_data,name='updatedata'),
    path('activecustomer/',views.getactivecustomer,name='activecustomer'),
    path('inactivecustomer/',views.getinactivecustomer,name='inactivecustomer'),
    path('addshow/',views.add_show_customer,name='addandshowcustomer'),
    path('update/<int:id>/',views.update_customer_data,name='updatecustomer'),
    path('deletecustomer/<int:id>/',views.delete_customer_data,name='deletecustomerdata'),
    path('addshow/paidsubscriber/',views.getpaidsubscriber,name='paidsubscriber'),
    path('addshow/unpaidsubscriber/',views.getunpaidsubscriber,name='unpaidsubscriber'),
    path('lalit/',views.sendemail,name='lalit'),
    path('signup/',views.get_signup,name='signup'),
    path('signin/',views.get_signin,name='signin'),
    path('logout/',views.logoutpage,name='logout'),
    path('createcustomer/',views.add_customer,name='createcustomer'),
]


from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('commtrack', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='stockstate',
            name='last_modified_form_id',
            field=models.CharField(max_length=100, null=True),
            preserve_default=True,
        ),
    ]


# -*- coding: UTF-8 -*-
import pandas as pd
import numpy as np
import PySimpleGUI as sg
from Dijkstra import dijkstra
from Grafo import Grafo_Dir
import Main
import time
import random

# Importando os dados de entrada
df = pd.read_csv('rotas-potenciais.txt', sep=';')

O, D = df['O'], df['D']  # Origem e Destinos
C_O, C_D = df['Origem'],  df['Destino'] # Nomes das cidades de Origem e Destino
dist = df['Distância aérea'] # As distâncias dos respectivos trajetos

# Transformando de Dataframe para Array
O = np.array(O) # Origem
D = np.array(D) # Destino
C_O = np.array(C_O) # Nomes das cidades de Origem
C_D = np.array(C_D) # Nomes das cidades de Destino
dist = np.array(dist) # Distâncias aéreas

entrada = [i-1 for i in O]  # Iniciando a indentação em 0 para as Origens
saida = [i-1 for i in D]    # Iniciando a indentação em 0 para os Destinos
distancias = [i for i in dist]

cidades = [None for i in range(0, int(max(D)))]

# Para cada entrada/saida, pegar as cidades exclusivas e incluir na lista cidades
for city in entrada:
  if cidades[int(city)] == None:
    cidades[int(city)] = C_O[entrada.index(int(city))]
for city in saida:
  if cidades[int(city)] == None:
    cidades[int(city)] = C_D[saida.index(int(city))]

#print(cidades)
#print(len(cidades))

def tracar_menor_caminho(Grafo, origem, destino):
  origem, destino = origem-1, destino-1
  if not cidades[origem]:
    return ('A Origem {} não existe no roteiro!'.format(origem+1))
  elif not cidades[destino]:
    return ('O Destino {} não existe no roteiro!'.format(destino+1))
  else:
    print('Origem: {}'.format(cidades[origem]))
    print('Destino: {}'.format(cidades[destino]))
    caminho = list()
    caminho.append(destino)
    atual = destino
    distancias, antecessores = dijkstra(Grafo, origem)
    while caminho[-1] != origem:
      atual = antecessores[atual]
      caminho.append(atual)
    caminho = caminho[::-1]
    rota = [cidades[i] for i in caminho]
    if distancias[destino] == float("+infinity"):
      #print('Não existe translado de {} para {}.'.format(cidades[origem], cidades[destino]))
      return False
    #print('O melhor trajeto com Origem em {} e Destino em {} é:\n{} \nDistância total: {} Km.'.format(origem+1, destino+1, rota, distancias[destino]))
    return True

def teste1():
  grafo = Grafo_Dir(len(cidades))

  for i in range(0,len(entrada)):
    grafo.adicionar_aresta(entrada[i], saida[i], distancias[i])

  Grafo = grafo.imprimir_grafo()
  tempos_de_atendimento = list()
  rodadas = 0
  while rodadas < 100:
    inicio = time.time()
    PosOrigem = random.choice(cidades)
    PosDestino = random.choice(cidades)
    while PosDestino == PosOrigem:
      PosDestino = random.choice(cidades)
    if (tracar_menor_caminho(Grafo, cidades.index(PosOrigem)+1, cidades.index(PosDestino)+1)):
      tempos_de_atendimento.append(time.time()-inicio)
      rodadas += 1
      print(rodadas)
  print('Tempo médio de atendimento para rotas válidas: %.4f' %(sum(tempos_de_atendimento)/rodadas))

def teste2():
  grafo = Grafo_Dir(len(cidades))

  for i in range(0,len(entrada)):
    grafo.adicionar_aresta(entrada[i], saida[i], distancias[i])

  Grafo = grafo.imprimir_grafo()
  tempos_de_atendimento = list()
  rodadas = 0
  while rodadas < 100:
    inicio = time.time()
    PosOrigem = random.choice(cidades)
    PosDestino = random.choice(cidades)
    while PosDestino == PosOrigem:
      PosDestino = random.choice(cidades)
    if not (tracar_menor_caminho(Grafo, cidades.index(PosOrigem)+1, cidades.index(PosDestino)+1)):
      tempos_de_atendimento.append(time.time()-inicio)
      rodadas += 1
  print('Tempo médio de atendimento para rotas inválidas: %.4f' %(sum(tempos_de_atendimento)/rodadas))

def teste3():
  grafo = Grafo_Dir(len(cidades))

  for i in range(0,len(entrada)):
    grafo.adicionar_aresta(entrada[i], saida[i], distancias[i])

  Grafo = grafo.imprimir_grafo()
  tempos_de_atendimento = list()
  for i in range(0,100):
    inicio = time.time()
    PosOrigem = random.choice(cidades)
    PosDestino = random.choice(cidades)
    while PosDestino == PosOrigem:
      PosDestino = random.choice(cidades)
    tracar_menor_caminho(Grafo, cidades.index(PosOrigem)+1, cidades.index(PosDestino)+1)
    tempos_de_atendimento.append(time.time()-inicio)

  print('Tempo médio de atendimento para rotas válidas e inválidas: %.4f' %(sum(tempos_de_atendimento)/100))

teste1()
teste2()
teste3()


from django.core.management.base import BaseCommand, CommandError
from main.models import BoxLocation
from main.models import create_box_key

class Command(BaseCommand):
    help = 'Create Dummy Box locations'

    def handle(self, *args, **options):

        boxes = BoxLocation.objects.filter(is_active=True)
        for center in boxes:
            center:BoxLocation
            center.center_apikey=create_box_key()
            center.save()

        self.stdout.write(self.style.SUCCESS("Successfully updated %s box locations" %len(boxes)))


import pandas as pd
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_absolute_error
from sklearn.model_selection import train_test_split

input_txt=pd.read_csv("../household data/train.csv");
test_txt=pd.read_csv("../household data/test.csv");

y=input_txt.SalePrice;
label=['LotArea','YearBuilt','1stFlrSF','2ndFlrSF','FullBath','BedroomAbvGr','TotRmsAbvGrd'];
x=input_txt[label];

train_x,val_x,train_y,val_y=train_test_split(x,y,random_state=0);
model=DecisionTreeRegressor();

#test_x=test_txt[label];
model.fit(train_x,train_y);

predicted=model.predict(val_x)
print(mean_absolute_error(val_y,predicted))


from django.db.models.signals import post_save
from django.dispatch import receiver
from archive_app.models import FamilyMember

@receiver(post_save, sender=FamilyMember)
def update_partner(sender, instance, created, **kwargs):
    print(created)
    print(instance.partner)
    if created and instance.partner:
        partner = FamilyMember.objects.get(id=instance.partner.id)
        partner.partner = instance
        partner.save()
    if created and instance.partner2:
        partner2 = FamilyMember.objects.get(id=instance.partner2.id)
        partner2.partner2 = instance
        partner2.save()


from pytube import Playlist

while True:
    # Prompt the user to enter a YouTube playlist URL
    playlist_url = input("Enter the URL of the YouTube playlist (or type 'exit' to terminate): ")

    if playlist_url.lower() == 'exit':
        break

    try:
        # Create a Playlist object
        playlist_obj = Playlist(playlist_url)

        # Extract video titles along with their numbers
        video_titles = [f"{i+1}. {video.title}" for i, video in enumerate(playlist_obj.videos)]

        # Define the output file name based on the playlist title
        playlist_title = playlist_obj.title
        output_file = f"{playlist_title}_titles.txt"

        # Write video titles to a text file
        with open(output_file, "w", encoding="utf-8") as file:
            file.write("\n".join(video_titles))

        print(f"{len(video_titles)} video titles have been saved to {output_file}.\n")
    except Exception as e:
        print(f"An error occurred: {str(e)}\n")

    # Ask the user if they want to continue
    while True:
        user_choice = input("Do you want to fetch another playlist? (yes/no): ").strip().lower()
        if user_choice in ('yes', 'no'):
            break
        else:
            print("Please enter 'yes' or 'no'.")

    if user_choice == 'no':
        break

print("Script terminated.")