#!/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.11

bl_info = {
    "name": "SL Collada",
    "author": "Gaia Clary",
    "version": (1, 11),
    "blender": (2, 5, 9),
    "location": "File > Export > SL Collada",
    "description": "Export to SL Collada",
    "warning": "This addon is tested, but it still can contain bugs!",
    "wiki_url": "http://blog.machinimatrix.org/avastar/mesh/",
    "tracker_url": "http://www.the-machinimatrix.com/clarify/issues",
    "category": "Export"}


import bpy,math
from math import pi
from mathutils import Matrix
from bpy.props import *
import sys, os
import xml.dom.minidom


sl_bonelist = ["mHead", "mNeck" ,"mCollarRight", "mCollarLeft",
              "mShoulderRight", "mShoulderLeft", "mElbowRight",
              "mElbowLeft", "mWristRight", "mWristLeft", "mChest",
              "mTorso", "mPelvis", "mHipRight", "mHipLeft",
              "mKneeRight", "mKneeLeft", "mAnkleRight",
              "mAnkleLeft", "mFootRight", "mFootLeft" ]
sl_optional_bones = ["mSkull","mEyeRight","mEyeLeft","mToeRight","mToeLeft"]


def rotate_bones(armature, rotate, temporary_change):

    if rotate:
        print("Assume, the character looks to you when in front view.")
    else:
        print("Assume, the character looks to the right side when in front view.")

    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_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:


        tail        = [bone.tail.x, bone.tail.y, bone.tail.z]
        roll        = original_rolls[bone.name]
        use_connect = original_connects[bone.name]


        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]


    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()



def find_armature(selection):

    armature = None
    for obj in selection:

        type = obj.type
        if type == "ARMATURE":
            return obj
        else:
           armat = obj.find_armature()
           if armat != None:
               armature = armat
               break
    return armature


def prepare_for_export(selection):


    for obj in selection:
        type = obj.type
        if type == "ARMATURE":

            bpy.context.scene.objects.active = obj
            obj.select=True


            bpy.ops.object.mode_set(mode='POSE')
            bpy.ops.armature.layers_show_all()


            bpy.ops.object.mode_set(mode='OBJECT')

            bones= obj.pose.bones
            for bone in bones:
                cs = bone.custom_shape
                if cs != None:
                    cs.parent=None


            bpy.ops.object.mode_set(mode='EDIT')

            for bone in obj.data.edit_bones:
                bone.select=False
                if not  ( bone.name in sl_bonelist or bone.name in sl_optional_bones ):
                    bone.select=True
            bpy.ops.armature.delete()

            bpy.ops.object.mode_set(mode='OBJECT')

        else:
           armature = obj.find_armature()
           if armature != None:
               bpy.context.scene.objects.active = obj
               obj.select=True

               for key in sl_bonelist:
                  if not key in obj.vertex_groups.keys():
                      obj.vertex_groups.new(key)

               if obj.data.shape_keys != None:
                   sk= obj.shape_key_add("mix", from_mix=True)
                   sk.value=1.0
                   sks=obj.data.shape_keys.key_blocks
                   length= len(sks)
                   for index in range (1, length-1):
                       obj.active_shape_key_index = 1
                       bpy.ops.object.shape_key_remove()
                   obj.active_shape_key_index = 0
                   bpy.ops.object.shape_key_remove() # old Basekey
                   bpy.ops.object.shape_key_remove() # new shape mix

def create_selection(only_visible, delete_invisible):
    selection = bpy.context.selected_objects
    if only_visible:

        visible_selection = []
        invisible_selection = []

        for object in selection:
            if object.is_visible(bpy.context.scene) or object.type=="ARMATURE":
                visible_selection.append(object)
            else:
                invisible_selection.append(object)
        selection = visible_selection

        if delete_invisible: 
            for object in invisible_selection: 
                bpy.context.scene.objects.unlink(object)
                bpy.data.objects.remove(object) 
    return selection
 

def export_collada(op, context, path, rotate, keep_mesh, only_visible, 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'}

    original_mode    = bpy.context.mode

    if original_mode != 'OBJECT':
      try:
        bpy.ops.object.mode_set(mode='OBJECT')
      except:
        op.report({'ERROR'}, "Please export while in Object mode")
        pass

    active_object       = bpy.context.scene.objects.active
    selection           = create_selection(only_visible, delete_invisible=False)
    export_selection    = selection
    armature            = None
    armature_to_layer   = -1
    use_joints          = False

    work_on_copy        = False

    if fix:
        armature = find_armature(selection)

        if armature == None:
            work_on_copy = False
        else:
            work_on_copy = True

            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'}

            if armature in selection:
                use_joints = True
            else:
                selection.append(armature)
                armature.select=True
                if not armature.is_visible(bpy.context.scene):
                    index = -1
                    for layer in bpy.context.scene.layers:
                        index +=1
                        if layer == True:
                            armature.layers[index]=True
                            armature_to_layer=index
                            break

            bpy.context.scene.objects.active = armature
            bpy.ops.object.mode_set(mode='OBJECT')

            bpy.ops.object.duplicate()
            export_selection=create_selection(only_visible, delete_invisible=True)
            prepare_for_export(export_selection)
            armat = find_armature(export_selection)

            export_selection.remove(armat)
            export_selection.append(armat)

            rotate_bones(armat, rotate, not apply)

            if not use_joints:
                armat.select=False

    print("Export Collada into", path)
    bpy.ops.wm.collada_export(filepath=path, selected=True)
    print("Export complete.")


    if keep_mesh and work_on_copy:
      for obj in selection:
        obj.select=False
    else:
      if work_on_copy:
        for obj in export_selection:
          bpy.context.scene.objects.unlink(obj)
          bpy.data.objects.remove(obj)

      for obj in selection:
        try:
          if obj == armature and not use_joints: 
              obj.select=False
          else:
              obj.select=True
        except:
          pass

      bpy.context.scene.objects.active = active_object

    if armature != None and armature_to_layer != -1:
        armature.layers[armature_to_layer]=False

    if original_mode != 'OBJECT':
      try:
        bpy.ops.object.mode_set(mode=original_mode) 
      except:
        print("warn: Failed to reset to original mode", original_mode)
    
    return {'FINISHED'}


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"  
    bl_label = "Export SL Collada"

    
    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 True

    def execute(self, context):
        return export_collada(self, context, self.filepath, self.rotate, False, True, False, True)



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()

    


