# 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 bpy
import os
from . import kex_utils
from bpy_extras.io_utils import ImportHelper

T1MaterialAlphaFuncs = [
    ("GEQUAL", "gequal", "", 0),
    ("EQUAL", "equal", "", 1),
    ("LEQUAL", "lequal", "", 2),
]

T1MaterialSorts = [
    ("DEFAULT", "default", "", 0),
    ("MASKED", "masked", "", 1),
    ("TRANSLUCENT", "translucent", "", 2),
    ("WATER", "water", "", 3),
    ("CUSTOM1", "custom1", "", 4),
    ("CUSTOM2", "custom2", "", 5),
    ("CUSTOM3", "custom3", "", 6)
]

T1MaterialCulls = [
    ("AUTO", "Use Settings", 'Uses the material setting "Backface Culling"', 0),
    ("BACK", "Back", "", 1),
    ("FRONT", "Front", "", 2),
    ("NONE", "None", "", 3),
]

T1MaterialQuickSetups = [
    ("ONESIDE", "One Sided", "", 0),
    ("TWOSIDE", "Two Sided", "", 1),
    ("WATER", "Water", "", 2)
]

# -----------------------------------------------------------------------------
#
class TurokMaterialParamsPropGroup(bpy.types.PropertyGroup):
    def get_value_type(self):
        return self.get("valueType", 1)
        
    def set_value_type(self, value):
        self["valueType"] = value
        self["valueTypeBool"] = self.valueType == 1
        
    def get_value_type_bool(self):
        return self.get("valueTypeBool", True)
        
    def set_value_type_bool(self, value):
        self["valueTypeBool"] = value
        self["valueType"] = 1 if self.valueTypeBool else 0
        
    name: bpy.props.StringProperty(name="Name")
    valueType: bpy.props.IntProperty(name="realType", description="Param type to Int or Float", default = 1, get=get_value_type, set=set_value_type)
    value: bpy.props.StringProperty(name = "Value", description="Param value")
    #used for ui only
    valueTypeBool: bpy.props.BoolProperty(name="Type", description="Param type to Int or Float", default = 1, get=get_value_type_bool, set=set_value_type_bool)

# -----------------------------------------------------------------------------
#
class TUROK_UL_material_param_props(bpy.types.UIList):
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        row = layout.row()
        row.prop(item, "name")
        row = layout.row()
        row.prop(item, "valueTypeBool", icon_only=True, icon="FILE_FONT" if item.valueTypeBool else "ITALIC")
        row = layout.row()
        row.prop(item, "value", text="")
        
# -----------------------------------------------------------------------------
#
class TUROK_OT_add_material_param(bpy.types.Operator):
    bl_idname = "turok_material.param_add"
    bl_label = "Add Turok Material Param"
    bl_description = "Adds a new Param"
    bl_options = {'UNDO'}

    @classmethod
    def poll(cls, context):
        obj = context.object
        return obj is not None and obj.active_material is not None
        
    def execute(self, context):
        obj = context.object
        mat = obj.active_material
        mat = kex_utils.get_material_texset_index(mat, 0)
        if mat is not None:
            mat.t1.params.add()

        return {'FINISHED'}
       
# -----------------------------------------------------------------------------
# 
class TUROK_OT_remove_material_param(bpy.types.Operator):
    bl_idname = "turok_material.param_remove"
    bl_label = "Remove Turok Material Param"
    bl_description = "Removes selected Param"
    bl_options = {'UNDO'}

    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or obj.active_material is None:
            return False
        mat = kex_utils.get_material_texset_index(obj.active_material , 0)    
        return len(mat.t1.params) > 0
    #end func
    
    def execute(self, context):
        obj = context.object
        mat = obj.active_material
        mat = kex_utils.get_material_texset_index(mat, 0)
        paramsLength = len(mat.t1.params)
        if mat.t1.paramsIndex >= 0 and mat.t1.paramsIndex < paramsLength:
            mat.t1.params.remove(mat.t1.paramsIndex)
            mat.t1.paramsIndex -= 1
        else:
            mat.t1.params.remove(paramsLength - 1)

        return {'FINISHED'}
    #end func
#end class
 
