# 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.
#

import os
import bpy
import functools
from bpy.props import (
        CollectionProperty,
        BoolProperty,
        FloatProperty,
        StringProperty,
        EnumProperty,
        )
from bpy_extras.io_utils import (
        ImportHelper,
        ExportHelper,
        orientation_helper,
        path_reference_mode,
        axis_conversion,
        )
from bpy_extras.wm_utils.progress_report import (
    ProgressReport,
    ProgressReportSubstep,
)
from pathlib import Path
from mathutils import Matrix
from . import kex_utils

# -----------------------------------------------------------------------------
#
@orientation_helper(axis_forward='Y', axis_up='Z')
class TUROK_OT_import_model(bpy.types.Operator, ImportHelper):
    """Import a Turok Model File"""
    bl_idname = "turok_object.import_model"
    bl_label = "Import Turok Model"
    bl_options = {'PRESET', 'REGISTER', 'UNDO'}

	# Properties used by the file browser
    filepath: StringProperty(name="File Path", description="File path used for importing the BIN file", default="", options={'HIDDEN'})
    files: CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN'})
    directory: StringProperty(subtype='FILE_PATH', options={'HIDDEN'})
    filter_folder: BoolProperty(name="Filter Folders", description="", default=True, options={'HIDDEN'})
    filename_ext = ".bin"
    filter_glob: StringProperty(default="*.bin", options={'HIDDEN'})

    # Custom properties
    importAnim: BoolProperty(name="Import Anim", description="Import the .Anim file at the same time from the <unzipped game>/anims/ folder. Assumes the model your loading is in <unzipped game/Turok folder>/models", default=True)
    animBakeYawOffsets: BoolProperty(name="Bake Yaw Offsets", description="For each animation, rotates the root bone by it's Yaw Offset. (Do not turn on if you intend to export this model back into Turok)", default=False)
    animStashActions: BoolProperty(name="Stash Actions", description="Stash each animation action into the NLA Tracks", default=True)
    anim60FPS: BoolProperty(name="60 FPS", description="Set the action keyframes and render fps to 60 FPS. Else will be set to Turok's standard 15 FPS", default=True)
    hideVariants: BoolProperty(name="Hide Variants", description="Hides all mesh variants except the first one", default=False)
    

    def execute(self, context):
        from . import turok_import_model
        
        global_matrix = (Matrix.Scale(1.0/kex_utils.IMPORT_EXPORT_SCALE, 4) @ axis_conversion(to_forward=self.axis_forward, to_up=self.axis_up).to_4x4())
        kex_utils.mode_set('OBJECT')
        with ProgressReport(context.window_manager) as progress:
            modelObj = turok_import_model.load(self, progress, context, self.filepath, global_matrix, self.hideVariants)
            if modelObj is None:
                return {'CANCELLED'}
        #end with
            
        #------------
        if self.importAnim:
            from . import turok_import_anim
            
            curFilePath = os.path.normpath(self.filepath)
            addon_prefs = bpy.context.preferences.addons[__package__].preferences
            
            spltPath = curFilePath
            folderNames = []
            curFileName = ""
            curFileExt = ""
            i = 0
            while True:
                dir, name = os.path.split(spltPath)
                spltPath = dir
                if not dir or not name or name.lower() == "models":
                    break
                if i != 0:
                    folderNames.append(name)
                else:
                    if name:
                        curFileName, curFileExt = os.path.splitext(name)
                i += 1
                
            folderNames.append("")
            postFolders = ""
            if folderNames:
                postFolders = functools.reduce(os.path.join, folderNames)
            
            animPathNoExt = os.path.join(addon_prefs.gameDirectory, "anims", postFolders) + curFileName;
            animPaths = [animPathNoExt + "_anim" + curFileExt, animPathNoExt + curFileExt]
            for animPath in animPaths:
                if os.path.isfile(animPath):
                    turok_import_anim.load(self, context, animPath, global_matrix, self.animBakeYawOffsets, self.animStashActions, self.anim60FPS)
                    break;
                #end if
            #end for
        #end if
        
        return {'FINISHED'}
    #end func
    
    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        
        row = layout.row()
        row.label(text="Model Options:", icon="MONKEY")
        box = layout.box()
        row = box.row()
        row.prop(self, "hideVariants")
        row = box.row()
        row.prop(self, "importAnim")
        
        if self.importAnim:
            row = layout.row()
            row.label(text="Animation Options:", icon="ANIM")
            box = layout.box()
            row = box.row()
            row.prop(self, "anim60FPS")
            row = box.row()
            row.prop(self, "animStashActions")
            row = box.row()
            row.prop(self, "animBakeYawOffsets")
        
        # row = layout.row()
        # row = layout.row()
        # row.label(text="Misc Options:", icon="PREFERENCES")
        # box = layout.box()
        # row = box.row()
        # row.prop(self, "axis_forward")
        # row = box.row()
        # row.prop(self, "axis_up")
    #end func
    
