# ##### BEGIN GPL LICENSE BLOCK #####
#
#  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 2
#  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, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

###############################################################################
#234567890123456789012345678901234567890123456789012345678901234567890123456789
#--------1---------2---------3---------4---------5---------6---------7---------


# ##### BEGIN COPYRIGHT BLOCK #####
#
# initial script copyright (c)2011-2013 Alexander Nussbaumer
#
# ##### END COPYRIGHT BLOCK #####


#import python stuff
from random import (
        randrange,
        )


# import io_scene_ms3d stuff
from io_scene_ms3d.ms3d_strings import (
        ms3d_str,
        )
from io_scene_ms3d.ms3d_spec import (
        Ms3dSpec,
        )
from io_scene_ms3d.ms3d_utils import (
        enable_edit_mode,
        get_edge_split_modifier_add_if,
        )


#import blender stuff
from bmesh import (
        from_edit_mesh,
        )
from bpy.utils import (
        register_class,
        unregister_class,
        )
from bpy_extras.io_utils import (
        ExportHelper,
        ImportHelper,
        )
from bpy.props import (
        BoolProperty,
        CollectionProperty,
        EnumProperty,
        FloatProperty,
        FloatVectorProperty,
        IntProperty,
        StringProperty,
        PointerProperty,
        )
from bpy.types import (
        Operator,
        PropertyGroup,
        Panel,
        Armature,
        Bone,
        Mesh,
        Material,
        Action,
        Group,
        UIList,
        )
from bpy.app import (
        debug,
        )


class Ms3dUi:
    VERBOSE_MODE_NONE = 'NONE'
    VERBOSE_MODE_NORMAL = 'NORMAL'
    VERBOSE_MODE_MAXIMAL = 'MAXIMAL'

    VERBOSE_NONE = {}
    VERBOSE_NORMAL = {True, VERBOSE_MODE_NORMAL, VERBOSE_MODE_MAXIMAL, }
    VERBOSE_MAXIMAL = {True, VERBOSE_MODE_MAXIMAL, }

    DEFAULT_VERBOSE = VERBOSE_MODE_NONE


    ###########################################################################
    FLAG_TEXTURE_COMBINE_ALPHA = 'COMBINE_ALPHA'
    FLAG_TEXTURE_HAS_ALPHA = 'HAS_ALPHA'
    FLAG_TEXTURE_SPHERE_MAP = 'SPHERE_MAP'

    @staticmethod
    def texture_mode_from_ms3d(ms3d_value):
        ui_value = set()
        if (ms3d_value & Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA) \
                == Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA:
            ui_value.add(Ms3dUi.FLAG_TEXTURE_COMBINE_ALPHA)
        if (ms3d_value & Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA) \
                == Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA:
            ui_value.add(Ms3dUi.FLAG_TEXTURE_HAS_ALPHA)
        if (ms3d_value & Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP) \
                == Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP:
            ui_value.add(Ms3dUi.FLAG_TEXTURE_SPHERE_MAP)
        return ui_value

    @staticmethod
    def texture_mode_to_ms3d(ui_value):
        ms3d_value = Ms3dSpec.FLAG_TEXTURE_NONE

        if Ms3dUi.FLAG_TEXTURE_COMBINE_ALPHA in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA
        if Ms3dUi.FLAG_TEXTURE_HAS_ALPHA in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA
        if Ms3dUi.FLAG_TEXTURE_SPHERE_MAP in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP
        return ms3d_value


    MODE_TRANSPARENCY_SIMPLE = 'SIMPLE'
    MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF \
            = 'DEPTH_BUFFERED_WITH_ALPHA_REF'
    MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES = 'DEPTH_SORTED_TRIANGLES'

    @staticmethod
    def transparency_mode_from_ms3d(ms3d_value):
        if(ms3d_value == Ms3dSpec.MODE_TRANSPARENCY_SIMPLE):
            return Ms3dUi.MODE_TRANSPARENCY_SIMPLE
        elif(ms3d_value == \
                Ms3dSpec.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF):
            return Ms3dUi.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF
        elif(ms3d_value == Ms3dSpec.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES):
            return Ms3dUi.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES

        return Ms3dUi.MODE_TRANSPARENCY_SIMPLE

    @staticmethod
    def transparency_mode_to_ms3d(ui_value):
        if(ui_value == Ms3dUi.MODE_TRANSPARENCY_SIMPLE):
            return Ms3dSpec.MODE_TRANSPARENCY_SIMPLE
        elif(ui_value == Ms3dUi.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF):
            return Ms3dSpec.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF
        elif(ui_value == Ms3dUi.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES):
            return Ms3dSpec.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES

        return Ms3dSpec.MODE_TRANSPARENCY_SIMPLE


    FLAG_NONE = 'NONE'
    FLAG_SELECTED = 'SELECTED'
    FLAG_HIDDEN = 'HIDDEN'
    FLAG_SELECTED2 = 'SELECTED2'
    FLAG_DIRTY = 'DIRTY'
    FLAG_ISKEY = 'ISKEY'
    FLAG_NEWLYCREATED = 'NEWLYCREATED'
    FLAG_MARKED = 'MARKED'

    @staticmethod
    def flags_from_ms3d(ms3d_value):
        ui_value = set()
        if (ms3d_value & Ms3dSpec.FLAG_SELECTED) == Ms3dSpec.FLAG_SELECTED:
            ui_value.add(Ms3dUi.FLAG_SELECTED)
        if (ms3d_value & Ms3dSpec.FLAG_HIDDEN) == Ms3dSpec.FLAG_HIDDEN:
            ui_value.add(Ms3dUi.FLAG_HIDDEN)
        if (ms3d_value & Ms3dSpec.FLAG_SELECTED2) == Ms3dSpec.FLAG_SELECTED2:
            ui_value.add(Ms3dUi.FLAG_SELECTED2)
        if (ms3d_value & Ms3dSpec.FLAG_DIRTY) == Ms3dSpec.FLAG_DIRTY:
            ui_value.add(Ms3dUi.FLAG_DIRTY)
        if (ms3d_value & Ms3dSpec.FLAG_ISKEY) == Ms3dSpec.FLAG_ISKEY:
            ui_value.add(Ms3dUi.FLAG_ISKEY)
        if (ms3d_value & Ms3dSpec.FLAG_NEWLYCREATED) == \
                Ms3dSpec.FLAG_NEWLYCREATED:
            ui_value.add(Ms3dUi.FLAG_NEWLYCREATED)
        if (ms3d_value & Ms3dSpec.FLAG_MARKED) == Ms3dSpec.FLAG_MARKED:
            ui_value.add(Ms3dUi.FLAG_MARKED)
        return ui_value

    @staticmethod
    def flags_to_ms3d(ui_value):
        ms3d_value = Ms3dSpec.FLAG_NONE
        if Ms3dUi.FLAG_SELECTED in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_SELECTED
        if Ms3dUi.FLAG_HIDDEN in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_HIDDEN
        if Ms3dUi.FLAG_SELECTED2 in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_SELECTED2
        if Ms3dUi.FLAG_DIRTY in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_DIRTY
        if Ms3dUi.FLAG_ISKEY in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_ISKEY
        if Ms3dUi.FLAG_NEWLYCREATED in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_NEWLYCREATED
        if Ms3dUi.FLAG_MARKED in ui_value:
            ms3d_value |= Ms3dSpec.FLAG_MARKED
        return ms3d_value

    ###########################################################################
    ICON_OPTIONS = 'LIGHT'
    ICON_OBJECT = 'WORLD'
    ICON_PROCESSING = 'OBJECT_DATAMODE'
    ICON_MODIFIER = 'MODIFIER'
    ICON_ANIMATION = 'RENDER_ANIMATION'
    ICON_ROTATION_MODE = 'BONE_DATA'
    ICON_ERROR = 'ERROR'

    ###########################################################################
    PROP_DEFAULT_VERBOSE = DEFAULT_VERBOSE

    ###########################################################################
    PROP_DEFAULT_USE_JOINT_SIZE = False
    PROP_DEFAULT_JOINT_SIZE = 0.01
    PROP_JOINT_SIZE_MIN = 0.01
    PROP_JOINT_SIZE_MAX = 10.0
    PROP_JOINT_SIZE_STEP = 0.1
    PROP_JOINT_SIZE_PRECISION = 2

    ###########################################################################
    PROP_DEFAULT_USE_ANIMATION = True
    PROP_DEFAULT_NORMALIZE_WEIGHTS = True
    PROP_DEFAULT_SHRINK_TO_KEYS = False
    PROP_DEFAULT_BAKE_EACH_FRAME = True
    PROP_DEFAULT_JOINT_TO_BONES = False
    PROP_DEFAULT_USE_BLENDER_NAMES = True
    PROP_DEFAULT_USE_BLENDER_MATERIALS = False
    PROP_DEFAULT_EXTENDED_NORMAL_HANDLING = False
    PROP_DEFAULT_APPLY_TRANSFORM = True
    PROP_DEFAULT_APPLY_MODIFIERS = True

    ###########################################################################
    PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW = 'PREVIEW'
    PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER = 'RENDER'
    PROP_DEFAULT_APPLY_MODIFIERS_MODE = PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW

    ###########################################################################
    PROP_ITEM_ROTATION_MODE_EULER = 'EULER'
    PROP_ITEM_ROTATION_MODE_QUATERNION = 'QUATERNION'
    PROP_DEFAULT_ANIMATION_ROTATION = PROP_ITEM_ROTATION_MODE_EULER

    ###########################################################################
    OPT_SMOOTHING_GROUP_APPLY = 'io_scene_ms3d.apply_smoothing_group'
    OPT_GROUP_APPLY = 'io_scene_ms3d.apply_group'
    OPT_MATERIAL_APPLY = 'io_scene_ms3d.apply_material'