# -----------------------------------------------------------------------------
#
class TUROK_OT_material_shader_list(bpy.types.Operator):
    """Select one of the shaders that comes with TurokEX from a list"""
    bl_idname = "turok_material.mat_shader_list"
    bl_label = "Show Material Shader List"
    bl_options = {'UNDO'}
    bl_property = "enumprop"
    
    def items_cb(self, context):
        items = []
        for key, value in kex_utils.turok1Defs["shaders"].items():
            items.append((key, value["description"], ""))
        #end for
        return items
    #end func

    enumprop: bpy.props.EnumProperty(items=items_cb)

    @classmethod
    def poll(cls, context):
        obj = context.object
        return obj is not None and obj.active_material is not None and "shaders" in kex_utils.turok1Defs
    #end func
        
    def execute(self, context):
        mat = context.object.active_material
        mat = kex_utils.get_material_texset_index(mat, 0)
        mat.t1.shaderPath = self.enumprop
        
        return {'FINISHED'}
    #end func

    def invoke(self, context, event):
        context.window_manager.invoke_search_popup(self)
        
        return {'FINISHED'}
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TUROK_OT_material_quicktex_import(bpy.types.Operator):
    """Setup the material quickly with a texture"""
    bl_idname = "turok_material.mat_quicktex_import"
    bl_label = "Quick Material Texture Setup"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or obj.active_material is None:
            return False
        mat = kex_utils.get_material_texset_index(obj.active_material , 0)    
        return mat == obj.active_material and mat.t1.quickImage is not None
    #end func
        
    def execute(self, context):
        mat = context.object.active_material
        mat = kex_utils.get_material_texset_index(mat, 0)

        imageFilePath = os.path.normpath(mat.t1.quickImage.filepath).strip('\\')
        imageFileDir, imageFileName = os.path.split(imageFilePath)
        imageFileNameNoExt = os.path.splitext(imageFileName)[0]
        realMatName, textureSetIndex = kex_utils.get_real_material_name(mat.name)
        if textureSetIndex < 0:
            mat.name = kex_utils.get_texset_material_name(imageFileNameNoExt, 0)
        else:
            mat.name = kex_utils.get_texset_material_name(realMatName, textureSetIndex)
        #end if
        
        #setup nodes with image
        mat.node_tree.nodes.clear()
        moNode = mat.node_tree.nodes.new("ShaderNodeOutputMaterial")
        moNode.location = [300, 300]
        bsdf = mat.node_tree.nodes.new("ShaderNodeBsdfPrincipled")
        bsdf.location = [10, 300]
        bsdf.inputs['Specular'].default_value = 0.0
        mat.node_tree.links.new(moNode.inputs['Surface'], bsdf.outputs['BSDF'])
        texShaderNode = mat.node_tree.nodes.new('ShaderNodeTexImage')
        texShaderNode.label = imageFileName
        texShaderNode.location = [-280.0, 300.0]
        texShaderNode.projection = 'FLAT'
        texShaderNode.interpolation = 'Linear'
        texShaderNode.extension = 'REPEAT'
        texShaderNode.image = mat.t1.quickImage;
        
        mat.node_tree.links.new(bsdf.inputs['Base Color'], texShaderNode.outputs['Color'])
        mat.node_tree.links.new(bsdf.inputs['Alpha'], texShaderNode.outputs['Alpha'])
        for shaderNode in mat.node_tree.nodes:
            shaderNode.select = False
            
        #Setup Material Props
        mat.t1.cull = "AUTO"
        if mat.t1.quickImageSetup == "ONESIDE" or mat.t1.quickImageSetup == "TWOSIDE":
            if mat.t1.quickImageSetup == "ONESIDE":
                mat.use_backface_culling = True
            else:
                mat.use_backface_culling = False
            #end if
            if mat.t1.quickImageAlpha:
                mat.t1.blend = True
                mat.t1.alphaTest = True
                mat.t1.sort = "MASKED"
            else:
                mat.t1.blend = False
                mat.t1.alphaTest = False
                mat.t1.sort = "DEFAULT"
            #end if
            mat.t1.shaderPath = "progs/world"
            mat.t1.depthTest = True
        else: #"WATER"
            mat.use_backface_culling = False
            mat.t1.shaderPath = "progs/colorOverlay"
            mat.t1.blend = True
            mat.t1.sort = "WATER"
            mat.t1.depthTest = True
            mat.t1.alphaTest = False
        #end if
        
        #Reset convenience function props
        mat.t1.quickImage = None
        mat.t1.quickImageSetup = "ONESIDE"
        mat.t1.quickImageAlpha = False
        mat.t1.showConButtons = False
        return {'FINISHED'}
    #end func