#end class
# -----------------------------------------------------------------------------
#
@orientation_helper(axis_forward='Y', axis_up='Z')
class TUROK_OT_import_anim(bpy.types.Operator, ImportHelper):
    """Import a Turok anim File"""
    bl_idname = "turok_object.import_anim"
    bl_label = "Import Turok Anim"
    bl_options = {'PRESET', 'REGISTER', 'UNDO'}

    files: CollectionProperty(name="File Path",
                          description="File path used for importing the BIN file",
                          type=bpy.types.OperatorFileListElement, options={'HIDDEN'})

    directory: StringProperty(options={'HIDDEN'})

    filename_ext = ".bin"
    filter_glob: StringProperty(default="*.bin", options={'HIDDEN'})
    
    animBakeYawOffsets: BoolProperty(name="Bake Yaw Offsets", description="For each animation, rotates the root bone by it's Yaw Offset. (Do not turn on if you intend to export this model back into Turok)", default=False)
    stashActions: BoolProperty(name="Stash Actions", description="Stash each animation action into the NLA Tracks", default=True)
    anim60FPS: BoolProperty(name="60 FPS", description="Set the action keyframes and render fps to 60 FPS. Else will be set to Turok's standard 15 FPS", default=True)

    @classmethod
    def poll(cls, context):
        return context.object is not None

    def execute(self, context):
        from . import turok_import_anim
        
        obj = context.object
        if obj is None:
            kex_utils.show_error("[Import Anim] No Object Selected")
            return {'CANCELLED'}
            
        kex_utils.mode_set('OBJECT')
        global_matrix = (Matrix.Scale(1.0/kex_utils.IMPORT_EXPORT_SCALE, 4) @ axis_conversion(to_forward=self.axis_forward, to_up=self.axis_up).to_4x4())
        successful = turok_import_anim.load(self, context, self.filepath, global_matrix, self.animBakeYawOffsets, self.stashActions, self.anim60FPS)
        if not successful:
            return {'CANCELLED'}
        #end if

        return {'FINISHED'}
    #end func
    
    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        
        row = layout.row()
        row.label(text="Animation Options:", icon="ANIM")
        box = layout.box()
        row = box.row()
        row.prop(self, "anim60FPS")
        row = box.row()
        row.prop(self, "stashActions")
        row = box.row()
        row.prop(self, "animBakeYawOffsets")
        
        # row = layout.row()
        # row = layout.row()
        # row.label(text="Misc Options:", icon="PREFERENCES")
        # box = layout.box()
        # row = box.row()
        # row.prop(self, "axis_forward")
        # row = box.row()
        # row.prop(self, "axis_up")
    #end func