###############################################################################
class Ms3dImportOperator(Operator, ImportHelper):
    """ Load a MilkShape3D MS3D File """
    bl_idname = 'import_scene.ms3d'
    bl_label = ms3d_str['BL_LABEL_IMPORTER']
    bl_description = ms3d_str['BL_DESCRIPTION_IMPORTER']
    bl_options = {'UNDO', 'PRESET', }
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'

    filepath: StringProperty(
            subtype='FILE_PATH',
            options={'HIDDEN', }
            )

    verbose: EnumProperty(
            name=ms3d_str['PROP_NAME_VERBOSE'],
            description=ms3d_str['PROP_DESC_VERBOSE'],
            items=( (Ms3dUi.VERBOSE_MODE_NONE,
                            ms3d_str['ENUM_VERBOSE_NONE_1'],
                            ms3d_str['ENUM_VERBOSE_NONE_2'],
                            ),
                    (Ms3dUi.VERBOSE_MODE_NORMAL,
                            ms3d_str['ENUM_VERBOSE_NORMAL_1'],
                            ms3d_str['ENUM_VERBOSE_NORMAL_2'],
                            ),
                    (Ms3dUi.VERBOSE_MODE_MAXIMAL,
                            ms3d_str['ENUM_VERBOSE_MAXIMALIMAL_1'],
                            ms3d_str['ENUM_VERBOSE_MAXIMALIMAL_2'],
                            ),
                    ),
            default=Ms3dUi.PROP_DEFAULT_VERBOSE,
            )

    use_animation: BoolProperty(
            name=ms3d_str['PROP_NAME_USE_ANIMATION'],
            description=ms3d_str['PROP_DESC_USE_ANIMATION'],
            default=Ms3dUi.PROP_DEFAULT_USE_ANIMATION,
            )

    rotation_mode: EnumProperty(
            name=ms3d_str['PROP_NAME_ROTATION_MODE'],
            description=ms3d_str['PROP_DESC_ROTATION_MODE'],
            items=( (Ms3dUi.PROP_ITEM_ROTATION_MODE_EULER,
                            ms3d_str['PROP_ITEM_ROTATION_MODE_EULER_1'],
                            ms3d_str['PROP_ITEM_ROTATION_MODE_EULER_2']),
                    (Ms3dUi.PROP_ITEM_ROTATION_MODE_QUATERNION,
                            ms3d_str['PROP_ITEM_ROTATION_MODE_QUATERNION_1'],
                            ms3d_str['PROP_ITEM_ROTATION_MODE_QUATERNION_2']),
                    ),
            default=Ms3dUi.PROP_DEFAULT_ANIMATION_ROTATION,
            )

    use_joint_size: BoolProperty(
            name=ms3d_str['PROP_NAME_USE_JOINT_SIZE'],
            description=ms3d_str['PROP_DESC_USE_JOINT_SIZE'],
            default=Ms3dUi.PROP_DEFAULT_USE_JOINT_SIZE,
            )

    joint_size: FloatProperty(
            name=ms3d_str['PROP_NAME_IMPORT_JOINT_SIZE'],
            description=ms3d_str['PROP_DESC_IMPORT_JOINT_SIZE'],
            min=Ms3dUi.PROP_JOINT_SIZE_MIN, max=Ms3dUi.PROP_JOINT_SIZE_MAX,
            precision=Ms3dUi.PROP_JOINT_SIZE_PRECISION, \
                    step=Ms3dUi.PROP_JOINT_SIZE_STEP,
            default=Ms3dUi.PROP_DEFAULT_JOINT_SIZE,
            subtype='FACTOR',
            #options={'HIDDEN', },
            )

    use_joint_to_bones: BoolProperty(
            name=ms3d_str['PROP_NAME_JOINT_TO_BONES'],
            description=ms3d_str['PROP_DESC_JOINT_TO_BONES'],
            default=Ms3dUi.PROP_DEFAULT_JOINT_TO_BONES,
            )

    use_extended_normal_handling: BoolProperty(
            name=ms3d_str['PROP_NAME_EXTENDED_NORMAL_HANDLING'],
            description=ms3d_str['PROP_DESC_EXTENDED_NORMAL_HANDLING'],
            default=Ms3dUi.PROP_DEFAULT_EXTENDED_NORMAL_HANDLING,
            )

    filename_ext: StringProperty(
            default=ms3d_str['FILE_EXT'],
            options={'HIDDEN', }
            )

    filter_glob: StringProperty(
            default=ms3d_str['FILE_FILTER'],
            options={'HIDDEN', }
            )


    @property
    def use_euler_rotation(self):
        return (Ms3dUi.PROP_ITEM_ROTATION_MODE_EULER \
                in self.rotation_mode)

    @property
    def use_quaternion_rotation(self):
        return (Ms3dUi.PROP_ITEM_ROTATION_MODE_QUATERNION \
                in self.rotation_mode)


    # draw the option panel
    def draw(self, blender_context):
        layout = self.layout

        box = layout.box()
        box.label(text=ms3d_str['LABEL_NAME_OPTIONS'], icon=Ms3dUi.ICON_OPTIONS)
        box.prop(self, 'verbose', icon='SPEAKER')

        box = layout.box()
        box.label(ms3d_str['LABEL_NAME_PROCESSING'],
                icon=Ms3dUi.ICON_PROCESSING)
        box.prop(self, 'use_extended_normal_handling')

        box = layout.box()
        box.label(text=ms3d_str['LABEL_NAME_ANIMATION'], icon=Ms3dUi.ICON_ANIMATION)
        box.prop(self, 'use_animation')
        if (self.use_animation):
            box.prop(self, 'rotation_mode', icon=Ms3dUi.ICON_ROTATION_MODE,
                    expand=False)
            flow = box.column_flow()
            flow.prop(self, 'use_joint_size')
            if (self.use_joint_size):
                flow.prop(self, 'joint_size')
            box.prop(self, 'use_joint_to_bones')
            if (self.use_joint_to_bones):
                box.box().label(ms3d_str['LABEL_NAME_JOINT_TO_BONES'],
                        icon=Ms3dUi.ICON_ERROR)

    # entrypoint for MS3D -> blender
    def execute(self, blender_context):
        """ start executing """
        from io_scene_ms3d.ms3d_import import (
                Ms3dImporter,
                )
        finished = Ms3dImporter(
                report=self.report,
                verbose=self.verbose,
                use_extended_normal_handling=self.use_extended_normal_handling,
                use_animation=self.use_animation,
                use_quaternion_rotation=self.use_quaternion_rotation,
                use_joint_size=self.use_joint_size,
                joint_size=self.joint_size,
                use_joint_to_bones=self.use_joint_to_bones,
                ).read(
                        blender_context,
                        self.filepath
                        )
        if finished:
            blender_context.scene.update()
            return {"FINISHED"}
        return {"CANCELLED"}

    def invoke(self, blender_context, event):
        blender_context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL', }

    @staticmethod
    def menu_func(cls, blender_context):
        cls.layout.operator(
                Ms3dImportOperator.bl_idname,
                text=ms3d_str['TEXT_OPERATOR'],
                )