#end class

# -----------------------------------------------------------------------------
#
kmat_import_enum_items = []
kmat_import_materials = []
kmat_import_isShowingList = False
# -----------------------------------------------------------------------------
#
class TUROK_OT_material_import(bpy.types.Operator, ImportHelper):
    """Import existing material over current material"""
    bl_idname = "turok_material.mat_import"
    bl_label = "Import Material"
    bl_options = {'PRESET', 'REGISTER', 'UNDO'}

	# Properties used by the file browser
    filepath: bpy.props.StringProperty(name="File Path", description="File path used for importing the material from the KMAT file", default="", options={'HIDDEN'})
    files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN'})
    directory: bpy.props.StringProperty(subtype='FILE_PATH', options={'HIDDEN'})
    filter_folder: bpy.props.BoolProperty(name="Filter Folders", description="", default=True, options={'HIDDEN'})
    filename_ext = ".kmat"
    filter_glob: bpy.props.StringProperty(default="*.kmat", options={'HIDDEN'})
    
    bl_property = "enumprop"
    
    def items_cb(self, context):
        global kmat_import_enum_items
        return kmat_import_enum_items
        
    enumprop: bpy.props.EnumProperty(items=items_cb, options={'HIDDEN'})

    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or obj.active_material is None:
            return False
        mat = kex_utils.get_material_texset_index(obj.active_material , 0)    
        return mat == obj.active_material
    #end func

    def execute(self, context):
        obj = context.object
        mat = obj.active_material
        mat = kex_utils.get_material_texset_index(mat, 0)
        global kmat_import_materials
        global kmat_import_enum_items
        global kmat_import_isShowingList
        if kmat_import_isShowingList:
            kmat_import_isShowingList = False
            kex_utils.kmat_to_material(kmat_import_materials[self.enumprop], mat)
            kmat_import_materials.clear()
            kmat_import_enum_items.clear()
        else:
            #read filepath. if exists then get all the materials and add to a list of materials (global var)
            kmat_import_materials = kex_utils.T1ReadKMat(self.filepath)
            if kmat_import_materials is not None and len(kmat_import_materials) > 0:
                kmat_import_enum_items.clear()
                i = 0
                for key in kmat_import_materials:
                    kmat_import_enum_items.append((key, key, "", i))
                    i += 1
                #end for
                kmat_import_isShowingList = True
                context.window_manager.invoke_search_popup(self)
            else:
                kex_utils.show_error("[Import Material] %s contains no valid materials." % (self.filepath))
            #end if
        #end if
        return {'FINISHED'}
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TurokMaterialSettings(bpy.types.PropertyGroup):
    isMatPathCustom: bpy.props.BoolProperty(name="Use Custom Material Path", description="If is turned off will automatically set the material file path to materials/mat_<ExportFileName>", default=False)
    matPath: bpy.props.StringProperty(name="Material Path", description="The path to the .kmat file where this material should be placed inside. Do not include the .kmat extension.", default="materials/mat_customName", subtype = "FILE_NAME")
    shaderPath: bpy.props.StringProperty(name="Shader Path", default="progs/world", subtype = "FILE_NAME")
    fullBright: bpy.props.BoolProperty(name="Full Bright", description="Do not use any lighting")
    blend: bpy.props.BoolProperty(name="Blend", description="Turn on for material transparency")
    alphaTest: bpy.props.BoolProperty(name="Alpha Test", description="Turn ON if you have a texture that uses alpha and use sort type Masked. Discards pixels depending on the outcome of the Alpha Func comparison between the textures alpha value and the Alpha Mask value.")
    alphaFunc: bpy.props.EnumProperty(name="Alpha Func", items = T1MaterialAlphaFuncs, default = "GEQUAL", description="The alpha comparison function (default: gequal)")
    alphaMask: bpy.props.FloatProperty(name="Alpha Mask", default = 0.6525, subtype="FACTOR", soft_min=0.0, soft_max=1.0, min=0.0, max=1.0, precision=6, description="The alpha cut off value to discard the pixel. (default: 0.6525)")
    sort: bpy.props.EnumProperty(name="Sort", items = T1MaterialSorts, default = "DEFAULT", description="The draw layer to use")
    cull: bpy.props.EnumProperty(name="Cull", items = T1MaterialCulls, default = "AUTO", description="Which side to render Triangles")
    noDraw: bpy.props.BoolProperty(name="No Draw", description="Do not render")
    depthTest: bpy.props.BoolProperty(name="Depth Test", default=True, description="Draws behind objects based on Depth buffer, otherwise will always draw on top of current render.")
    noDepthMask: bpy.props.BoolProperty(name="No Depth Mask", description="Turns off depth mask. The depth mask masks out areas on the model depending on how far from the camera it is.")
    diffuseColor: bpy.props.FloatVectorProperty(name = "Diffuse Color", subtype='COLOR', size = 4, default=[1.0,1.0,1.0,1.0], soft_min=0.0, soft_max=1.0, min=0.0, max=1.0, precision=6)
    params: bpy.props.CollectionProperty(name="Params", type = TurokMaterialParamsPropGroup, description="A list of uniform variables the shader will use. Assign the correct names from the shader and specify whether the variable is an Integer or Float. Then assign it's value.")
    
    #blender editor props
    paramsIndex: bpy.props.IntProperty(name="", default = -1)
    showParams: bpy.props.BoolProperty()
    showConButtons: bpy.props.BoolProperty(default=True)
    quickImage: bpy.props.PointerProperty(name="Quick Setup Image", type = bpy.types.Image)
    quickImageSetup: bpy.props.EnumProperty(name="Setup Type", items = T1MaterialQuickSetups, default = "ONESIDE")
    quickImageAlpha: bpy.props.BoolProperty(name="Image Has Alpha")
    
    def draw(self, context, obj):
        layout = obj.layout
        mat = kex_utils.get_material_texset_index(context.active_object.active_material, 0)
            
        # #check if theres already a none texture set suffix material as the first index then do nothing
        # if (textureSetIndex == 0 and baseMat is not None) or (textureSetIndex > 0):
            # row = layout.row()
            # row.label(text="This material is a variant of")
            # box = layout.box()
            # row = box.row()
            # row.label(text=matName)
            # row = layout.row()
            # row.label(text="Select that material instead to edit it's properties.")
            # return
        # #end if
        
        if mat == context.active_object.active_material:
            box = layout.box()
            row = box.row()
            row.prop(mat.t1, "showConButtons", icon="TRIA_DOWN" if mat.t1.showConButtons else "TRIA_RIGHT", icon_only=True, emboss=False)
            row.label(text="Convenience Functions")
            if mat.t1.showConButtons:
                row = box.row()
                row.template_ID(mat.t1, "quickImage", new="image.new", open="image.open", live_icon=True)
                row = box.row()
                row.prop(mat.t1, "quickImageSetup")
                row = box.row()
                row.alignment = "RIGHT"
                row.prop(mat.t1, "quickImageAlpha")
                row = box.row()
                row.separator()
                row.separator()
                row.separator()
                row.operator(TUROK_OT_material_quicktex_import.bl_idname, text="Quick Image Setup")
                row.separator()
                row.separator()
                row.separator()
                box.box()
                row = box.row()
                row.operator(TUROK_OT_material_import.bl_idname, icon="IMPORT", text="Import Existing Material from .kmat")
            #end if
        else:
            row = layout.row()
            row.alignment = "CENTER"
            row.alert = True
            row.label(text="Viewing: %s" % (mat.name))
        #end if
        box = layout.box()
        row = box.row()
        row.prop(mat.t1, "isMatPathCustom")
        if mat.t1.isMatPathCustom:
            row = box.row()
            row = row.split(factor=0.3)
            row.label(text="Material Path")
            row.prop(mat.t1, "matPath", text="")
        #end if
        row = layout.row(align=True)
        row.prop(mat.t1, "shaderPath", text="Shader")
        row.operator(TUROK_OT_material_shader_list.bl_idname, icon='COLLAPSEMENU', text="")
        row = layout.row()
        row.prop(mat.t1, "diffuseColor")
        row = layout.row()
        row.prop(mat.t1, "sort")
        row = layout.row()
        if mat.t1.cull == "AUTO":
            row = row.split(factor=0.8)
            row.prop(mat.t1, "cull")
            row.label(text="(Back)" if mat.use_backface_culling else "(None)")
        else:
            row.prop(mat.t1, "cull")
        #end if
        row = layout.row()
        row.prop(mat.t1, "blend")
        row.prop(mat.t1, "fullBright")
        row = layout.row()
        row.prop(mat.t1, "depthTest")
        row.prop(mat.t1, "noDraw")
        row = layout.row()
        row.prop(mat.t1, "noDepthMask")
        row = layout.row()
        row.prop(mat.t1, "alphaTest")
        col = row.column()
        col.enabled = mat.t1.alphaTest
        col.prop(mat.t1, "alphaFunc", text="Func")
        row = layout.row()
        row.enabled = mat.t1.alphaTest
        row.prop(mat.t1, "alphaMask")
        box = layout.box()
        row = box.row()
        row.prop(mat.t1, "showParams", icon="TRIA_DOWN" if mat.t1.showParams else "TRIA_RIGHT", icon_only=True, emboss=False)
        paramsText = 'Params'
        if len(mat.t1.params) > 0:
            paramsText = 'Params (%d)' % (len(mat.t1.params))
        row.label(text=paramsText)
        if mat.t1.showParams:
            row = box.row()
            row.template_list('TUROK_UL_material_param_props', "TurokMaterialParamsPropGroup", mat.t1, "params", mat.t1, "paramsIndex", rows=1, maxrows=2, type='DEFAULT')
            row = box.row()
            row.operator(TUROK_OT_add_material_param.bl_idname, text="Add Param", icon='ADD')
            row.operator(TUROK_OT_remove_material_param.bl_idname, text="Remove Param", icon='REMOVE')
        #end if
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TurokMaterialPanel(bpy.types.Panel):
    bl_label = "Turok Material"
    bl_idname = "MATERIAL_PT_TurokMatProp"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "material"

    def draw(self, context):
        if context.active_object is None or context.active_object.active_material is None:
            return
        
        mat = context.active_object.active_material
        mat = kex_utils.get_material_texset_index(mat, 0)
        mat.t1.draw(context, self)
    #end func