#end class
# -----------------------------------------------------------------------------
#
@orientation_helper(axis_forward='Y', axis_up='Z')
class TUROK_OT_export_model(bpy.types.Operator, ExportHelper):
    """Export a Turok Model"""
    bl_idname = "turok_object.export_model"
    bl_label = 'Export Turok Model'
    bl_options = {'PRESET'}

    filename_ext = ".bin"
    filter_glob: StringProperty(default="*.bin", options={'HIDDEN'})

    exportMaterials: BoolProperty(name="Export Materials", description="Export all used materials to .kmat files relative to the export directory", default=True)
    copyTextures: BoolProperty(name="Copy Textures", description="Copies all used textures to their path relative to the export directory", default=False)
    copyTextureSets: BoolProperty(name="Copy All Textures in Texture Sets", description="Copies all textures in every texture set this model uses", default=False)
    exportAnim: BoolProperty(name="Export Anim", description="Export all animations the model uses into a .anim file", default=True)
    animNLATracks: BoolProperty(name="NLA Tracks", description="Exported animations must be in the Armatures NLA Tracks", default=False)
    useModelsDir: BoolProperty(name="Use Models Directory", description="Exports materials, textures, and anims to parent folder of \"models\" + the sub folders after the models folder if it exists in the export path.", default=True)

    @classmethod
    def poll(cls, context):
        return context.object is not None
        
    def execute(self, context):
        from . import turok_export_model
        
        keywords = self.as_keywords(ignore=("axis_forward",
                                            "axis_up",
                                            "check_existing",
                                            "filter_glob",
                                            ))

        global_matrix = (Matrix.Scale(kex_utils.IMPORT_EXPORT_SCALE, 4) @ axis_conversion(to_forward=self.axis_forward,to_up=self.axis_up).to_4x4())
        keywords["global_matrix"] = global_matrix
        kex_utils.mode_set('OBJECT')
        return turok_export_model.save(context, **keywords)
    #end func
        
    def invoke(self, context, event):
        obj = context.object
        #check obj parents for an armature object and use that instead if found
        while True:
            if obj.parent is None or obj.parent.type != "ARMATURE":
                break
            obj = obj.parent
        self.filepath = obj.name
        
        validateResults = kex_utils.T1ValidateModel(context, obj)
        if len(validateResults["errors"]) > 0 and len(validateResults["warnings"]) > 0:
            kex_utils.show_error("%s" % (validateResults["errors"][0]), "Errors and Warnings - See System Console for full details", toConsole=False)
        elif len(validateResults["errors"]) > 0:
            kex_utils.show_error("%s" % (validateResults["errors"][0]), "Error - See System Console for full details", toConsole=False)
        #end if
        if len(validateResults["errors"]) > 0:
            for message in validateResults["errors"]:
                print("[Export Model Error] %s" % (message))
            #end for
            return {'CANCELLED'}
        #end if
        if len(validateResults["warnings"]) > 0:
            for message in validateResults["warnings"]:
                print("[Export Model Warning] %s" % (message))
            #end for
        #end if

        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}
    #end func
        
    def draw(self, context):
        layout = self.layout
        #layout.use_property_split = True
        
        row = layout.row()
        row.label(text="Model Options:", icon="MONKEY")
        box = layout.box()
        row = box.row()
        row.prop(self, "exportMaterials")
        row = box.row()
        row.prop(self, "copyTextures")
        if self.copyTextures:
            row = box.row()
            row.separator()
            row.prop(self, "copyTextureSets")
        #end if
        row = box.row()
        row.prop(self, "exportAnim")
        row = box.row()
        row.prop(self, "useModelsDir")
        #end if
        
        if self.exportAnim:
            row = layout.row()
            row.label(text="Animation Options:", icon="ANIM")
            box = layout.box()
            row = box.row()
            row.prop(self, "animNLATracks")
        #end if
        
        # row = layout.row()
        # row = layout.row()
        # row.label(text="Misc Options:", icon="PREFERENCES")
        # box = layout.box()
        # row = box.row()
        # row.prop(self, "axis_forward")
        # row = box.row()
        # row.prop(self, "axis_up")
    #end func
#end class
# -----------------------------------------------------------------------------
#
@orientation_helper(axis_forward='Y', axis_up='Z')
class TUROK_OT_export_anim(bpy.types.Operator, ExportHelper):
    """Export a Turok Animation"""
    bl_idname = "turok_object.export_anim"
    bl_label = 'Export Turok Anim'
    bl_options = {'PRESET'}

    filename_ext = ".bin"
    filter_glob: StringProperty(default="*.bin", options={'HIDDEN'})
    animNLATracks: BoolProperty(name="NLA Tracks", description="Exported animations must be in the Armatures NLA Tracks", default=False)

    @classmethod
    def poll(cls, context):
        return context.object is not None
    #end func
        
    def execute(self, context):
        from . import turok_export_anim

        keywords = self.as_keywords(ignore=("axis_forward",
                                            "axis_up",
                                            "check_existing",
                                            "filter_glob",
                                            ))

        global_matrix = (Matrix.Scale(kex_utils.IMPORT_EXPORT_SCALE, 4) @ axis_conversion(to_forward=self.axis_forward,to_up=self.axis_up).to_4x4())
        keywords["global_matrix"] = global_matrix
        kex_utils.mode_set('OBJECT')
        return turok_export_anim.save(context, **keywords)
    #end func

    def invoke(self, context, event):
        obj = context.object
        #check obj parents for an armature object and use that instead if found
        while True:
            if obj.parent is None or obj.parent.type != "ARMATURE":
                break
            obj = obj.parent
        #end while
        
        self.filepath = obj.name + "_anim"
        
        validateResults = kex_utils.T1ValidateModel(context, obj)
        if len(validateResults["errors"]) > 0 and len(validateResults["warnings"]) > 0:
            kex_utils.show_error("%s" % (validateResults["errors"][0]), "Errors and Warnings - See System Console for full details", toConsole=False)
        elif len(validateResults["errors"]) > 0:
            kex_utils.show_error("%s" % (validateResults["errors"][0]), "Error - See System Console for full details", toConsole=False)
        #end if
        if len(validateResults["errors"]) > 0:
            for message in validateResults["errors"]:
                print("[Export Anim Error] %s" % (message))
            #end for
            return {'CANCELLED'}
        #end if
        if len(validateResults["warnings"]) > 0:
            for message in validateResults["warnings"]:
                print("[Export Anim Warning] %s" % (message))
            #end for
        #end if

        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}
    #end func
    
    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        
        row = layout.row()
        row.label(text="Animation Options:", icon="ANIM")
        box = layout.box()
        row = box.row()
        row.prop(self, "animNLATracks")
        
        # row = layout.row()
        # row = layout.row()
        # row.label(text="Misc Options:", icon="PREFERENCES")
        # box = layout.box()
        # row = box.row()
        # row.prop(self, "axis_forward")
        # row = box.row()
        # row.prop(self, "axis_up")
    #end func