class Ms3dExportOperator(Operator, ExportHelper):
    """Save a MilkShape3D MS3D File"""
    bl_idname = 'export_scene.ms3d'
    bl_label = ms3d_str['BL_LABEL_EXPORTER']
    bl_description = ms3d_str['BL_DESCRIPTION_EXPORTER']
    bl_options = {'UNDO', 'PRESET', }
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'

    filepath: StringProperty(
            subtype='FILE_PATH',
            options={'HIDDEN', }
            )

    verbose: EnumProperty(
            name=ms3d_str['PROP_NAME_VERBOSE'],
            description=ms3d_str['PROP_DESC_VERBOSE'],
            items=( (Ms3dUi.VERBOSE_MODE_NONE,
                            ms3d_str['ENUM_VERBOSE_NONE_1'],
                            ms3d_str['ENUM_VERBOSE_NONE_2'],
                            ),
                    (Ms3dUi.VERBOSE_MODE_NORMAL,
                            ms3d_str['ENUM_VERBOSE_NORMAL_1'],
                            ms3d_str['ENUM_VERBOSE_NORMAL_2'],
                            ),
                    (Ms3dUi.VERBOSE_MODE_MAXIMAL,
                            ms3d_str['ENUM_VERBOSE_MAXIMALIMAL_1'],
                            ms3d_str['ENUM_VERBOSE_MAXIMALIMAL_2'],
                            ),
                    ),
            default=Ms3dUi.PROP_DEFAULT_VERBOSE,
            )

    use_blender_names: BoolProperty(
            name=ms3d_str['PROP_NAME_USE_BLENDER_NAMES'],
            description=ms3d_str['PROP_DESC_USE_BLENDER_NAMES'],
            default=Ms3dUi.PROP_DEFAULT_USE_BLENDER_NAMES,
            )

    use_blender_materials: BoolProperty(
            name=ms3d_str['PROP_NAME_USE_BLENDER_MATERIALS'],
            description=ms3d_str['PROP_DESC_USE_BLENDER_MATERIALS'],
            default=Ms3dUi.PROP_DEFAULT_USE_BLENDER_MATERIALS,
            )

    apply_transform: BoolProperty(
            name=ms3d_str['PROP_NAME_APPLY_TRANSFORM'],
            description=ms3d_str['PROP_DESC_APPLY_TRANSFORM'],
            default=Ms3dUi.PROP_DEFAULT_APPLY_TRANSFORM,
            )

    apply_modifiers: BoolProperty(
            name=ms3d_str['PROP_NAME_APPLY_MODIFIERS'],
            description=ms3d_str['PROP_DESC_APPLY_MODIFIERS'],
            default=Ms3dUi.PROP_DEFAULT_APPLY_MODIFIERS,
            )

    apply_modifiers_mode:  EnumProperty(
            name=ms3d_str['PROP_NAME_APPLY_MODIFIERS_MODE'],
            description=ms3d_str['PROP_DESC_APPLY_MODIFIERS_MODE'],
            items=( (Ms3dUi.PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW,
                            ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_1'],
                            ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_2']),
                    (Ms3dUi.PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER,
                            ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_1'],
                            ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_2']),
                    ),
            default=Ms3dUi.PROP_DEFAULT_APPLY_MODIFIERS_MODE,
            )

    use_animation: BoolProperty(
            name=ms3d_str['PROP_NAME_USE_ANIMATION'],
            description=ms3d_str['PROP_DESC_USE_ANIMATION'],
            default=Ms3dUi.PROP_DEFAULT_USE_ANIMATION,
            )

    normalize_weights: BoolProperty(
            name=ms3d_str['PROP_NAME_NORMALIZE_WEIGHTS'],
            description=ms3d_str['PROP_DESC_NORMALIZE_WEIGHTS'],
            default=Ms3dUi.PROP_DEFAULT_NORMALIZE_WEIGHTS,
            )

    shrink_to_keys: BoolProperty(
            name=ms3d_str['PROP_NAME_SHRINK_TO_KEYS'],
            description=ms3d_str['PROP_DESC_SHRINK_TO_KEYS'],
            default=Ms3dUi.PROP_DEFAULT_SHRINK_TO_KEYS,
            )

    bake_each_frame: BoolProperty(
            name=ms3d_str['PROP_NAME_BAKE_EACH_FRAME'],
            description=ms3d_str['PROP_DESC_BAKE_EACH_FRAME'],
            default=Ms3dUi.PROP_DEFAULT_BAKE_EACH_FRAME,
            )

    check_existing: BoolProperty(
            default=False,
            options={'HIDDEN', }
            )

    filename_ext: StringProperty(
            default=ms3d_str['FILE_EXT'],
            options={'HIDDEN', }
            )

    filter_glob: StringProperty(
            default=ms3d_str['FILE_FILTER'],
            options={'HIDDEN', }
            )

    ##def object_items(self, blender_context):
    ##    return[(item.name, item.name, "") for item in blender_context.selected_objects if item.type in {'MESH', }]
    ##
    ##object_name = EnumProperty(
    ##        name="Object",
    ##        description="select an object to export",
    ##        items=object_items,
    ##        )

    ##EXPORT_ACTIVE_ONLY:
    ##limit availability to only active mesh object
    @classmethod
    def poll(cls, blender_context):
        return (blender_context
                and blender_context.active_object
                and blender_context.active_object.type in {'MESH', }
                and blender_context.active_object.data
                and blender_context.active_object.data.ms3d is not None
                )

    # draw the option panel
    def draw(self, blender_context):
        layout = self.layout

        box = layout.box()
        flow = box.column_flow()
        flow.label(text=ms3d_str['LABEL_NAME_OPTIONS'], icon=Ms3dUi.ICON_OPTIONS)
        flow.prop(self, 'verbose', icon='SPEAKER')

        box = layout.box()
        flow = box.column_flow()
        flow.label(ms3d_str['LABEL_NAME_PROCESSING'],
                icon=Ms3dUi.ICON_PROCESSING)
        row = flow.row()
        row.label(text=ms3d_str['PROP_NAME_ACTIVE'], icon='ROTACTIVE')
        row.label(blender_context.active_object.name)
        ##flow.prop(self, 'object_name')
        flow.prop(self, 'use_blender_names')
        flow.prop(self, 'use_blender_materials')

        box = layout.box()
        flow = box.column_flow()
        flow.label(ms3d_str['LABEL_NAME_MODIFIER'],
                icon=Ms3dUi.ICON_MODIFIER)
        flow.prop(self, 'apply_transform')
        row = flow.row()
        row.prop(self, 'apply_modifiers')
        sub = row.row()
        sub.active = self.apply_modifiers
        sub.prop(self, 'apply_modifiers_mode', text="")

        box = layout.box()
        flow = box.column_flow()
        flow.label(ms3d_str['LABEL_NAME_ANIMATION'],
                icon=Ms3dUi.ICON_ANIMATION)
        flow.prop(self, 'use_animation')
        if (self.use_animation):
            flow.prop(self, 'normalize_weights')
            flow.prop(self, 'shrink_to_keys')
            flow.prop(self, 'bake_each_frame')

    # entrypoint for blender -> MS3D
    def execute(self, blender_context):
        """start executing"""
        from io_scene_ms3d.ms3d_export import (
                Ms3dExporter,
                )
        finished = Ms3dExporter(
                self.report,
                verbose=self.verbose,
                use_blender_names=self.use_blender_names,
                use_blender_materials=self.use_blender_materials,
                apply_transform=self.apply_transform,
                apply_modifiers=self.apply_modifiers,
                apply_modifiers_mode=self.apply_modifiers_mode,
                use_animation=self.use_animation,
                normalize_weights=self.normalize_weights,
                shrink_to_keys=self.shrink_to_keys,
                bake_each_frame=self.bake_each_frame,
                ).write(
                        blender_context,
                        self.filepath
                        )
        if finished:
            blender_context.scene.update()
            return {"FINISHED"}
        return {"CANCELLED"}

    #
    def invoke(self, blender_context, event):
        blender_context.window_manager.fileselect_add(self)
        return {"RUNNING_MODAL", }

    @staticmethod
    def menu_func(cls, blender_context):
        cls.layout.operator(
                Ms3dExportOperator.bl_idname,
                text=ms3d_str['TEXT_OPERATOR']
                )


###############################################################################
##
###############################################################################


###############################################################################
class Ms3dSetSmoothingGroupOperator(Operator):
    bl_idname = Ms3dUi.OPT_SMOOTHING_GROUP_APPLY
    bl_label = ms3d_str['BL_LABEL_SMOOTHING_GROUP_OPERATOR']
    bl_options = {'UNDO', 'INTERNAL', }

    smoothing_group_index: IntProperty(
            name=ms3d_str['PROP_SMOOTHING_GROUP_INDEX'],
            options={'HIDDEN', 'SKIP_SAVE', },
            )

    @classmethod
    def poll(cls, blender_context):
        return (blender_context
                and blender_context.object
                and blender_context.object.type in {'MESH', }
                and blender_context.object.data
                and blender_context.object.data.ms3d is not None
                and blender_context.mode == 'EDIT_MESH'
                and blender_context.tool_settings.mesh_select_mode[2]
                )

    def execute(self, blender_context):
        custom_data = blender_context.object.data.ms3d
        blender_mesh = blender_context.object.data
        bm = from_edit_mesh(blender_mesh)
        layer_smoothing_group = bm.faces.layers.int.get(
                ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
        if custom_data.apply_mode in {'SELECT', 'DESELECT', }:
            if layer_smoothing_group is not None:
                is_select = (custom_data.apply_mode == 'SELECT')
                for bmf in bm.faces:
                    if (bmf[layer_smoothing_group] \
                            == self.smoothing_group_index):
                        bmf.select_set(is_select)
        elif custom_data.apply_mode == 'ASSIGN':
            if layer_smoothing_group is None:
                layer_smoothing_group = bm.faces.layers.int.new(
                        ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
                blender_mesh_object = blender_context.object
                get_edge_split_modifier_add_if(blender_mesh_object)
            blender_face_list = []
            for bmf in bm.faces:
                if not bmf.smooth:
                    bmf.smooth = True
                if bmf.select:
                    bmf[layer_smoothing_group] = self.smoothing_group_index
                    blender_face_list.append(bmf)
            edge_dict = {}
            for bmf in blender_face_list:
                bmf.smooth = True
                for bme in bmf.edges:
                    if edge_dict.get(bme) is None:
                        edge_dict[bme] = 0
                    else:
                        edge_dict[bme] += 1
                    is_border = (edge_dict[bme] == 0)
                    if is_border:
                        surround_face_smoothing_group_index \
                                = self.smoothing_group_index
                        for bmf in bme.link_faces:
                            if bmf[layer_smoothing_group] \
                                    != surround_face_smoothing_group_index:
                                surround_face_smoothing_group_index \
                                        = bmf[layer_smoothing_group]
                                break;
                        if surround_face_smoothing_group_index \
                                == self.smoothing_group_index:
                            is_border = False
                    bme.seam = is_border
                    bme.smooth = not is_border
        bm.free()
        enable_edit_mode(False, blender_context)
        enable_edit_mode(True, blender_context)
        return {'FINISHED', }


class Ms3dGroupOperator(Operator):
    bl_idname = Ms3dUi.OPT_GROUP_APPLY
    bl_label = ms3d_str['BL_LABEL_GROUP_OPERATOR']
    bl_options = {'UNDO', 'INTERNAL', }

    mode: EnumProperty(
            items=( ('', "", ""),
                    ('ADD_GROUP',
                            ms3d_str['ENUM_ADD_GROUP_1'],
                            ms3d_str['ENUM_ADD_GROUP_2']),
                    ('REMOVE_GROUP',
                            ms3d_str['ENUM_REMOVE_GROUP_1'],
                            ms3d_str['ENUM_REMOVE_GROUP_2']),
                    ('ASSIGN',
                            ms3d_str['ENUM_ASSIGN_1'],
                            ms3d_str['ENUM_ASSIGN_2_GROUP']),
                    ('REMOVE',
                            ms3d_str['ENUM_REMOVE_1'],
                            ms3d_str['ENUM_REMOVE_2_GROUP']),
                    ('SELECT',
                            ms3d_str['ENUM_SELECT_1'],
                            ms3d_str['ENUM_SELECT_2_GROUP']),
                    ('DESELECT',
                            ms3d_str['ENUM_DESELECT_1'],
                            ms3d_str['ENUM_DESELECT_2_GROUP']),
                    ),
            options={'HIDDEN', 'SKIP_SAVE', },
            )

    @classmethod
    def poll(cls, blender_context):
        return (blender_context
                and blender_context.object
                and blender_context.object.type in {'MESH', }
                and blender_context.object.data
                and blender_context.object.data.ms3d is not None
                and blender_context.mode == 'EDIT_MESH'
                #and blender_context.object.data.ms3d.selected_group_index != -1
                )

    def execute(self, blender_context):
        custom_data = blender_context.object.data.ms3d
        blender_mesh = blender_context.object.data
        bm = None
        bm = from_edit_mesh(blender_mesh)

        if self.mode == 'ADD_GROUP':
            item = custom_data.create_group()
            layer_group = bm.faces.layers.int.get(
                    ms3d_str['OBJECT_LAYER_GROUP'])
            if layer_group is None:
                bm.faces.layers.int.new(ms3d_str['OBJECT_LAYER_GROUP'])

        elif self.mode == 'REMOVE_GROUP':
            custom_data.remove_group()

        elif (custom_data.selected_group_index >= 0) and (
                custom_data.selected_group_index < len(custom_data.groups)):
            if self.mode in {'SELECT', 'DESELECT', }:
                layer_group = bm.faces.layers.int.get(
                        ms3d_str['OBJECT_LAYER_GROUP'])
                if layer_group is not None:
                    is_select = (self.mode == 'SELECT')
                    id = custom_data.groups[
                            custom_data.selected_group_index].id
                    for bmf in bm.faces:
                        if bmf[layer_group] == id:
                            bmf.select_set(is_select)

            elif self.mode in {'ASSIGN', 'REMOVE', }:
                layer_group = bm.faces.layers.int.get(
                        ms3d_str['OBJECT_LAYER_GROUP'])
                if layer_group is None:
                    layer_group = bm.faces.layers.int.new(
                            ms3d_str['OBJECT_LAYER_GROUP'])

                is_assign = (self.mode == 'ASSIGN')
                id = custom_data.groups[custom_data.selected_group_index].id
                for bmf in bm.faces:
                    if bmf.select:
                        if is_assign:
                            bmf[layer_group] = id
                        else:
                            bmf[layer_group] = -1
        if bm is not None:
            bm.free()
        enable_edit_mode(False, blender_context)
        enable_edit_mode(True, blender_context)
        return {'FINISHED', }


class Ms3dMaterialOperator(Operator):
    bl_idname = Ms3dUi.OPT_MATERIAL_APPLY
    bl_label = ms3d_str['BL_LABEL_MATERIAL_OPERATOR']
    bl_options = {'UNDO', 'INTERNAL', }

    mode: EnumProperty(
            items=( ('', "", ""),
                    ('FROM_BLENDER',
                            ms3d_str['ENUM_FROM_BLENDER_1'],
                            ms3d_str['ENUM_FROM_BLENDER_2']),
                    ('TO_BLENDER',
                            ms3d_str['ENUM_TO_BLENDER_1'],
                            ms3d_str['ENUM_TO_BLENDER_2']),
                    ),
            options={'HIDDEN', 'SKIP_SAVE', },
            )

    @classmethod
    def poll(cls, blender_context):
        return (blender_context
                and blender_context.object
                and blender_context.object.type in {'MESH', }
                and blender_context.object.data
                and blender_context.object.data.ms3d is not None
                and blender_context.material
                and blender_context.material.ms3d is not None
                )

    def execute(self, blender_context):
        blender_material = blender_context.active_object.active_material
        ms3d_material = blender_material.ms3d

        if self.mode == 'FROM_BLENDER':
            Ms3dMaterialHelper.copy_from_blender(self, blender_context,
                    ms3d_material, blender_material)
            pass

        elif self.mode == 'TO_BLENDER':
            # not implemented
            pass

        return {'FINISHED', }

    # entrypoint for option via UI
    def invoke(self, blender_context, event):
        return blender_context.window_manager.invoke_props_dialog(self)


###############################################################################
class Ms3dGroupProperties(PropertyGroup):
    name: StringProperty(
            name=ms3d_str['PROP_NAME_NAME'],
            description=ms3d_str['PROP_DESC_GROUP_NAME'],
            default="",
            #options={'HIDDEN', },
            )

    flags: EnumProperty(
            name=ms3d_str['PROP_NAME_FLAGS'],
            description=ms3d_str['PROP_DESC_FLAGS_GROUP'],
            items=(#(Ms3dUi.FLAG_NONE, ms3d_str['ENUM_FLAG_NONE_1'],
                   #         ms3d_str['ENUM_FLAG_NONE_2'],
                   #         Ms3dSpec.FLAG_NONE),
                    (Ms3dUi.FLAG_SELECTED,
                            ms3d_str['ENUM_FLAG_SELECTED_1'],
                            ms3d_str['ENUM_FLAG_SELECTED_2'],
                            Ms3dSpec.FLAG_SELECTED),
                    (Ms3dUi.FLAG_HIDDEN,
                            ms3d_str['ENUM_FLAG_HIDDEN_1'],
                            ms3d_str['ENUM_FLAG_HIDDEN_2'],
                            Ms3dSpec.FLAG_HIDDEN),
                    (Ms3dUi.FLAG_SELECTED2,
                            ms3d_str['ENUM_FLAG_SELECTED2_1'],
                            ms3d_str['ENUM_FLAG_SELECTED2_2'],
                            Ms3dSpec.FLAG_SELECTED2),
                    (Ms3dUi.FLAG_DIRTY,
                            ms3d_str['ENUM_FLAG_DIRTY_1'],
                            ms3d_str['ENUM_FLAG_DIRTY_2'],
                            Ms3dSpec.FLAG_DIRTY),
                    (Ms3dUi.FLAG_ISKEY,
                            ms3d_str['ENUM_FLAG_ISKEY_1'],
                            ms3d_str['ENUM_FLAG_ISKEY_2'],
                            Ms3dSpec.FLAG_ISKEY),
                    (Ms3dUi.FLAG_NEWLYCREATED,
                            ms3d_str['ENUM_FLAG_NEWLYCREATED_1'],
                            ms3d_str['ENUM_FLAG_NEWLYCREATED_2'],
                            Ms3dSpec.FLAG_NEWLYCREATED),
                    (Ms3dUi.FLAG_MARKED,
                            ms3d_str['ENUM_FLAG_MARKED_1'],
                            ms3d_str['ENUM_FLAG_MARKED_2'],
                            Ms3dSpec.FLAG_MARKED),
                    ),
            default=Ms3dUi.flags_from_ms3d(Ms3dSpec.DEFAULT_FLAGS),
            options={'ENUM_FLAG', 'ANIMATABLE', },
            )

    comment: StringProperty(
            name=ms3d_str['PROP_NAME_COMMENT'],
            description=ms3d_str['PROP_DESC_COMMENT_GROUP'],
            default="",
            #options={'HIDDEN', },
            )

    id: IntProperty(options={'HIDDEN', },)


class Ms3dModelProperties(PropertyGroup):
    name: StringProperty(
            name=ms3d_str['PROP_NAME_NAME'],
            description=ms3d_str['PROP_DESC_NAME_MODEL'],
            default="",
            #options={'HIDDEN', },
            )

    joint_size: FloatProperty(
            name=ms3d_str['PROP_NAME_JOINT_SIZE'],
            description=ms3d_str['PROP_DESC_JOINT_SIZE'],
            min=Ms3dUi.PROP_JOINT_SIZE_MIN, max=Ms3dUi.PROP_JOINT_SIZE_MAX,
            precision=Ms3dUi.PROP_JOINT_SIZE_PRECISION, \
                    step=Ms3dUi.PROP_JOINT_SIZE_STEP,
            default=Ms3dUi.PROP_DEFAULT_JOINT_SIZE,
            subtype='FACTOR',
            #options={'HIDDEN', },
            )

    transparency_mode: EnumProperty(
            name=ms3d_str['PROP_NAME_TRANSPARENCY_MODE'],
            description=ms3d_str['PROP_DESC_TRANSPARENCY_MODE'],
            items=( (Ms3dUi.MODE_TRANSPARENCY_SIMPLE,
                            ms3d_str['PROP_MODE_TRANSPARENCY_SIMPLE_1'],
                            ms3d_str['PROP_MODE_TRANSPARENCY_SIMPLE_2'],
                            Ms3dSpec.MODE_TRANSPARENCY_SIMPLE),
                    (Ms3dUi.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES,
                            ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_1'],
                            ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_2'],
                            Ms3dSpec.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES),
                    (Ms3dUi.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF,
                            ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_1'],
                            ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_2'],
                            Ms3dSpec.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF),
                    ),
            default=Ms3dUi.transparency_mode_from_ms3d(
                    Ms3dSpec.DEFAULT_MODEL_TRANSPARENCY_MODE),
            #options={'HIDDEN', },
            )

    alpha_ref: FloatProperty(
            name=ms3d_str['PROP_NAME_ALPHA_REF'],
            description=ms3d_str['PROP_DESC_ALPHA_REF'],
            min=0, max=1, precision=3, step=0.1,
            default=0.5,
            subtype='FACTOR',
            #options={'HIDDEN', },
            )

    comment: StringProperty(
            name=ms3d_str['PROP_NAME_COMMENT'],
            description=ms3d_str['PROP_DESC_COMMENT_MODEL'],
            default="",
            #options={'HIDDEN', },
            )

    ##########################
    # ms3d group handling
    #
    apply_mode: EnumProperty(
            items=( ('ASSIGN',
                            ms3d_str['ENUM_ASSIGN_1'],
                            ms3d_str['ENUM_ASSIGN_2_SMOOTHING_GROUP']),
                    ('SELECT',
                            ms3d_str['ENUM_SELECT_1'],
                            ms3d_str['ENUM_SELECT_2_SMOOTHING_GROUP']),
                    ('DESELECT',
                            ms3d_str['ENUM_DESELECT_1'],
                            ms3d_str['ENUM_DESELECT_2_SMOOTHING_GROUP']),
                    ),
            default='SELECT',
            options={'HIDDEN', 'SKIP_SAVE', },
            )

    selected_group_index: IntProperty(
            default=-1,
            min=-1,
            options={'HIDDEN', 'SKIP_SAVE', },
            )
    #
    # ms3d group handling
    ##########################

    groups: CollectionProperty(
            type=Ms3dGroupProperties,
            #options={'HIDDEN', },
            )


    def generate_unique_id(self):
        return randrange(1, 0x7FFFFFFF) # pseudo unique id

    def create_group(self):
        item = self.groups.add()
        item.id = self.generate_unique_id()
        length = len(self.groups)
        self.selected_group_index = length - 1

        item.name = ms3d_str['STRING_FORMAT_GROUP'].format(length)
        return item

    def remove_group(self):
        index = self.selected_group_index
        length = len(self.groups)
        if (index >= 0) and (index < length):
            if index > 0 or length == 1:
                self.selected_group_index = index - 1
            self.groups.remove(index)


class Ms3dArmatureProperties(PropertyGroup):
    name: StringProperty(
            name=ms3d_str['PROP_NAME_NAME'],
            description=ms3d_str['PROP_DESC_NAME_ARMATURE'],
            default="",
            #options={'HIDDEN', },
            )


class Ms3dJointProperties(PropertyGroup):
    name: StringProperty(
            name=ms3d_str['PROP_NAME_NAME'],
            description=ms3d_str['PROP_DESC_NAME_JOINT'],
            default="",
            #options={'HIDDEN', },
            )

    flags: EnumProperty(
            name=ms3d_str['PROP_NAME_FLAGS'],
            description=ms3d_str['PROP_DESC_FLAGS_JOINT'],
            items=(#(Ms3dUi.FLAG_NONE,
                   #         ms3d_str['ENUM_FLAG_NONE_1'],
                   #         ms3d_str['ENUM_FLAG_NONE_2'],
                   #         Ms3dSpec.FLAG_NONE),
                    (Ms3dUi.FLAG_SELECTED,
                            ms3d_str['ENUM_FLAG_SELECTED_1'],
                            ms3d_str['ENUM_FLAG_SELECTED_2'],
                            Ms3dSpec.FLAG_SELECTED),
                    (Ms3dUi.FLAG_HIDDEN,
                            ms3d_str['ENUM_FLAG_HIDDEN_1'],
                            ms3d_str['ENUM_FLAG_HIDDEN_2'],
                            Ms3dSpec.FLAG_HIDDEN),
                    (Ms3dUi.FLAG_SELECTED2,
                            ms3d_str['ENUM_FLAG_SELECTED2_1'],
                            ms3d_str['ENUM_FLAG_SELECTED2_2'],
                            Ms3dSpec.FLAG_SELECTED2),
                    (Ms3dUi.FLAG_DIRTY,
                            ms3d_str['ENUM_FLAG_DIRTY_1'],
                            ms3d_str['ENUM_FLAG_DIRTY_2'],
                            Ms3dSpec.FLAG_DIRTY),
                    (Ms3dUi.FLAG_ISKEY,
                            ms3d_str['ENUM_FLAG_ISKEY_1'],
                            ms3d_str['ENUM_FLAG_ISKEY_2'],
                            Ms3dSpec.FLAG_ISKEY),
                    (Ms3dUi.FLAG_NEWLYCREATED,
                            ms3d_str['ENUM_FLAG_NEWLYCREATED_1'],
                            ms3d_str['ENUM_FLAG_NEWLYCREATED_2'],
                            Ms3dSpec.FLAG_NEWLYCREATED),
                    (Ms3dUi.FLAG_MARKED,
                            ms3d_str['ENUM_FLAG_MARKED_1'],
                            ms3d_str['ENUM_FLAG_MARKED_2'],
                            Ms3dSpec.FLAG_MARKED),
                    ),
            default=Ms3dUi.flags_from_ms3d(Ms3dSpec.DEFAULT_FLAGS),
            options={'ENUM_FLAG', 'ANIMATABLE', },
            )

    color: FloatVectorProperty(
            name=ms3d_str['PROP_NAME_COLOR'],
            description=ms3d_str['PROP_DESC_COLOR_JOINT'],
            subtype='COLOR', size=3, min=0, max=1, precision=3, step=0.1,
            default=Ms3dSpec.DEFAULT_JOINT_COLOR,
            #options={'HIDDEN', },
            )

    comment: StringProperty(
            name=ms3d_str['PROP_NAME_COMMENT'],
            description=ms3d_str['PROP_DESC_COMMENT_JOINT'],
            default="",
            #options={'HIDDEN', },
            )


class Ms3dMaterialHelper:
    @staticmethod
    def copy_to_blender_ambient(cls, blender_context):
        pass

    @staticmethod
    def copy_to_blender_diffuse(cls, blender_context):
        cls.id_data.diffuse_color = cls.diffuse[0:3]
        #cls.id_data.diffuse_intensity = cls.diffuse[3]
        pass

    @staticmethod
    def copy_to_blender_specular(cls, blender_context):
        cls.id_data.specular_color = cls.specular[0:3]
        #cls.id_data.specular_intensity = cls.specular[3]
        pass

    @staticmethod
    def copy_to_blender_emissive(cls, blender_context):
        cls.id_data.emit = (cls.emissive[0] + cls.emissive[1] \
                + cls.emissive[2]) / 3.0
        pass

    @staticmethod
    def copy_to_blender_shininess(cls, blender_context):
        cls.id_data.specular_hardness = cls.shininess * 4.0
        pass

    @staticmethod
    def copy_to_blender_transparency(cls, blender_context):
        cls.id_data.alpha = 1.0 - cls.transparency
        pass


    @staticmethod
    def copy_from_blender(cls, blender_context, ms3d_material, blender_material):
        # copy, because of auto update, it would distord original values
        blender_material_diffuse_color = blender_material.diffuse_color.copy()
        blender_material_diffuse_intensity = blender_material.diffuse_intensity
        blender_material_specular_color = blender_material.specular_color.copy()
        blender_material_specular_intensity = \
                blender_material.specular_intensity
        blender_material_emit = blender_material.emit
        blender_material_specular_hardness = \
                blender_material.specular_hardness
        blender_material_alpha = blender_material.alpha

        blender_material_texture = None
        for slot in blender_material.texture_slots:
            if slot and slot.use_map_color_diffuse \
                    and slot.texture.type == 'IMAGE':
                blender_material_texture = slot.texture.image.filepath
                break

        blender_material_alphamap = None
        for slot in blender_material.texture_slots:
            if slot and not slot.use_map_color_diffuse \
                    and slot.use_map_alpha and slot.texture.type == 'IMAGE':
                blender_material_alphamap = slot.texture.image.filepath
                break

        ms3d_material.diffuse[0] = blender_material_diffuse_color[0]
        ms3d_material.diffuse[1] = blender_material_diffuse_color[1]
        ms3d_material.diffuse[2] = blender_material_diffuse_color[2]
        ms3d_material.diffuse[3] = 1.0
        ms3d_material.specular[0] = blender_material_specular_color[0]
        ms3d_material.specular[1] = blender_material_specular_color[1]
        ms3d_material.specular[2] = blender_material_specular_color[2]
        ms3d_material.specular[3] = 1.0
        ms3d_material.emissive[0] = blender_material_emit
        ms3d_material.emissive[1] = blender_material_emit
        ms3d_material.emissive[2] = blender_material_emit
        ms3d_material.emissive[3] = 1.0
        ms3d_material.shininess = blender_material_specular_hardness / 4.0
        ms3d_material.transparency = 1.0 - blender_material_alpha

        if blender_material_texture:
            ms3d_material.texture = blender_material_texture
        else:
            ms3d_material.texture = ""

        if blender_material_alphamap:
            ms3d_material.alphamap = blender_material_alphamap
        else:
            ms3d_material.alphamap = ""


class Ms3dMaterialProperties(PropertyGroup):
    name: StringProperty(
            name=ms3d_str['PROP_NAME_NAME'],
            description=ms3d_str['PROP_DESC_NAME_MATERIAL'],
            default="",
            #options={'HIDDEN', },
            )

    ambient: FloatVectorProperty(
            name=ms3d_str['PROP_NAME_AMBIENT'],
            description=ms3d_str['PROP_DESC_AMBIENT'],
            subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1,
            default=Ms3dSpec.DEFAULT_MATERIAL_AMBIENT,
            update=Ms3dMaterialHelper.copy_to_blender_ambient,
            #options={'HIDDEN', },
            )

    diffuse: FloatVectorProperty(
            name=ms3d_str['PROP_NAME_DIFFUSE'],
            description=ms3d_str['PROP_DESC_DIFFUSE'],
            subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1,
            default=Ms3dSpec.DEFAULT_MATERIAL_DIFFUSE,
            update=Ms3dMaterialHelper.copy_to_blender_diffuse,
            #options={'HIDDEN', },
            )

    specular: FloatVectorProperty(
            name=ms3d_str['PROP_NAME_SPECULAR'],
            description=ms3d_str['PROP_DESC_SPECULAR'],
            subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1,
            default=Ms3dSpec.DEFAULT_MATERIAL_SPECULAR,
            update=Ms3dMaterialHelper.copy_to_blender_specular,
            #options={'HIDDEN', },
            )

    emissive: FloatVectorProperty(
            name=ms3d_str['PROP_NAME_EMISSIVE'],
            description=ms3d_str['PROP_DESC_EMISSIVE'],
            subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1,
            default=Ms3dSpec.DEFAULT_MATERIAL_EMISSIVE,
            update=Ms3dMaterialHelper.copy_to_blender_emissive,
            #options={'HIDDEN', },
            )

    shininess: FloatProperty(
            name=ms3d_str['PROP_NAME_SHININESS'],
            description=ms3d_str['PROP_DESC_SHININESS'],
            min=0, max=Ms3dSpec.MAX_MATERIAL_SHININESS, precision=3, step=0.1,
            default=Ms3dSpec.DEFAULT_MATERIAL_SHININESS,
            subtype='FACTOR',
            update=Ms3dMaterialHelper.copy_to_blender_shininess,
            #options={'HIDDEN', },
            )

    transparency: FloatProperty(
            name=ms3d_str['PROP_NAME_TRANSPARENCY'],
            description=ms3d_str['PROP_DESC_TRANSPARENCY'],
            min=0, max=1, precision=3, step=0.1,
            default=0,
            subtype='FACTOR',
            update=Ms3dMaterialHelper.copy_to_blender_transparency,
            #options={'HIDDEN', },
            )

    mode: EnumProperty(
            name=ms3d_str['PROP_NAME_MODE'],
            description=ms3d_str['PROP_DESC_MODE_TEXTURE'],
            items=( (Ms3dUi.FLAG_TEXTURE_COMBINE_ALPHA,
                            ms3d_str['PROP_FLAG_TEXTURE_COMBINE_ALPHA_1'],
                            ms3d_str['PROP_FLAG_TEXTURE_COMBINE_ALPHA_2'],
                            Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA),
                    (Ms3dUi.FLAG_TEXTURE_HAS_ALPHA,
                            ms3d_str['PROP_FLAG_TEXTURE_HAS_ALPHA_1'],
                            ms3d_str['PROP_FLAG_TEXTURE_HAS_ALPHA_2'],
                            Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA),
                    (Ms3dUi.FLAG_TEXTURE_SPHERE_MAP,
                            ms3d_str['PROP_FLAG_TEXTURE_SPHERE_MAP_1'],
                            ms3d_str['PROP_FLAG_TEXTURE_SPHERE_MAP_2'],
                            Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP),
                    ),
            default=Ms3dUi.texture_mode_from_ms3d(
                    Ms3dSpec.DEFAULT_MATERIAL_MODE),
            options={'ANIMATABLE', 'ENUM_FLAG', },
            )

    texture: StringProperty(
            name=ms3d_str['PROP_NAME_TEXTURE'],
            description=ms3d_str['PROP_DESC_TEXTURE'],
            default="",
            subtype = 'FILE_PATH'
            #options={'HIDDEN', },
            )

    alphamap: StringProperty(
            name=ms3d_str['PROP_NAME_ALPHAMAP'],
            description=ms3d_str['PROP_DESC_ALPHAMAP'],
            default="",
            subtype = 'FILE_PATH'
            #options={'HIDDEN', },
            )

    comment: StringProperty(
            name=ms3d_str['PROP_NAME_COMMENT'],
            description=ms3d_str['PROP_DESC_COMMENT_MATERIAL'],
            default="",
            #options={'HIDDEN', },
            )


###############################################################################
# http://www.blender.org/documentation/blender_python_api_2_65_10/bpy.types.UIList.html
#
# uiList: ctrl-clic-edit name
# http://lists.blender.org/pipermail/bf-committers/2013-November/042113.html
# http://git.blender.org/gitweb/gitweb.cgi/blender.git/commit/f842ce82e6c92b156c0036cbefb4e4d97cd1d498
# http://git.blender.org/gitweb/gitweb.cgi/blender.git/commit/4c52e737df39e538d3b41a232035a4a1e240505d
class Ms3dGroupUILise(UIList):
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
        if self.layout_type in {'DEFAULT', 'COMPACT', }:
            if item:
                layout.prop(item, "name", text="", emboss=False, icon_value=icon)
            else:
                layout.label(text="", icon_value=icon)
        elif self.layout_type in {'GRID', }:
            layout.alignment = 'CENTER'
            layout.label(text="", icon_value=icon)

###############################################################################
class Ms3dMeshPanel(Panel):
    bl_label = ms3d_str['LABEL_PANEL_MODEL']
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = 'object'

    @classmethod
    def poll(cls, blender_context):
        return (blender_context
                and blender_context.object
                and blender_context.object.type in {'MESH', }
                and blender_context.object.data
                and blender_context.object.data.ms3d is not None
                )

    def draw_header(self, blender_context):
        layout = self.layout
        layout.label(icon='PLUGIN')

    def draw(self, blender_context):
        layout = self.layout
        custom_data = blender_context.object.data.ms3d

        row = layout.row()
        row.prop(custom_data, 'name')

        col = layout.column()
        row = col.row()
        row.prop(custom_data, 'joint_size')
        row = col.row()
        row.prop(custom_data, 'transparency_mode')
        row = col.row()
        row.prop(custom_data, 'alpha_ref', )

        row = layout.row()
        row.prop(custom_data, 'comment')


class Ms3dMaterialPanel(Panel):
    bl_label = ms3d_str['LABEL_PANEL_MATERIALS']
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = 'material'

    @classmethod
    def poll(cls, blender_context):
        return (blender_context
                and blender_context.object
                and blender_context.object.type in {'MESH', }
                and blender_context.object.data
                and blender_context.object.data.ms3d is not None
                and blender_context.material
                and blender_context.material.ms3d is not None
                )

    def draw_header(self, blender_context):
        layout = self.layout
        layout.label(icon='PLUGIN')

    def draw(self, blender_context):
        layout = self.layout
        custom_data = blender_context.material.ms3d

        row = layout.row()
        row.prop(custom_data, 'name')

        col = layout.column()
        row = col.row()
        row.prop(custom_data, 'diffuse')
        row.prop(custom_data, 'ambient')
        row = col.row()
        row.prop(custom_data, 'specular')
        row.prop(custom_data, 'emissive')
        row = col.row()
        row.prop(custom_data, 'shininess')
        row.prop(custom_data, 'transparency')

        col = layout.column()
        row = col.row()
        row.prop(custom_data, 'texture')
        row = col.row()
        row.prop(custom_data, 'alphamap')
        row = col.row()
        row.prop(custom_data, 'mode', expand=True)

        row = layout.row()
        row.prop(custom_data, 'comment')

        layout.row().operator(
                Ms3dUi.OPT_MATERIAL_APPLY,
                text=ms3d_str['ENUM_FROM_BLENDER_1'],
                icon='APPEND_BLEND').mode = 'FROM_BLENDER'
        pass


class Ms3dBonePanel(Panel):
    bl_label = ms3d_str['LABEL_PANEL_JOINTS']
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = 'bone'

    @classmethod
    def poll(cls, blender_context):
        return (blender_context
                and blender_context.object.type in {'ARMATURE', }
                and blender_context.active_bone
                and isinstance(blender_context.active_bone, Bone)
                and blender_context.active_bone.ms3d is not None
                )

    def draw_header(self, blender_context):
        layout = self.layout
        layout.label(icon='PLUGIN')

    def draw(self, blender_context):
        import bpy
        layout = self.layout
        custom_data = blender_context.active_bone.ms3d

        row = layout.row()
        row.prop(custom_data, 'name')
        row = layout.row()
        row.prop(custom_data, 'flags', expand=True)
        row = layout.row()
        row.prop(custom_data, 'color')
        row = layout.row()
        row.prop(custom_data, 'comment')


class Ms3dGroupPanel(Panel):
    bl_label = ms3d_str['LABEL_PANEL_GROUPS']
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = 'data'

    @classmethod
    def poll(cls, blender_context):
        return (blender_context
                and blender_context.object
                and blender_context.object.type in {'MESH', }
                and blender_context.object.data
                and blender_context.object.data.ms3d is not None
                )

    def draw_header(self, blender_context):
        layout = self.layout
        layout.label(icon='PLUGIN')

    def draw(self, blender_context):
        layout = self.layout
        custom_data = blender_context.object.data.ms3d
        layout.enabled = (blender_context.mode == 'EDIT_MESH') and (
                blender_context.tool_settings.mesh_select_mode[2])

        row = layout.row()

        row.template_list(
                listtype_name='Ms3dGroupUILise',
                dataptr=custom_data,
                propname='groups',
                active_dataptr=custom_data,
                active_propname='selected_group_index',
                rows=2,
                type='DEFAULT',
                )

        col = row.column(align=True)
        col.operator(
                Ms3dUi.OPT_GROUP_APPLY,
                text="", icon='ZOOMIN').mode = 'ADD_GROUP'
        col.operator(
                Ms3dUi.OPT_GROUP_APPLY,
                text="", icon='ZOOMOUT').mode = 'REMOVE_GROUP'

        index = custom_data.selected_group_index
        collection = custom_data.groups
        if (index >= 0 and index < len(collection)):
            row = layout.row()
            subrow = row.row(align=True)
            subrow.operator(
                    Ms3dUi.OPT_GROUP_APPLY,
                    text=ms3d_str['ENUM_ASSIGN_1']).mode = 'ASSIGN'
            subrow.operator(
                    Ms3dUi.OPT_GROUP_APPLY,
                    text=ms3d_str['ENUM_REMOVE_1']).mode = 'REMOVE'
            subrow = row.row(align=True)
            subrow.operator(
                    Ms3dUi.OPT_GROUP_APPLY,
                    text=ms3d_str['ENUM_SELECT_1']).mode = 'SELECT'
            subrow.operator(
                    Ms3dUi.OPT_GROUP_APPLY,
                    text=ms3d_str['ENUM_DESELECT_1']).mode = 'DESELECT'

            row = layout.row()
            row.prop(collection[index], 'flags', expand=True)

            row = layout.row()
            row.prop(collection[index], 'comment')


class Ms3dSmoothingGroupPanel(Panel):
    bl_label = ms3d_str['BL_LABEL_PANEL_SMOOTHING_GROUP']
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = 'data'

    def preview(self, dict, id, text):
        item = dict.get(id)
        if item is None:
            return "{}".format(text)
        elif item:
            return "{}:".format(text)

        return "{}.".format(text)

    def build_preview(self, blender_context):
        dict = {}
        if (blender_context.mode != 'EDIT_MESH') or (
                not blender_context.tool_settings.mesh_select_mode[2]):
            return dict

        custom_data = blender_context.object.data.ms3d
        blender_mesh = blender_context.object.data
        bm = from_edit_mesh(blender_mesh)
        layer_smoothing_group = bm.faces.layers.int.get(
                ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
        if layer_smoothing_group is not None:
            for bmf in bm.faces:
                item = dict.get(bmf[layer_smoothing_group])
                if item is None:
                    dict[bmf[layer_smoothing_group]] = bmf.select
                else:
                    if not item:
                        dict[bmf[layer_smoothing_group]] = bmf.select
        return dict

    @classmethod
    def poll(cls, blender_context):
        return (blender_context
                and blender_context.object
                and blender_context.object.type in {'MESH', }
                and blender_context.object.data
                and blender_context.object.data.ms3d is not None
                )

    def draw_header(self, blender_context):
        layout = self.layout
        layout.label(icon='PLUGIN')

    def draw(self, blender_context):
        dict = self.build_preview(blender_context)

        custom_data = blender_context.object.data.ms3d
        layout = self.layout
        layout.enabled = (blender_context.mode == 'EDIT_MESH') and (
                blender_context.tool_settings.mesh_select_mode[2])

        row = layout.row()
        subrow = row.row()
        subrow.prop(custom_data, 'apply_mode', expand=True)

        col = layout.column(align=True)
        subrow = col.row(align=True)
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 1, "1")
                ).smoothing_group_index = 1
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 2, "2")
                ).smoothing_group_index = 2
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 3, "3")
                ).smoothing_group_index = 3
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 4, "4")
                ).smoothing_group_index = 4
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 5, "5")
                ).smoothing_group_index = 5
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 6, "6")
                ).smoothing_group_index = 6
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 7, "7")
                ).smoothing_group_index = 7
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 8, "8")
                ).smoothing_group_index = 8
        subrow = col.row(align=True)
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 9, "9")
                ).smoothing_group_index = 9
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 10, "10")
                ).smoothing_group_index = 10
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 11, "11")
                ).smoothing_group_index = 11
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 12, "12")
                ).smoothing_group_index = 12
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 13, "13")
                ).smoothing_group_index = 13
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 14, "14")
                ).smoothing_group_index = 14
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 15, "15")
                ).smoothing_group_index = 15
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 16, "16")
                ).smoothing_group_index = 16
        subrow = col.row(align=True)
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 17, "17")
                ).smoothing_group_index = 17
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 18, "18")
                ).smoothing_group_index = 18
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 19, "19")
                ).smoothing_group_index = 19
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 20, "20")
                ).smoothing_group_index = 20
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 21, "21")
                ).smoothing_group_index = 21
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 22, "22")
                ).smoothing_group_index = 22
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 23, "23")
                ).smoothing_group_index = 23
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 24, "24")
                ).smoothing_group_index = 24
        subrow = col.row(align=True)
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 25, "25")
                ).smoothing_group_index = 25
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 26, "26")
                ).smoothing_group_index = 26
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 27, "27")
                ).smoothing_group_index = 27
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 28, "28")
                ).smoothing_group_index = 28
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 29, "29")
                ).smoothing_group_index = 29
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 30, "30")
                ).smoothing_group_index = 30
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 31, "31")
                ).smoothing_group_index = 31
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 32, "32")
                ).smoothing_group_index = 32
        subrow = col.row()
        subrow.operator(
                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
                text=self.preview(dict, 0, ms3d_str['LABEL_PANEL_BUTTON_NONE'])
                ).smoothing_group_index = 0