#end class

# -----------------------------------------------------------------------------
#
def register():
    bpy.utils.register_class(TurokMaterialParamsPropGroup)
    bpy.utils.register_class(TUROK_UL_material_param_props)
    bpy.utils.register_class(TUROK_OT_add_material_param)
    bpy.utils.register_class(TUROK_OT_remove_material_param)
    bpy.utils.register_class(TUROK_OT_material_shader_list)
    bpy.utils.register_class(TUROK_OT_material_quicktex_import)
    bpy.utils.register_class(TUROK_OT_material_import)
    bpy.utils.register_class(TurokMaterialSettings)
    bpy.utils.register_class(TurokMaterialPanel)
    bpy.types.Material.t1 = bpy.props.PointerProperty(type=TurokMaterialSettings)
#end func

# -----------------------------------------------------------------------------
#     
def unregister():
    bpy.utils.unregister_class(TurokMaterialParamsPropGroup)
    bpy.utils.unregister_class(TUROK_UL_material_param_props)
    bpy.utils.unregister_class(TUROK_OT_add_material_param)
    bpy.utils.unregister_class(TUROK_OT_remove_material_param)
    bpy.utils.unregister_class(TUROK_OT_material_shader_list)
    bpy.utils.unregister_class(TUROK_OT_material_quicktex_import)
    bpy.utils.unregister_class(TUROK_OT_material_import)
    bpy.utils.unregister_class(TurokMaterialSettings)
    bpy.utils.unregister_class(TurokMaterialPanel)
    del bpy.types.Material.t1
#end func