#end class
# -----------------------------------------------------------------------------
#
@orientation_helper(axis_forward='Y', axis_up='Z')
class TUROK_OT_import_map(bpy.types.Operator, ImportHelper):
    """Import a Turok Map File"""
    bl_idname = "turok_object.import_map"
    bl_label = "Import Turok Map"
    bl_options = {'PRESET', 'REGISTER', 'UNDO'}

	# Properties used by the file browser
    filepath: StringProperty(name="File Path", description="File path used for importing the MAP file", default="", options={'HIDDEN'})
    files: CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN'})
    directory: StringProperty(subtype='FILE_PATH', options={'HIDDEN'})
    filter_folder: BoolProperty(name="Filter Folders", description="", default=True, options={'HIDDEN'})
    filename_ext = ".map"
    filter_glob: StringProperty(default="*.map", options={'HIDDEN'})

    ObjectTypes = [
        ("ALL", "All", "", 0),
        ("STATICMESHES", "Staticmeshes", "", 1),
        ("ACTORS", "Actors", "", 2),
    ]

    # Custom properties
    importType: bpy.props.EnumProperty(items=ObjectTypes, name="Import Objects", description="Import the Staticmeshes and/or Actors into the map", default="ALL")
    importSectors: bpy.props.BoolProperty(name="Sectors", description="Create the Sectors Mesh Object", default=True)
    visTables: bpy.props.BoolProperty(name="Visibility Tables", description="Import the visibility tables if there are any in the map", default=True)

    def execute(self, context):
        from . import turok_import_map
        
        global_matrix = (Matrix.Scale(1.0/kex_utils.IMPORT_EXPORT_SCALE, 4) @ axis_conversion(to_forward=self.axis_forward, to_up=self.axis_up).to_4x4())
        kex_utils.mode_set('OBJECT')
        successful = turok_import_map.load(self, context, self.filepath, global_matrix, self.importType, self.importSectors, self.visTables)
        if not successful:
            return {'CANCELLED'}
                    
        return {'FINISHED'}
    #end func
    
    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        
        row = layout.row()
        row.label(text="Map Options:", icon="MONKEY")
        box = layout.box()
        row = box.row()
        row.prop(self, "importType")
        row = box.row()
        row.prop(self, "importSectors")
        row = box.row()
        row.prop(self, "visTables")
        
        # row = layout.row()
        # row = layout.row()
        # row.label(text="Misc Options:", icon="PREFERENCES")
        # box = layout.box()
        # row = box.row()
        # row.prop(self, "axis_forward")
        # row = box.row()
        # row.prop(self, "axis_up")
    #end func
    
#end class