###############################################################################
def register():
    register_class(Ms3dGroupProperties)
    register_class(Ms3dModelProperties)
    register_class(Ms3dArmatureProperties)
    register_class(Ms3dJointProperties)
    register_class(Ms3dMaterialProperties)
    inject_properties()
    register_class(Ms3dSetSmoothingGroupOperator)
    register_class(Ms3dGroupOperator)

def unregister():
    unregister_class(Ms3dGroupOperator)
    unregister_class(Ms3dSetSmoothingGroupOperator)
    delete_properties()
    unregister_class(Ms3dMaterialProperties)
    unregister_class(Ms3dJointProperties)
    unregister_class(Ms3dArmatureProperties)
    unregister_class(Ms3dModelProperties)
    unregister_class(Ms3dGroupProperties)

def inject_properties():
    Mesh.ms3d = PointerProperty(type=Ms3dModelProperties)
    Armature.ms3d = PointerProperty(type=Ms3dArmatureProperties)
    Bone.ms3d = PointerProperty(type=Ms3dJointProperties)
    Material.ms3d = PointerProperty(type=Ms3dMaterialProperties)
    Action.ms3d = PointerProperty(type=Ms3dArmatureProperties)
    Group.ms3d = PointerProperty(type=Ms3dGroupProperties)

def delete_properties():
    del Mesh.ms3d
    del Armature.ms3d
    del Bone.ms3d
    del Material.ms3d
    del Action.ms3d
    del Group.ms3d

###############################################################################


###############################################################################
#234567890123456789012345678901234567890123456789012345678901234567890123456789
#--------1---------2---------3---------4---------5---------6---------7---------
# ##### END OF FILE #####
