#!/usr/bin/python
#
# Copyright 2012 Gaia Clary
#
# This file 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.
# 
# It 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 script. If not, see <http://www.gnu.org/licenses/>.
#
# The general implementation idea is based on the following article:
#
#
# http://www.secondcitizen.net/Forum/showpost.php?p=410101
#
# Blender SL Collada 1.4

bl_info = {
    "name": "SL Collada",
    "author": "Gaia Clary",
    "version": (1, 4),
    "blender": (2, 5, 9),
    "location": "File > Export > SL Collada",
    "description": "Export to SL Collada",
    "warning": "Experimental!",
    "wiki_url": "http://blog.machinimatrix.org/avastar/mesh/",
    "tracker_url": "http://www.the-machinimatrix.com/clarify/issues",
    "category": "Object"}


import bpy,math
from math import pi
from mathutils import Matrix
from bpy.props import *
import sys, os
import xml.dom.minidom


def rotate_bones(armature, rotate, temporary_change):
    print("processing armature", armature.name)
    if rotate:
        print("Assume, in front view the character looks to you.")
    else:
        print("Assume, in front view the character looks to the right side")

    armature_select= armature.select
    armature.select=True
    bpy.context.scene.objects.active = armature

    if (rotate):
        armature.rotation_euler.z += pi/2
        bpy.ops.object.transform_apply(rotation=True)
       
    bpy.ops.object.mode_set(mode='EDIT')
    original_data={}
    original_connects={}
    original_rolls={}
    bones = armature.data.edit_bones

    for bone in bones:
        original_connects[bone.name]=bone.use_connect
        bone.use_connect=False
 
    for bone in bones:
        original_rolls[bone.name]=bone.roll
        bone.roll=0

    for bone in bones:

        #print ("found bone: ", bone.name)

        tail        = [bone.tail.x, bone.tail.y, bone.tail.z]
        roll        = original_rolls[bone.name]
        use_connect = original_connects[bone.name]
        original_data[bone.name] = [tail, roll, use_connect]

        #print("from:", tail, roll, use_connect)

        bone.tail.x      = bone.head.x
        bone.tail.y      = bone.head.y + 0.01
        bone.tail.z      = bone.head.z
        tail             = [bone.tail.x, bone.tail.y, bone.tail.z]

        #print("to  :", tail, bone.roll, bone.use_connect) 

    bpy.ops.object.mode_set(mode='OBJECT')

    if (rotate and temporary_change):
        armature.rotation_euler.z -= pi/2
        bpy.ops.object.transform_apply(rotation=True)       

    armature.select=armature_select
    armature.update_tag()
    bpy.context.scene.update()
    return original_data   
  

def restore_data(armature, armature_data, rotate):
    print("Restoring original data to armature", armature.name)
    armature_select = armature.select
    armature.select=True
    bpy.context.scene.objects.active = armature

    if (rotate):
        armature.rotation_euler.z += pi/2
        bpy.ops.object.transform_apply(rotation=True)

    bpy.ops.object.mode_set(mode='EDIT')
    bones = armature.data.edit_bones

    original_connects={}
        

    for bone in bones:
        vec = [bone.tail.x, bone.tail.y, bone.tail.z]
        #print("from:", vec)
        tail, roll, use_connect = armature_data[bone.name]
        #print("to  :", vec)
        bone.tail.x      = tail[0]
        bone.tail.y      = tail[1]
        bone.tail.z      = tail[2]
        bone.roll        = roll
        original_connects[bone.name] = use_connect

    for bone in bones:
        bone.use_connect=original_connects[bone.name]

    bpy.ops.object.mode_set(mode='OBJECT')


    if (rotate):
        armature.rotation_euler.z -= pi/2
        bpy.ops.object.transform_apply(rotation=True)
 
    armature.select=armature_select
    armature.update_tag()
    bpy.context.scene.update()   



def find_armature(selection):

    armature = None
    for obj in selection:

        type = obj.type
        if type != "ARMATURE":
           armat = obj.find_armature()
           if armat != None and armat.name == "avatar":
               armature = armat
               break
    return armature


def export_collada(op, context, path, rotate, apply, fix):

    minor=bpy.app.version[1]
    if minor > 62 or minor < 59:
        op.report({'ERROR'}, "Script can not run on release " + bpy.app.version_string)
        return {'CANCELLED'}


    active_object = bpy.context.scene.objects.active
    original_mode = bpy.context.mode
    bpy.ops.object.mode_set(mode='OBJECT')
    selection = bpy.context.selected_objects

    armature_data = None
    if fix:
        armature = find_armature(selection)
        if armature != None:
            if armature.rotation_euler.z != 0:
                bpy.ops.object.mode_set(mode=original_mode)
                op.report({'ERROR'}, "Please apply Rotation to Armature and run again.")
                return {'CANCELLED'}

            armature_data = rotate_bones(armature, rotate, not apply)

    print("Exporting to Collada")

    bpy.ops.wm.collada_export(filepath=path, selected=True)


    # Cleanup

    if armature_data != None and not apply:
        restore_data(armature, armature_data, rotate)

    for obj in selection:
        obj.select = True

    bpy.context.scene.objects.active = active_object
    bpy.ops.object.mode_set(mode=original_mode) 
    
    return {'FINISHED'}


# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty





class ExportSLCollada(bpy.types.Operator, ExportHelper):
    '''Export the selection as Collada-1.4.1 with modified armatures for compatibility to SL'''
    bl_idname = "export.some_data"  # this is important since its how bpy.ops.export.some_data is constructed
    bl_label = "Export SL Collada"

    # ExportHelper mixin class uses this
    filename_ext = ".dae"

    filter_glob = StringProperty(
            default="*.dae",
            options={'HIDDEN'},
            )

    rotate = BoolProperty(
            name="Rotate z -90",
            description="Rotate z by -90 degrees. Use when Character looks at -y",
            default=True
            )


    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        return export_collada(self, context, self.filepath, self.rotate, False, True)


# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
    self.layout.operator(ExportSLCollada.bl_idname, text="SL Collada")


def register():
    bpy.utils.register_class(ExportSLCollada)
    bpy.types.INFO_MT_file_export.append(menu_func_export)


def unregister():
    bpy.utils.unregister_class(ExportSLCollada)
    bpy.types.INFO_MT_file_export.remove(menu_func_export)


if __name__ == "__main__":
    register()

    # test call
    #bpy.ops.export.some_data('INVOKE_DEFAULT')