# -----------------------------------------------------------------------------
#
@orientation_helper(axis_forward='Y', axis_up='Z')
class TUROK_OT_export_map(bpy.types.Operator, ExportHelper):
    """Export a Turok Map"""
    bl_idname = "turok_object.export_map"
    bl_label = 'Export Turok Map'
    bl_options = {'PRESET'}

    filename_ext = ".map"
    filter_glob: StringProperty(default="*.map", options={'HIDDEN'})

    @classmethod
    def poll(cls, context):
        return context.object is not None
        
    def execute(self, context):
        from . import turok_export_map
        
        keywords = self.as_keywords(ignore=("axis_forward",
                                            "axis_up",
                                            "check_existing",
                                            "filter_glob",
                                            ))
        global_matrix = (Matrix.Scale(kex_utils.IMPORT_EXPORT_SCALE, 4) @ axis_conversion(to_forward=self.axis_forward,to_up=self.axis_up).to_4x4())
        keywords["global_matrix"] = global_matrix
        kex_utils.mode_set('OBJECT')
        return turok_export_map.save(context, **keywords)
    #end func
        
    # def invoke(self, context, event):
        # obj = context.object
        # #check obj parents for an armature object and use that instead if found
        # while True:
            # if obj.parent is None or obj.parent.type != "ARMATURE":
                # break
            # obj = obj.parent
        # self.filepath = obj.name
        
        # context.window_manager.fileselect_add(self)
        # return {'RUNNING_MODAL'}
    # #end func
        
    # def draw(self, context):
        # layout = self.layout
        # layout.use_property_split = True
        
        # row = layout.row()
        # row.label(text="Model Options:", icon="MONKEY")
        # box = layout.box()
        # row = box.row()
        # row.prop(self, "exportMaterials")
        # row = box.row()
        # row.prop(self, "copyTextures")
        # row = box.row()
        # row.prop(self, "exportAnim")
        # row = box.row()
        # row.prop(self, "useModelsDir")
        # #end if
        
        # if self.exportAnim:
            # row = layout.row()
            # row.label(text="Animation Options:", icon="ANIM")
            # box = layout.box()
            # row = box.row()
            # row.prop(self, "animNLATracks")
        # #end if
        
        # # row = layout.row()
        # # row = layout.row()
        # # row.label(text="Misc Options:", icon="PREFERENCES")
        # # box = layout.box()
        # # row = box.row()
        # # row.prop(self, "axis_forward")
        # # row = box.row()
        # # row.prop(self, "axis_up")
    # #end func
#end class

# -----------------------------------------------------------------------------
#
def menu_func_import_model(self, context):
    self.layout.operator(TUROK_OT_import_model.bl_idname, text="Turok Model (.bin)")

# -----------------------------------------------------------------------------
#
def menu_func_import_anim(self, context):
    self.layout.operator(TUROK_OT_import_anim.bl_idname, text="Turok Anim (.bin)")

# -----------------------------------------------------------------------------
#
def menu_func_export_model(self, context):
    self.layout.operator(TUROK_OT_export_model.bl_idname, text="Turok Model (.bin)")

# -----------------------------------------------------------------------------
#
def menu_func_export_anim(self, context):
    self.layout.operator(TUROK_OT_export_anim.bl_idname, text="Turok Anim (.bin)")

# -----------------------------------------------------------------------------
#
def menu_func_import_map(self, context):
    self.layout.operator(TUROK_OT_import_map.bl_idname, text="Turok Map (.map)")

    # -----------------------------------------------------------------------------
#
def menu_func_export_map(self, context):
    self.layout.operator(TUROK_OT_export_map.bl_idname, text="Turok Map (.map)")

# -----------------------------------------------------------------------------
#
def register():
    bpy.utils.register_class(TUROK_OT_import_model)
    bpy.utils.register_class(TUROK_OT_import_anim)
    bpy.utils.register_class(TUROK_OT_export_model)
    bpy.utils.register_class(TUROK_OT_export_anim)
    bpy.utils.register_class(TUROK_OT_import_map)
    bpy.utils.register_class(TUROK_OT_export_map)
    bpy.types.TOPBAR_MT_file_import.append(menu_func_import_model)
    bpy.types.TOPBAR_MT_file_import.append(menu_func_import_anim)
    bpy.types.TOPBAR_MT_file_export.append(menu_func_export_model)
    bpy.types.TOPBAR_MT_file_export.append(menu_func_export_anim)
    bpy.types.TOPBAR_MT_file_import.append(menu_func_import_map)
    # bpy.types.TOPBAR_MT_file_export.append(menu_func_export_map)
#end func

# -----------------------------------------------------------------------------
#
def unregister():
    bpy.utils.unregister_class(TUROK_OT_import_model)
    bpy.utils.unregister_class(TUROK_OT_import_anim)
    bpy.utils.unregister_class(TUROK_OT_export_model)
    bpy.utils.unregister_class(TUROK_OT_export_anim)
    bpy.utils.unregister_class(TUROK_OT_import_map)
    bpy.utils.unregister_class(TUROK_OT_export_map)
    bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_model)
    bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_anim)
    bpy.types.TOPBAR_MT_file_export.remove(menu_func_export_model)
    bpy.types.TOPBAR_MT_file_export.remove(menu_func_export_anim)
    bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_map)
    # bpy.types.TOPBAR_MT_file_export.remove(menu_func_export_map)
#end func
