# 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 aud
import bpy
import math
import bmesh
import random
import bpy_extras.view3d_utils
from mathutils import Vector
from mathutils import Matrix
from . import kex_utils
from . import turok_bone_panel
from . import handlers

T1ActorTabItems = [
    ("SETUP",      "Setup",   ""),
    ("SPAWNFLAGS", "Flags",   ""),
    ("AI",         "AI",      ""),
    ("GENERAL",    "General", ""),
    ("MISC",       "Misc",    "")
]

T1SectorTabItems = [
    ("SETTINGS", "Settings", ""),
    ("FLAGS", "Flags", ""),
]

T1ImpactTypes = [
    ("DEFAULT", "Default", "", 0),
    ("WATER", "Water", "", 1),
    ("METAL", "Metal", "", 2),
    ("STONE", "Stone", "", 3),
    ("FLESH_HUMAN", "Flesh Human", "", 4),
    ("FLESH_CREATURE", "Flesh Creature", "", 5),
    ("FLESH_WATER", "Flesh Water", "", 6),
    ("LAVA", "Lava", "", 7),
    ("SLIME", "Slime", "", 8),
    ("FORCEFIELD", "Force field", "", 9),
]

T1SectorColorTypes = [
    ("WHITE", "White", "", 0),
    ("GREEN", "Green", "", 1),
    ("BLUE", "Blue", "", 2),
    ("RED", "Red", "", 3),
    ("PINK", "Pink", "", 4),
    ("PURPLE", "Purple", "", 5),
    ("BROWN", "Brown", "", 6),
    ("CYAN", "Cyan", "", 7),
    ("YELLOW", "Yellow", "", 8),
    ("ORANGE", "Orange", "", 9),
    ("BLACK", "Black", "", 10),
]

T1ActorSpawnFlagTabItems = [
    ("1", "Spawn Flags 1", ""),
    ("2", "Spawn Flags 2", ""),
    ("3", "Spawn Flags 3", "")
]

T1ActorSpawnFlags1Items = [
    ("SOLID",                   "Solid",                    "", 1 << 0),
    ("PROJECTILEATTACK1",       "ProjectileAttack1",        "", 1 << 1),
    ("LEADER",                  "Leader",                   "", 1 << 2),
    ("SNAP_TO_FLOOR",           "Snap To Floor",            "", 1 << 3),
    ("EXPLOSION_DEATH",         "Explosion Death",          "", 1 << 4),
    ("CLIMB_WALLS",             "Climb Walls",              "", 1 << 5),
    ("PROJECTILE_ATTACK2",      "Projectile Attack2",       "", 1 << 6),
    ("NO_REPEAT_EXPLOSION",     "No Repeat Explosion",      "", 1 << 7),
    ("DIE_ON_EXPLOSION",        "Die On Explosion",         "", 1 << 8),
    ("FLOCKER",                 "Flocker",                  "", 1 << 9),
    ("SLOW_ENEMY",              "Slow Enemy",               "", 1 << 10),
    ("RANDOM_RESURRECTION",     "Random Resurrection",      "", 1 << 11),
    ("RANDOM_FEIGN_DEATH",      "Random Feign Death",       "", 1 << 12),
    ("KAMIKAZE",                "Kamikaze",                 "", 1 << 13),
    ("AVOID_PLAYERS",           "Avoid Players",            "", 1 << 14),
    ("FLOAT_IN_WATER_ON_DEATH", "Float In Water On Death",  "", 1 << 15),
    ("TELEPORT",                "Teleport",                 "", 1 << 16),
    ("CAST_SHADOW",             "Cast Shadow",              "", 1 << 17),
    ("TELEPORT_WAIT",           "Teleport Wait",            "", 1 << 18),
    ("USE_STRONG_ATTACKS",      "Use Strong Attacks",       "", 1 << 19),
    ("USE_WEAK_ATTACKS",        "Use Weak Attacks",         "", 1 << 20),
    ("SNIPER",                  "Sniper",                   "", 1 << 21),
    ("MELT_ON_DEATH",           "Melt On Death",            "", 1 << 22),
    ("AVOID_WATER",             "Avoid Water",              "", 1 << 23),
    ("FLYING",                  "Flying",                   "", 1 << 24),
    ("TELEPORT_AVOID_WATER",    "Teleport Avoid Water",     "", 1 << 25),
    ("TELEPORT_AVOID_CLIFFS",   "Teleport Avoid Cliffs",    "", 1 << 26),
    ("TRIGGER_STUFF",           "Trigger Stuff",            "", 1 << 27),
    ("CANNOT_CAUSE_A_FIGHT",    "Cannot Cause A Fight",     "", 1 << 28),
    ("NO_WALL_COLLISION",       "No Wall Collision",        "", 1 << 29),
    ("SCREEN_SHAKE",            "Screen Shake",             "", 1 << 30),
    ("RESPAWN_ANIMATION",       "Respawn Animation",        "", -2147483648),
]

T1ActorSpawnFlags2Items = [
    ("DROPITEMMASK1",           "Drop Item Mask1",          "", 1 << 0),
    ("DROPITEMMASK2",           "Drop Item Mask2",          "", 1 << 1),
    ("DROPITEMMASK3",           "Drop Item Mask3",          "", 1 << 2),
    ("DROPITEMMASK3",           "Drop Item Mask4",          "", 1 << 3),
    ("DROPITEMMASK3",           "Drop Item Mask5",          "", 1 << 4),
    ("DROPITEMMASK3",           "Drop Item Mask6",          "", 1 << 5),
    ("DROPITEMMASK3",           "Drop Item Mask7",          "", 1 << 6),
    ("DROPITEMMASK3",           "Drop Item Mask8",          "", 1 << 7),
    ("DROPITEMMASK3",           "Drop Item Mask9",          "", 1 << 8),
    ("DROPITEMMASK3",           "Drop Item Mask10",         "", 1 << 9),
    ("DROPITEMMASK3",           "Drop Item Mask11",         "", 1 << 10),
    ("DROPITEMMASK3",           "Drop Item Mask12",         "", 1 << 11),
    ("DROPITEMMASK3",           "Drop Item Mask13",         "", 1 << 12),
    ("DROPITEMMASK3",           "Drop Item Mask14",         "", 1 << 13),
    ("REMOVE_ON_COMPLETION",    "Remove On Completion",     "", 1 << 14),
    ("NO_BLOOD",                "No Blood",                 "", 1 << 15),
    ("HOLD_TRIGGER_ANIMATION",  "Hold Trigger Animation",   "", 1 << 16),
    ("PROJECTILE_ATTACK3",      "Projectile Attack3",       "", 1 << 17),
    ("PROJECTILE_ATTACK4",      "Projectile Attack4",       "", 1 << 18),
    ("DROP_ITEM_ON_DAMAGE",     "Drop Item On Damage",      "", 1 << 19),
    ("NO_AUTOMAP_DRAW",         "No Automap Draw",          "", 1 << 20),
    ("ALTERNATE_MOVES",         "Alternate Moves",          "", 1 << 21),
    ("AA_REDUCED",              "Anti Aliasing Reduced",    "", 1 << 22),
    ("AA_FULL",                 "Anti Aliasing Full",       "", 1 << 23),
    ("PROJECTILE_ATTACK5",      "Projectile Attack5",       "", 1 << 24),
    ("PROJECTILE_ATTACK6",      "Projectile Attack6",       "", 1 << 25),
    ("MORTAL_WOUND_IMPACT",     "Mortal Wound Impact",      "", 1 << 26),
    ("STAY_IN_WATER",           "Stay In Water",            "", 1 << 27),
    ("DEVICE_WARP_DEATH",       "Device Warp Death",        "", 1 << 28),
    ("STORE_WARP_RETURN",       "Store Warp Return",        "", 1 << 29),
    ("PROJECTILE_ATTACK7",      "Projectile Attack7",       "", 1 << 30),
    ("PROJECTILE_ATTACK8",      "Projectile Attack7",       "", -2147483648),
]

T1ActorSpawnFlags3Items = [
    ("RETURNWARP",              "ReturnWarp",              "", 1 << 0),
    ("PLAYTRIGGERANIMONCE",     "PlayTriggerAnimOnce",     "", 1 << 1),
    ("REGENERATEFROMSTART",     "RegenerateFromStart",     "", 1 << 2),
    ("WALK_IN_STRAIGHT_LINE",   "Walk In Straight Line",   "", 1 << 3),
    ("KILL_OUTSIDE_OF_VIEW",    "Kill Outside Of View",    "", 1 << 4),
    ("NO_THINKER",              "No Thinker",              "", 1 << 5),
    ("AVOID_PLAYERS2",          "Avoid Players2",          "", 1 << 6),
    ("NO_VIOLENT_DEATH",        "No Violent Death",        "", 1 << 7),
    ("PROJECTILE_ATTACK9",      "Projectile Attack9",      "", 1 << 8),
    ("PROJECTILE_ATTACK10",     "Projectile Attack10",     "", 1 << 9),
    ("MAKE_SPAWN_ANIM_VISIBLE", "Make Spawn Anim Visible", "", 1 << 10),
    ("NO_DRAW_ON_CAMERA",       "No Draw On Camera",       "", 1 << 11),
    ("BIT12",                   "Bit12",                   "", 1 << 12),
    ("BIT13",                   "Bit13",                   "", 1 << 13),
    ("BIT14",                   "Bit14",                   "", 1 << 14),
    ("BIT15",                   "Bit15",                   "", 1 << 15),
]

T1StaticMeshFlags = [
    ("COLLIDE", "Collide", "", 1 << 0),
    ("NOPOLYCOLLISION", "No Poly Collision", "", 1 << 1),
    ("NODRAWCAMERA", "No Draw Camera", "", 1 << 2),
]

T1SectorSetFlags = [
    ("WATER",           "Water",             "", 1 << 0),
    ("BLOCK",           "Block",             "", 1 << 1),
    ("TOGGLE",          "Toggle",            "", 1 << 2),
    ("CLIFF",           "Cliff",             "", 1 << 3),
    ("CLIMB",           "Climb",             "", 1 << 4),
    ("BRIDGE",          "Bridge",            "", 1 << 5),
    ("CEILING",         "Collide Ceiling",   "", 1 << 6),
    ("CRAWL",           "Crawl",             "", 1 << 7),
    ("ENTERCRAWL",      "Enter Crawl",       "", 1 << 8),
    ("HIDDEN",          "Hidden",            "", 1 << 9),
    ("ENTERED",         "Entered",           "", 1 << 10),
    ("SECRET",          "Secret",            "", 1 << 11),
    ("RESTRICTED",      "Restricted",        "", 1 << 12),
    ("SLOPETEST",       "Slope Test",        "", 1 << 13),
    ("DEATHPIT",        "Death Pit",         "", 1 << 14),
    ("MAPPED",          "Mapped",            "", 1 << 15),
    ("EVENT",           "Event",             "", 1 << 16),
    ("REPEATABLE",      "Repeatable",        "", 1 << 17),
    ("TELEPORT",        "Teleport",          "", 1 << 18),
    ("DAMAGE",          "Damage",            "", 1 << 19),
    ("DRAWSKY",         "Draw Sky",          "", 1 << 20),
    ("TELEPORTAIR",     "Teleport Air",      "", 1 << 21),
    ("LAVA",            "Lava",              "", 1 << 22),
    ("EVENTSOUND",      "Event Sound",       "", 1 << 23),
    ("ANTIGRAVITY",     "Anti Gravity",      "", 1 << 24),
    ("LADDER",          "Ladder",            "", 1 << 25),
    ("CHECKPOINT",      "Checkpoint",        "", 1 << 26),
    ("SAVEGAME",        "Save Game",         "", 1 << 27),
    ("WARPRETURN",      "Warp Return",       "", 1 << 28),
    ("SHALLOWWATER",    "Shallow Water",     "", 1 << 29),
    ("DRAWSUN",         "Draw Sun",          "", 1 << 30),
    ("STOREWARPRETURN", "Store Warp Return", "", -2147483648),
]

T1ObjectTypes = [
    ("NONE", "None", "", 0),
    ("MODEL", "Model", "", 1),
    ("MAP STATICMESH", "Map Staticmesh", "", 2),
    ("MAP ACTOR", "Map Actor", "", 3),
    ("MAP SECTORS", "Map Sectors", "", 4),
    ("MAP CEILING SECTORS", "Map Ceiling Sectors", "", 5),
    ("MAP SUN", "Map Sun", "", 6),
    ("MAP ROOT", "Map Root", "", 7),
]

# -----------------------------------------------------------------------------
#
class TurokObjectKeyFramesPropGroup(bpy.types.PropertyGroup):

    def on_event_changed(self, context):
        self.name = kex_utils.get_animation_action_full_desc(self.event, self.arg1)
        
    def get_args(self, argIndex):
        if argIndex == 0:
            return self.arg1
        elif argIndex == 1:
            return self.arg2
        elif argIndex == 2:
            return self.arg3
        else:
            return self.arg4
            
    def set_args(self, argIndex, value):
        if argIndex == 0:
            self.arg1 = value
        elif argIndex == 1:
            self.arg2 = value
        elif argIndex == 2:
            self.arg3 = value
        else:
            self.arg4 = value
            
    def get_arg_names(self):
        argNames = [""] * 4
        for i in range(0, 4):
            argNames[i] = kex_utils.get_animation_action_arg_name(self.event, i)
        return argNames
        
    frame: bpy.props.IntProperty(name="Frame", min=0, max=2147483647, default=1)
    event: bpy.props.IntProperty(name="Event Func", min=-2147483648, max=2147483647, update=on_event_changed)
    arg1: bpy.props.FloatProperty(name="Arg1", step=100, precision=6)
    arg2: bpy.props.FloatProperty(name="Arg2", step=100, precision=6)
    arg3: bpy.props.FloatProperty(name="Arg3", step=100, precision=6)
    arg4: bpy.props.FloatProperty(name="Arg4", step=100, precision=6)
#end func

# -----------------------------------------------------------------------------
#
class TUROK_UL_key_frames_props(bpy.types.UIList):
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        row = layout.row(align=True)
        row = row.split(factor=0.85)
        row.label(text="%s" % (kex_utils.get_animation_action_full_desc(item.event, item.arg1)))
        row.label(text="(%d)" % (item.frame))
    #end func
#end class
# -----------------------------------------------------------------------------
#
class TurokObjectSettings(bpy.types.PropertyGroup):

    def get_bb_min(self):
        return self.get("boundingBoxMin", Vector((0.0, 0.0, 0.0)))
        
    def set_bb_min(self, value):
        self["boundingBoxMin"] = value
        
    def get_bb_max(self):
        return self.get("boundingBoxMax", Vector((0.0, 0.0, 0.0)))
        
    def set_bb_max(self, value):
        self["boundingBoxMax"] = value
        
    def on_updated_sector_prop(self, context):
        kex_utils.sectorDirty = True
    #end if
    
    def on_textureset_index_changed(self, context):
        if context is None or not hasattr(context, "object") or self.ignoreUpdateFuncs:
            return
        kex_utils.set_obj_material_tex_index(context.object, self.textureSetIndex)
    #end func   
    
    def on_actortexture_changed(self, context):
        if context is None or not hasattr(context, "object") or self.ignoreUpdateFuncs:
            return
        kex_utils.set_obj_material_tex_index(context.object, self.actorTexture)
    #end func
    
    def on_staticmeshtexture_changed(self, context):
        if context is None or not hasattr(context, "object") or self.ignoreUpdateFuncs:
            return
        kex_utils.set_obj_material_tex_index(context.object, self.staticmeshTexture)
    #end func
    
    objType: bpy.props.EnumProperty(name="Turok Object Types", description="The type of Turok Object this Blender Object is.", items=T1ObjectTypes, default="NONE")
    ignoreUpdateFuncs: bpy.props.BoolProperty(default=False)

    #Map Actor Properties
    actorType: bpy.props.IntProperty(name = "Type", min=-2147483648, max=2147483647, default=0)
    actorModelPath: bpy.props.StringProperty(name = "Model Path", subtype='FILE_NAME', description="File must be relative to the Game or Turok Folder")
    actorAnimPath: bpy.props.StringProperty(name = "Anim Path", subtype='FILE_NAME', description="File must be relative to the Game or Turok Folder")
    actorSpeciesMask: bpy.props.IntProperty(name="Species Mask", min=-2147483648, max=2147483647, default=255)
    actorSectorIndex: bpy.props.IntProperty(name="Sector Index", min=-2147483648, max=2147483647, default=0)
    actorBoundsMin: bpy.props.FloatVectorProperty(name = "BB Min", description="Bounding Box Min", default=Vector((-0.5, -0.5, -0.5)), subtype='XYZ', size = 3, step=100, precision=6)
    actorBoundsMax: bpy.props.FloatVectorProperty(name = "BB Max", description="Bounding Box Max", default=Vector((0.5, 0.5, 0.5)), subtype='XYZ', size = 3, step=100, precision=6)
    actorSightRange: bpy.props.FloatProperty(name="Sight Range", subtype='DISTANCE', step=100, precision=6)
    actorSightFOV: bpy.props.FloatProperty(name="Sight FOV", subtype='ANGLE', step=100, precision=6)
    actorLoudRange: bpy.props.FloatProperty(name="Loud Range", subtype='DISTANCE', description="Distance when loud type noises will go to attack you", step=100, precision=6)
    actorQuietRange: bpy.props.FloatProperty(name="Quiet Range", subtype='DISTANCE', description="Distance when quiet type noises will go to attack you (never used in Remaster unless you use a analog stick for movement)", step=100, precision=6)
    actorAttackRange: bpy.props.FloatProperty(name="Attack Range", subtype='DISTANCE', step=100, precision=6)
    actorFlyHeight: bpy.props.FloatProperty(name="Fly Height", subtype='DISTANCE', step=100, precision=6)
    actorWidth: bpy.props.FloatProperty(name="Width", step=100, precision=6)
    actorWallWidth: bpy.props.FloatProperty(name="Wall Width", step=100, precision=6)
    actorHeight: bpy.props.FloatProperty(name="Height", step=100, precision=6)
    actorDeadHeight: bpy.props.FloatProperty(name="Dead Height", step=100, precision=6)
    actorMeleeRange: bpy.props.FloatProperty(name="Melee Range", subtype='DISTANCE', description="Distance when AI can perform melee animations", step=100, precision=6)
    actorLeashRadius: bpy.props.FloatProperty(name="Leash Radius", subtype='DISTANCE', description="Distance when AI has gone too far from their spawn point and will return back to the spawn radius", step=100, precision=6)
    actorTriggerDelay: bpy.props.FloatProperty(name="Trigger Delay", subtype='TIME', description="Time in seconds until the actor will be triggered after triggering the actor.", step=10, precision=6)
    actorSpawnFlags1: bpy.props.EnumProperty(items = T1ActorSpawnFlags1Items, options={'ENUM_FLAG'})
    actorSpawnFlags2: bpy.props.EnumProperty(items = T1ActorSpawnFlags2Items, options={'ENUM_FLAG'})
    actorSpawnFlags3: bpy.props.EnumProperty(items = T1ActorSpawnFlags3Items, options={'ENUM_FLAG'})
    actorHealth: bpy.props.IntProperty(name="Health", min=-32768, max=32767)
    actorTID: bpy.props.IntProperty(name="TID", min=-32768, max=32767)
    actorTargetTID: bpy.props.IntProperty(name="Target TID", min=-32768, max=32767)
    actorMaxRegenerations: bpy.props.IntProperty(name="Max Regenerations", min=-32768, max=32767)
    actorAttackChance: bpy.props.IntProperty(name="Attack Chance", min=0, max=255)
    actorTriggerAnim: bpy.props.IntProperty(name="Trigger Anim", min=0, max=255, description="When this actor is triggered the actor will play the animation ID: event01 + this value")
    actorVariant: bpy.props.IntProperty(name="Variant", min=-128, max=127)
    actorTexture: bpy.props.IntProperty(name="Texture Index", min=-128, max=127, update=on_actortexture_changed)
    actorParams1: bpy.props.IntProperty(name="Params 1", min=-128, max=127)
    actorParams2: bpy.props.IntProperty(name="Params 2", min=-128, max=127)
    
    actorTabs: bpy.props.EnumProperty(items = T1ActorTabItems)
    actorFlagsTabs: bpy.props.EnumProperty(items = T1ActorSpawnFlagTabItems)
    
    #Map Staticmesh Properties
    staticmeshModelPath: bpy.props.StringProperty(name = "Model Path", subtype='FILE_NAME', description="File must be relative to the Game or Turok Folder")
    staticmeshFlags: bpy.props.EnumProperty(items = T1StaticMeshFlags, options={'ENUM_FLAG'})
    staticmeshRadius: bpy.props.FloatProperty(name="Radius")
    staticmeshHeight: bpy.props.FloatProperty(name="Height")
    staticmeshTexture: bpy.props.IntProperty(name="Texture Index", min=-2147483648, max=2147483647, update=on_staticmeshtexture_changed)
    staticmeshCullBits: bpy.props.IntProperty(name="CullBits", min=-2147483648, max=2147483647, default=-1)
    staticmeshSectorIndex: bpy.props.IntProperty(name="Sector Index", min=-2147483648, max=2147483647, default=-1)
    staticmeshBoundsMin: bpy.props.FloatVectorProperty(name = "BB Min", description="Bounding Box Min", default=Vector((0.0,0.0,0.0)), subtype='XYZ', size = 3, step=100, precision=6)
    staticmeshBoundsMax: bpy.props.FloatVectorProperty(name = "BB Max", description="Bounding Box Max", default=Vector((0.0,0.0,0.0)), subtype='XYZ', size = 3, step=100, precision=6)
    
    #Map Root Properties
    mapVisImage: bpy.props.PointerProperty(name="Vis Image", type = bpy.types.Image, description="Image containing packed file of bytes for the Visibility Tables")
    mapVisCount: bpy.props.IntProperty(name="Vis Count", min=-2147483648, max=2147483647, default=0)
    mapVisSize: bpy.props.IntProperty(name="Vis Size", min=-2147483648, max=2147483647, default=0)
    
    mapSunlightDirection: bpy.props.FloatVectorProperty(name="Sunlight Direction", default=Vector((0.0, 0.0, 0.0)), subtype='XYZ', size = 3, step=1, precision=6)
    mapSunlightColor: bpy.props.FloatVectorProperty(name="Sunlight Color", default=Vector((1.0, 1.0, 1.0, 1.0)), subtype='COLOR', size = 4, step=100, precision=6, min=0.0, max=1.0)
    mapAmbientColor: bpy.props.FloatVectorProperty(name="Ambient Color", default=Vector((0.24705882, 0.24705882, 0.24705882, 1.0)), subtype='COLOR', size = 4, step=100, precision=6, min=0.0, max=1.0)
    mapSkyMaterial: bpy.props.StringProperty(name = "Sky Material", subtype='FILE_NAME', description="File must be relative to the Game or Turok Folder")
    
    #Model Properties
    boundingBoxMin: bpy.props.FloatVectorProperty(name = "BB Min", description="Bounding Box Min", default=Vector((0.0,0.0,0.0)), subtype='XYZ', size = 3, step=100, precision=6, get=get_bb_min, set=set_bb_min)
    boundingBoxMax: bpy.props.FloatVectorProperty(name = "BB Max", description="Bounding Box Max", default=Vector((0.0,0.0,0.0)), subtype='XYZ', size = 3, step=100, precision=6, get=get_bb_max, set=set_bb_max)
    variantIndex: bpy.props.IntProperty(name = "Mesh Index", min=0, max=65535, description="Set to a unique value starting from 0 for each mesh variant. If has same values on Export will sort and assign accordingly.") #aka. object index
    yawOffset: bpy.props.FloatProperty(name="Yaw Offset", subtype='ANGLE', description="Animate this value in your animation actions.")
    showBoundingBox: bpy.props.BoolProperty(default=True)
    showAnimAction: bpy.props.BoolProperty(default=False)
    textureSetIndex: bpy.props.IntProperty(name="TextureSet Index", min=0, max=2147483647, update=on_textureset_index_changed)
    
    #Sector Properties for Active BMesh Face selection only
    sec_drawOrder: bpy.props.IntProperty(name="Draw Order", min=-32768, max=32767, default=0, update=on_updated_sector_prop)
    sec_flags: bpy.props.EnumProperty(items=T1SectorSetFlags, options={'ENUM_FLAG'}, update=on_updated_sector_prop)
    sec_fogColor: bpy.props.FloatVectorProperty(name="Fog Color", default=Vector((0.286274, 0.352941, 0.498039, 1.0)), subtype='COLOR', size = 4, step=100, precision=6, min=0.0, max=1.0, update=on_updated_sector_prop)
    sec_waterColor: bpy.props.FloatVectorProperty(name="Water Color", default=Vector((0.0, 0.164705, 0.745098, 1.0)), subtype='COLOR', size = 4, step=100, precision=6, min=0.0, max=1.0, update=on_updated_sector_prop)
    sec_fogZFar: bpy.props.FloatProperty(name="Fog ZFar", step=100, precision=6, default=1024.0, update=on_updated_sector_prop)
    sec_waterZFar: bpy.props.FloatProperty(name="Water ZFar", step=100, precision=6, default=1024.0, update=on_updated_sector_prop)
    sec_fogStart: bpy.props.FloatProperty(name="Fog Start", step=100, precision=6, default=750.0, update=on_updated_sector_prop)
    sec_waterStart: bpy.props.FloatProperty(name="Water Fog Start", step=100, precision=6, default=744.0, update=on_updated_sector_prop)
    sec_waterHeight: bpy.props.FloatProperty(name="Water Fog Height", step=100, precision=6, update=on_updated_sector_prop)
    sec_skyHeight: bpy.props.FloatProperty(name="Sky Height", step=100, precision=6, default=100.0, update=on_updated_sector_prop)
    sec_skySpeed: bpy.props.FloatProperty(name="Sky Speed", step=100, precision=6, default=100.0, update=on_updated_sector_prop)
    sec_blendLength: bpy.props.FloatProperty(name="Blend Length", step=100, precision=6, default=1.0, update=on_updated_sector_prop)
    sec_arg1: bpy.props.IntProperty(name="Arg 1", min=-32768, max=32767, default=0, update=on_updated_sector_prop)
    sec_arg2: bpy.props.IntProperty(name="Arg 2", min=-32768, max=32767, default=0, update=on_updated_sector_prop)
    sec_arg3: bpy.props.IntProperty(name="Arg 3", min=-32768, max=32767, default=0, update=on_updated_sector_prop)
    sec_arg4: bpy.props.IntProperty(name="Arg 4", min=-32768, max=32767, default=0, update=on_updated_sector_prop)
    sec_arg5: bpy.props.IntProperty(name="Arg 5", min=-32768, max=32767, default=0, update=on_updated_sector_prop)
    sec_arg6: bpy.props.IntProperty(name="Arg 6", min=-32768, max=32767, default=0, update=on_updated_sector_prop)
    sec_floorImpactID: bpy.props.EnumProperty(name="Floor Impact ID", items=T1ImpactTypes, default="DEFAULT", update=on_updated_sector_prop)
    sec_wallImpactID: bpy.props.EnumProperty(name="Wall Impact ID", items=T1ImpactTypes, default="DEFAULT", update=on_updated_sector_prop)
    # sec_floorImpactID: bpy.props.IntProperty(name="Floor Impact ID", min=0, max=255, default=0)
    # sec_wallImpactID: bpy.props.IntProperty(name="Wall Impact ID", min=0, max=255, default=3)
    sec_ambience: bpy.props.IntProperty(name="Ambience", min=0, max=255, default=0, update=on_updated_sector_prop)
    sec_automapColor: bpy.props.EnumProperty(name="Automap Color", items=T1SectorColorTypes, default="WHITE", update=on_updated_sector_prop)
    # sec_automapColor: bpy.props.IntProperty(name="Automap Color", min=0, max=255, default=0)
    sec_music: bpy.props.IntProperty(name="Music", min=0, max=255, default=0, update=on_updated_sector_prop)
    sec_cullBits: bpy.props.IntProperty(name="Cull Bits", min=0, max=255, default=255, update=on_updated_sector_prop)
    sec_unused1: bpy.props.IntProperty(name="Unused1", min=0, max=255, default=0, update=on_updated_sector_prop)
    sec_unused2: bpy.props.IntProperty(name="Unused2", min=0, max=255, default=0, update=on_updated_sector_prop)
    
    sec_tabs: bpy.props.EnumProperty(items = T1SectorTabItems)

#end class

# -----------------------------------------------------------------------------
#
class TurokAnimationActionSettings(bpy.types.PropertyGroup):
    keyFrames: bpy.props.CollectionProperty(name="Key Frame Actions", type = TurokObjectKeyFramesPropGroup)
    keyFramesIndex: bpy.props.IntProperty(name="", default = -1)
    marker: bpy.props.BoolProperty(name="Marker", description="Whether to use Blend and Loop Frame", default=True)
    blend: bpy.props.IntProperty(name="Blend", description="Length of frames to blend (default 5)", default=5, min=0, max=65535)
    loopFrame: bpy.props.IntProperty(name="Loop Frame", description="start frame after looping (default 1)", default=1, min=0, max=65535)
    animID: bpy.props.IntProperty(name="AnimID", min=-2147483648, max=2147483647, default=-1)
    
    def copy_keyframe(self, index):
        if index < 0 or index >= len(self.keyFrames):
            return
        keyFrame = self.keyFrames[index]
        kex_utils.turok1CopiedKeyFrame = [keyFrame.frame, keyFrame.event, keyFrame.arg1, keyFrame.arg2, keyFrame.arg3, keyFrame.arg4]
        
    def paste_keyframe(self, index):
        if kex_utils.turok1CopiedKeyFrame is None or index < 0 or index >= len(self.keyFrames):
            return
        self.keyFrames[index].frame = kex_utils.turok1CopiedKeyFrame[0]
        self.keyFrames[index].arg1 = kex_utils.turok1CopiedKeyFrame[2]
        self.keyFrames[index].arg2 = kex_utils.turok1CopiedKeyFrame[3]
        self.keyFrames[index].arg3 = kex_utils.turok1CopiedKeyFrame[4]
        self.keyFrames[index].arg4 = kex_utils.turok1CopiedKeyFrame[5]
        self.keyFrames[index].event = kex_utils.turok1CopiedKeyFrame[1]
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TUROK_OT_loop_frame_last(bpy.types.Operator):
    bl_idname = "turok_action.loopframe_last"
    bl_label = "Loop Frame to last"
    bl_description = "Sets the loop frame to the last frame"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        scene = context.scene
        if scene is None or scene.render.fps < kex_utils.turok1FPS or scene.render.fps % kex_utils.turok1FPS != 0:
            return False
        return obj is not None and obj.animation_data is not None and obj.animation_data.action is not None and obj.animation_data.action.t1.marker
    #end func
        
    def invoke(self, context, event):
        scene = context.scene
        framesPerGameFrame = int(scene.render.fps / kex_utils.turok1FPS)
        action = context.object.animation_data.action
        frameStart, frameEnd = action.frame_range
        frameStart = int(frameStart)
        frameEnd = int(frameEnd)
        frameCount = int((frameEnd - frameStart) / framesPerGameFrame) + 1
        action.t1.loopFrame = frameCount - 1
                
        return {'FINISHED'}
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TUROK_OT_key_frame_action(bpy.types.Operator):
    bl_idname = "turok_action.keyframe_action"
    bl_label = "Perform Key Frame UI Action"
    bl_description = "Move items up and down, add and remove"
    bl_options = {'UNDO'}
    
    action: bpy.props.EnumProperty(items=(  ('UP', "Up", ""),
                                            ('DOWN', "Down", ""),
                                            ('REMOVE', "Remove", ""),
                                            ('ADD', "Add", "")
                                         ))
    @classmethod
    def poll(cls, context):
        obj = context.object
        return obj is not None and obj.animation_data is not None and obj.animation_data.action is not None
    #end func
        
    def invoke(self, context, event):
        tAction = context.object.animation_data.action.t1
        idx = tAction.keyFramesIndex
        if self.action == 'DOWN' and idx < len(tAction.keyFrames) - 1:
            tAction.keyFrames.move(idx, idx + 1)
            tAction.keyFramesIndex += 1
        elif self.action == 'UP' and idx >= 1:
            tAction.keyFrames.move(idx, idx - 1)
            tAction.keyFramesIndex -= 1
        elif self.action == 'REMOVE' and idx >= 0 and idx < len(tAction.keyFrames):
            tAction.keyFrames.remove(idx)
            if idx >= len(tAction.keyFrames):
                tAction.keyFramesIndex -= 1
        elif self.action == 'ADD':
            item = tAction.keyFrames.add()
            item.name = kex_utils.get_animation_action_full_desc(item.event, item.arg1)
            tAction.keyFramesIndex = len(tAction.keyFrames) - 1
                
        return {'FINISHED'}
    #end func
#end class
       
# -----------------------------------------------------------------------------
#
class TUROK_OT_key_frame_copy(bpy.types.Operator):
    bl_idname = "turok_action.keyframe_copy"
    bl_label = "Key Frame Action Copy"
    bl_description = "Copy Key Frame Action"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or obj.animation_data is None or obj.animation_data.action is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)
    
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        tAction.copy_keyframe(tAction.keyFramesIndex)
            
        return {'FINISHED'}
       
# -----------------------------------------------------------------------------
#
class TUROK_OT_key_frame_paste(bpy.types.Operator):
    bl_idname = "turok_action.keyframe_paste"
    bl_label = "Key Frame Action Paste"
    bl_description = "Paste Key Frame Action"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or obj.animation_data is None or obj.animation_data.action is None or kex_utils.turok1CopiedKeyFrame is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)
    
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        tAction.paste_keyframe(tAction.keyFramesIndex)
            
        return {'FINISHED'}
       
# -----------------------------------------------------------------------------
#
class TUROK_OT_key_frame_set_anim_frame(bpy.types.Operator):
    bl_idname = "turok_action.keyframe_set_anim_frame"
    bl_label = "Set scene animation frame"
    bl_description = "Sets the scene animation frame to the Key Frame Action Frame"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or obj.animation_data is None or obj.animation_data.action is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)
    
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        kfa = tAction.keyFrames[tAction.keyFramesIndex]
        bpy.context.scene.frame_set(kfa.frame * 4)
        
        return {'FINISHED'}
       
# -----------------------------------------------------------------------------
#
class TUROK_OT_key_frame_set_cur_frame(bpy.types.Operator):
    bl_idname = "turok_action.keyframe_set_cur_frame"
    bl_label = "Set Key Frame Action Frame"
    bl_description = "Sets Key Frame Action Frame to the current scene Animation Frame"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or obj.animation_data is None or obj.animation_data.action is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)
    
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        kfa = tAction.keyFrames[tAction.keyFramesIndex]
        kfa.frame = bpy.context.scene.frame_current / 4
        
        return {'FINISHED'}
       
# -----------------------------------------------------------------------------
#
class TUROK_OT_key_frame_toggle_point(bpy.types.Operator):
    bl_idname = "turok_action.keyframe_toggle_point"
    bl_label = "Toggle Key Frame Action Point"
    bl_description = "Show or Hide the Key Frame Action Point"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or obj.animation_data is None or obj.animation_data.action is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)
    
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        kfa = tAction.keyFrames[tAction.keyFramesIndex]
        turok1PointObj = kex_utils.get_object(kex_utils.turok1ActionPointName)
        if turok1PointObj is None:
            argNames = kfa.get_arg_names()
            posXArgIndex = kex_utils.index_of(argNames, "X Offset")
            posYArgIndex = kex_utils.index_of(argNames, "Y Offset")
            posZArgIndex = kex_utils.index_of(argNames, "Z Offset")
            radiusArgIndex = kex_utils.index_of(argNames, "Radius")
            if posXArgIndex != -1 and posYArgIndex != -1 and posZArgIndex != -1:
                pointObj = bpy.data.objects.new(kex_utils.turok1ActionPointName, None)
                context.scene.collection.objects.link(pointObj)
                if radiusArgIndex != -1:
                    isMeleeRadius = kex_utils.index_of(kex_utils.MELEE_RADIUS_EVENTS, kfa.event) != -1
                    pointObj.empty_display_type = 'SPHERE'
                    pointObj.empty_display_size = 1.0
                    if isMeleeRadius:
                        s = kfa.get_args(radiusArgIndex) * 10.24 * 2.0
                    else:
                        s = kfa.get_args(radiusArgIndex) * 10.24
                    #end if
                    pointObj.scale = (s, s, s)
                else:
                    pointObj.empty_display_type = 'PLAIN_AXES'
                    pointObj.empty_display_size = 10.24
                #end if
                pointObj.show_in_front = True
                pointObj.location = Matrix.Translation(context.object.location) @ Vector((kfa.get_args(posXArgIndex), kfa.get_args(posYArgIndex), kfa.get_args(posZArgIndex)))
            #end if
        else:
            bpy.data.objects.remove(turok1PointObj)
        #end if
            
        return {'FINISHED'}
       

# -----------------------------------------------------------------------------
#
class TUROK_OT_key_frame_point_update(bpy.types.Operator):
    bl_idname = "turok_action.keyframe_point_update"
    bl_label = "Update Key Frame Action Point"
    bl_description = "Updates the Key Frame Action Point object's location and scale using the Args"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        turok1PointObj = kex_utils.get_object(kex_utils.turok1ActionPointName)
        if turok1PointObj is None:
            return False
        obj = context.object
        if obj is None or obj.animation_data is None or obj.animation_data.action is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)
    
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        kfa = tAction.keyFrames[tAction.keyFramesIndex]
        argNames = kfa.get_arg_names()
        posXArgIndex = kex_utils.index_of(argNames, "X Offset")
        posYArgIndex = kex_utils.index_of(argNames, "Y Offset")
        posZArgIndex = kex_utils.index_of(argNames, "Z Offset")
        radiusArgIndex = kex_utils.index_of(argNames, "Radius")
        if posXArgIndex != -1 and posYArgIndex != -1 and posZArgIndex != -1:
            pointObj = kex_utils.get_object(kex_utils.turok1ActionPointName)
            if radiusArgIndex != -1:
                isMeleeRadius = kex_utils.index_of(kex_utils.MELEE_RADIUS_EVENTS, kfa.event) != -1
                pointObj.empty_display_type = 'SPHERE'
                pointObj.empty_display_size = 1.0
                if isMeleeRadius:
                    s = kfa.get_args(radiusArgIndex) * 10.24 * 2.0
                else:
                    s = kfa.get_args(radiusArgIndex) * 10.24
                #end if
                pointObj.scale = (s, s, s)
            else:
                pointObj.empty_display_type = 'PLAIN_AXES'
                pointObj.empty_display_size = 10.24
                s = 1.0
                pointObj.scale = (s, s, s)
            #end if
            pointObj.location = Matrix.Translation(context.object.location) @ Vector((kfa.get_args(posXArgIndex), kfa.get_args(posYArgIndex), kfa.get_args(posZArgIndex)))
        #end if
            
        return {'FINISHED'}
       
# -----------------------------------------------------------------------------
#
class TUROK_OT_key_frame_point_to_args(bpy.types.Operator):
    bl_idname = "turok_action.keyframe_point_to_args"
    bl_label = "Set Key Frame Action Args to Point"
    bl_description = "Sets the Key Frame Action Args to the point objects location and scale"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        turok1PointObj = kex_utils.get_object(kex_utils.turok1ActionPointName)
        if turok1PointObj is None:
            return False
        obj = context.object
        if obj is None or obj.animation_data is None or obj.animation_data.action is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)
    
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        kfa = tAction.keyFrames[tAction.keyFramesIndex]
        
        argNames = kfa.get_arg_names()
        posXArgIndex = kex_utils.index_of(argNames, "X Offset")
        posYArgIndex = kex_utils.index_of(argNames, "Y Offset")
        posZArgIndex = kex_utils.index_of(argNames, "Z Offset")
        radiusArgIndex = kex_utils.index_of(argNames, "Radius")
        if posXArgIndex != -1 and posYArgIndex != -1 and posZArgIndex != -1:
            pointObj = kex_utils.get_object(kex_utils.turok1ActionPointName)
            pos = Matrix.Translation(context.object.location).inverted() @ Vector(pointObj.location)
            kfa.set_args(posXArgIndex, pos[0])
            kfa.set_args(posYArgIndex, pos[1])
            kfa.set_args(posZArgIndex, pos[2])
            if radiusArgIndex != -1:
                isMeleeRadius = kex_utils.index_of(kex_utils.MELEE_RADIUS_EVENTS, kfa.event) != -1
                if isMeleeRadius:
                    kfa.set_args(radiusArgIndex, pointObj.scale[0] / 10.24 / 2.0)
                else:
                    kfa.set_args(radiusArgIndex, pointObj.scale[0] / 10.24)
                #end if
            #end if
        #end if
            
        return {'FINISHED'}
       
# -----------------------------------------------------------------------------
#
class TUROK_MT_key_frame_special_menu(bpy.types.Menu):
    bl_idname = "TUROK_MT_key_frame"
    bl_label = "keyframe menu"
    
    def draw(self, context):
        layout = self.layout
        layout.operator(TUROK_OT_key_frame_copy.bl_idname, icon='COPYDOWN', text="Copy")
        layout.operator(TUROK_OT_key_frame_paste.bl_idname, icon='PASTEDOWN', text="Paste")


# -----------------------------------------------------------------------------
#
class TUROK_OT_anim_action_list(bpy.types.Operator):
    """Select a Turok Animation Action from a list"""
    bl_idname = "turok_action.animaction_list"
    bl_label = "Set Turok Anim Action Event with list"
    bl_options = {'UNDO'}
    bl_property = "enumprop"
    
    def items_cb(self, context):
        items = []
        for id in kex_utils.turok1Defs["animactions"]:
            action = kex_utils.turok1Defs["animactions"][id]
            items.append((action["description"], action["description"], "", action["id"]))
        return items
    
    enumprop: bpy.props.EnumProperty(items=items_cb)

    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or obj.animation_data is None or obj.animation_data.action is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)
        
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        keyFrame = tAction.keyFrames[tAction.keyFramesIndex]
        keyFrame.event = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)
        
        return {'FINISHED'}

    def invoke(self, context, event):
        context.window_manager.invoke_search_popup(self)
        return {'FINISHED'}
# -----------------------------------------------------------------------------
#
class TUROK_OT_play_music(bpy.types.Operator):
    """Play music"""
    bl_idname = "turok_action.play_music_id"
    bl_label = "Play Music"

    musicID: bpy.props.IntProperty()

    def execute(self, context):
        kex_utils.stop_sound()
        kex_utils.play_music_path(kex_utils.get_music_path(self.musicID))
        return {'FINISHED'}
    #end func
#end func
        
# -----------------------------------------------------------------------------
#
class TUROK_OT_play_sound_id(bpy.types.Operator):
    """Play sound"""
    bl_idname = "turok_action.play_sound_id"
    bl_label = "Play Sound"

    soundID: bpy.props.IntProperty()

    def execute(self, context):
        kex_utils.stop_sound()
        kex_utils.play_ksnd(kex_utils.get_sound_path(self.soundID))
        return {'FINISHED'}
    #end func
#end func

# -----------------------------------------------------------------------------
#
class TUROK_OT_play_ambience(bpy.types.Operator):
    """Play sound Path"""
    bl_idname = "turok_action.play_ambience"
    bl_label = "Play Sound Path"

    ambienceID: bpy.props.IntProperty()

    def execute(self, context):
        amb = kex_utils.get_ambience(self.ambienceID)
        if amb is not None and len(amb["sounds"]) > 0:
            kex_utils.stop_sound()
            soundPath = amb["sounds"][random.randint(0, len(amb["sounds"]) - 1)]
            kex_utils.play_ksnd(soundPath)
        #end if
        return {'FINISHED'}
    #end func
    
#end func
# -----------------------------------------------------------------------------
#
class TUROK_OT_stop_all_sounds(bpy.types.Operator):
    """Stop all Sounds"""
    bl_idname = "turok_action.stop_all_sounds"
    bl_label = "Stop all sounds"

    def execute(self, context):
        kex_utils.stop_sound()
        return {'FINISHED'}
    #end func
    
#end func

# -----------------------------------------------------------------------------
#
class TUROK_OT_anim_action_arg_sound_list(bpy.types.Operator):
    """Set an Arg in a Turok Animation Action from a sound list"""
    bl_idname = "turok_action.animaction_arg_sound_list"
    bl_label = "Set Turok Anim Action Arg with sound list"
    bl_options = {'UNDO'}
    bl_property = "enumprop"
    
    def items_cb(self, context):
        items = []
        for id in kex_utils.turok1Defs["sounds"]:
            action = kex_utils.turok1Defs["sounds"][id]
            items.append((action["description"], action["description"], "", action["id"]))
        return items
    
    enumprop: bpy.props.EnumProperty(items=items_cb)
    argIndex: bpy.props.IntProperty()

    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or "sounds" not in kex_utils.turok1Defs or obj.animation_data is None or obj.animation_data.action is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)

        
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        keyFrame = tAction.keyFrames[tAction.keyFramesIndex]
        if self.argIndex == 1:
            keyFrame.arg2 = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)
        elif self.argIndex == 2:
            keyFrame.arg3 = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)
        elif self.argIndex == 3:
            keyFrame.arg4 = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)
        else:
            keyFrame.arg1 = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)
        
        return {'FINISHED'}

    def invoke(self, context, event):
        context.window_manager.invoke_search_popup(self)
        return {'FINISHED'}
        
# -----------------------------------------------------------------------------
#
class TUROK_OT_anim_action_arg_fx_list(bpy.types.Operator):
    """Set an Arg in a Turok Animation Action from a Fx list"""
    bl_idname = "turok_action.animaction_arg_fx_list"
    bl_label = "Set Turok Anim Action Arg with fx list"
    bl_options = {'UNDO'}
    bl_property = "enumprop"
    
    def items_cb(self, context):
        items = []
        for id in kex_utils.turok1Defs["fx"]:
            action = kex_utils.turok1Defs["fx"][id]
            items.append((action["description"], action["description"], "", action["id"]))
        return items
    
    enumprop: bpy.props.EnumProperty(items=items_cb)
    argIndex: bpy.props.IntProperty()

    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or "fx" not in kex_utils.turok1Defs or obj.animation_data is None or obj.animation_data.action is None:
            return False
        tAction = obj.animation_data.action.t1
        return tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames)
        
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        keyFrame = tAction.keyFrames[tAction.keyFramesIndex]
        if self.argIndex == 1:
            keyFrame.arg2 = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)
        elif self.argIndex == 2:
            keyFrame.arg3 = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)
        elif self.argIndex == 3:
            keyFrame.arg4 = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)
        else:
            keyFrame.arg1 = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)
        
        return {'FINISHED'}

    def invoke(self, context, event):
        context.window_manager.invoke_search_popup(self)
        return {'FINISHED'}
        
# -----------------------------------------------------------------------------
#
class TUROK_OT_anim_list(bpy.types.Operator):
    """Set Turok Animation ID from a list"""
    bl_idname = "turok_action.anim_list"
    bl_label = "Set Turok Anim ID with a list"
    bl_options = {'UNDO'}
    bl_property = "enumprop"
    
    def items_cb(self, context):
        items = []
        for id in kex_utils.turok1Defs["animations"]:
            anim = kex_utils.turok1Defs["animations"][id]
            items.append((anim["description"], anim["description"], "", anim["id"]))
        return items
    
    enumprop: bpy.props.EnumProperty(items=items_cb)

    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None or "animations" not in kex_utils.turok1Defs or obj.animation_data is None or obj.animation_data.action is None:
            return False
        return True

        
    def execute(self, context):
        tAction = context.object.animation_data.action.t1
        tAction.animID = kex_utils.get_enum_type(self.items_cb(context), self.enumprop)        
        return {'FINISHED'}

    def invoke(self, context, event):
        context.window_manager.invoke_search_popup(self)
        return {'FINISHED'}
        
# -----------------------------------------------------------------------------
#
class TUROK_OT_set_turokobjects_children(bpy.types.Operator):
    """Sets all child Turok Object Types to be the same as the current Turok Object Type"""
    bl_idname = "turok_object.set_turokobjects_children"
    bl_label = "Set all child Turok Object Types to current Turok Object Type"
    bl_options = {'UNDO'}

    @classmethod
    def poll(cls, context):
        return context.object is not None
            
    def execute(self, context):
        obj = context.object
        for child in obj.children:
            child.t1.objType = obj.t1.objType
        
        return {'FINISHED'}
    #end func
    
#end func

# -----------------------------------------------------------------------------
#
class TUROK_OT_validate_model(bpy.types.Operator):
    bl_idname = "turok_object.validate_model"
    bl_label = "Validate Export Turok Model"
    bl_description = "Validates the model for exporting to Turok and prints any errors to console"
    bl_options = {'UNDO'}

    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None:
            return False
        if obj.type != "ARMATURE" and obj.type != "MESH":
            return False
            
        isRootTurokObject = kex_utils.get_root_turok_object(obj, "MODEL") == obj
        return isRootTurokObject
    #end func
        
    def execute(self, context):
        validateResults = kex_utils.T1ValidateModel(context)
        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)
        elif len(validateResults["warnings"]) > 0:
            kex_utils.show_error("%s" % (validateResults["warnings"][0]), "Warning - See System Console for full details", toConsole=False)
        else:
            kex_utils.show_message("Everything's good!", "Model Validation", toConsole=False)
        #end if
        
        if len(validateResults["errors"]) > 0:
            for message in validateResults["errors"]:
                print("[Model Validation Error] %s" % (message))
            #end for
        #end if
        if len(validateResults["warnings"]) > 0:
            for message in validateResults["warnings"]:
                print("[Model Validation Warning] %s" % (message))
            #end for
        #end if
        
        return {'FINISHED'}
    #end func
       
# -----------------------------------------------------------------------------
#
class TUROK_OT_obj_bb_toggle(bpy.types.Operator):
    bl_idname = "turok_object.bb_toggle"
    bl_label = "Toggle Bounding Box Display"
    bl_description = "Toggle Bounding Box Display"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None:
            return False
            
        return kex_utils.get_root_turok_object(obj, "MODEL") == obj
    #end func
    
    def execute(self, context):
        obj = context.object
        bbObj = kex_utils.get_object(kex_utils.turok1BoundingBoxName)
        if bbObj is None:
            bbMin = obj.t1.boundingBoxMin
            bbMax = obj.t1.boundingBoxMax
            if bbMin == Vector((0.0, 0.0, 0.0)) and bbMax == Vector((0.0, 0.0, 0.0)):
                bbMin = Vector((-0.5, -0.5, -0.5))
                bbMax = Vector((0.5, 0.5, 0.5))
            #end if
            min = Matrix.Translation(obj.matrix_world.translation) @ Vector((bbMin[0], bbMin[1], bbMin[2]))
            max = Matrix.Translation(obj.matrix_world.translation) @ Vector((bbMax[0], bbMax[1], bbMax[2]))
            bbObj = kex_utils.create_cube(kex_utils.turok1BoundingBoxName, min, max)
            bbObj.lock_rotation = (True, True, True)
            bbObj.lock_rotation_w = True
            bbObj.lock_rotations_4d = True
            bbObj.show_in_front = True
            bbObj.display_type = "BOUNDS"
            bbObj.display.show_shadows = False
        else:
            bpy.data.objects.remove(bbObj)
        #end if
            
        return {'FINISHED'}
    #end func

# -----------------------------------------------------------------------------
#
class TUROK_OT_bb_update(bpy.types.Operator):
    bl_idname = "turok_object.bb_update"
    bl_label = "Update Bounding Box Display"
    bl_description = "Update Bounding Box Display using min/max"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        bbObj = kex_utils.get_object(kex_utils.turok1BoundingBoxName)
        if bbObj is None:
            return False
            
        obj = context.object
        if obj is None:
            return False
        return kex_utils.get_root_turok_object(obj, "MODEL") == obj
    
    def execute(self, context):
        obj = context.object
        bbObj = kex_utils.get_object(kex_utils.turok1BoundingBoxName)
        bpy.data.objects.remove(bbObj)
        bbMin = obj.t1.boundingBoxMin
        bbMax = obj.t1.boundingBoxMax
        if bbMin == Vector((0.0, 0.0, 0.0)) and bbMax == Vector((0.0, 0.0, 0.0)):
            bbMin = Vector((-0.5, -0.5, -0.5))
            bbMax = Vector((0.5, 0.5, 0.5))
        #end if
        min = Matrix.Translation(obj.matrix_world.translation) @ Vector((bbMin[0], bbMin[1], bbMin[2]))
        max = Matrix.Translation(obj.matrix_world.translation) @ Vector((bbMax[0], bbMax[1], bbMax[2]))
        bbObj = kex_utils.create_cube(kex_utils.turok1BoundingBoxName, min, max)
        bbObj.lock_rotation = (True, True, True)
        bbObj.lock_rotation_w = True
        bbObj.lock_rotations_4d = True
        bbObj.show_in_front = True
        bbObj.display_type = "BOUNDS"
        bbObj.display.show_shadows = False
            
        return {'FINISHED'}
       
# -----------------------------------------------------------------------------
#
class TUROK_OT_obj_bb_to_min_max(bpy.types.Operator):
    bl_idname = "turok_object.bb_to_min_max"
    bl_label = "Set Min/Max to BB Display"
    bl_description = "Set Min/Max to BB Display"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        bbObj = kex_utils.get_object(kex_utils.turok1BoundingBoxName)
        if bbObj is None:
            return False
            
        obj = context.object
        if obj is None:
            return False
        return kex_utils.get_root_turok_object(obj, "MODEL") == obj
    
    def execute(self, context):
        obj = context.object
        bbObj = kex_utils.get_object(kex_utils.turok1BoundingBoxName)
        min = [math.inf, math.inf, math.inf]
        max = [-math.inf, -math.inf, -math.inf]
        bbLoc, bbRot, bbScale = bbObj.matrix_world.decompose()
        bbox_corners = [Matrix.Translation(bbLoc) @ kex_utils.VectorMultiply(Vector(corner), bbScale) for corner in bbObj.bound_box]
        bbox_corners = [Matrix.Translation(obj.matrix_world.translation).inverted() @ Vector(corner) for corner in bbox_corners]
        for corner in bbox_corners:
            for i in range(3):
                if corner[i] < min[i]:
                    min[i] = corner[i]
                if corner[i] > max[i]:
                    max[i] = corner[i]
            #end for
        #end for
        obj.t1.boundingBoxMin = min
        obj.t1.boundingBoxMax = max
            
        return {'FINISHED'}
       
# -----------------------------------------------------------------------------
#
class TUROK_OT_obj_bb_help(bpy.types.Operator):
    bl_idname = "turok_object.bb_help"
    bl_label = "Show Bounding Box Tip"
    bl_description = "Shows Bounding Box Help Message"
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None:
            return False
            
        return kex_utils.get_root_turok_object(obj, "MODEL") == obj
    
    def execute(self, context):
        kex_utils.show_message(title="If this model is being used as a Staticmesh then set these to all zero.",
                                message="Otherwise set these values to encompass the model in all it's animations.",
                                toConsole = False)
        return {'FINISHED'}
# -----------------------------------------------------------------------------
#
class TUROK_OT_apply_bmesh_sector_data(bpy.types.Operator):
    bl_idname = "turok_object.apply_sector_data"
    bl_label = "Apply Sector Data"
    bl_description = "Sets all the sector data to the selected sectors"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None:
            return False
            
        return obj.type == "MESH" and obj.t1.objType == "MAP SECTORS" and obj.mode == 'EDIT'
    #end func
                    
    def execute(self, context):
        obj = context.object
        bm = bmesh.from_edit_mesh(obj.data)
        selected_faces = [f for f in bm.faces if f.select]
        kex_utils.create_BMLayers(bm)
        for f in selected_faces:
            kex_utils.bmface_to_obj_sector(obj, f)
            kex_utils.bmface_update_material_index(obj.data, f)
        #end for
        bmesh.update_edit_mesh(obj.data)
        handlers.recreate_sector_ceiling_edit_mesh(obj, None, bm)
        kex_utils.sectorDirty = False
        
        return {'FINISHED'}
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TUROK_OT_apply_bmesh_sector_data_difference(bpy.types.Operator):
    bl_idname = "turok_object.apply_sector_data_difference"
    bl_label = "Apply Sector Data Difference"
    bl_description = "Applies only the sector data you made changes to"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        if obj is None:
            return False
            
        return obj.type == "MESH" and obj.t1.objType == "MAP SECTORS" and obj.mode == 'EDIT'
    #end func
                    
    def execute(self, context):
        obj = context.object
        bm = bmesh.from_edit_mesh(obj.data)
        selected_faces = [f for f in bm.faces if f.select]
        kex_utils.create_BMLayers(bm)
        active_face = bm.faces.active
        dict = kex_utils.get_obj_sector_dict(obj, active_face)
        for f in selected_faces:
            kex_utils.bmface_to_dict(dict, f)
            kex_utils.bmface_update_material_index(obj.data, f)
        #end for
        bmesh.update_edit_mesh(obj.data)
        handlers.recreate_sector_ceiling_edit_mesh(obj, None, bm)
        kex_utils.sectorDirty = False

        return {'FINISHED'}
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TUROK_OT_recreate_sectors_ceiling_mesh(bpy.types.Operator):
    bl_idname = "turok_object.recreate_sector_ceiling_mesh"
    bl_label = "Recreate Sectors Ceiling Mesh"
    bl_description = "Recreates the mesh for the sectors ceiling"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        return obj is not None and obj.type == "MESH" and obj.t1.objType == "MAP CEILING SECTORS"
    #end func
                    
    def execute(self, context):
        obj = context.object
        sectorsObj = kex_utils.get_root_turok_object(obj, "MAP SECTORS", True)
        if sectorsObj is not None and sectorsObj.type == "MESH":
            handlers.recreate_sector_ceiling_edit_mesh(sectorsObj, obj, None)
            if sectorsObj.mode != 'EDIT':
                lastMode = context.object.mode
                kex_utils.mode_set('EDIT')
                kex_utils.mode_set(lastMode)
            #end if
        #end if
        return {'FINISHED'}
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TUROK_OT_clear_visibility(bpy.types.Operator):
    bl_idname = "turok_object.clear_visibility_tables"
    bl_label = "Clears Visibility"
    bl_description = "Clears the visibility tables for this map"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        return obj is not None and obj.t1.objType == "MAP ROOT" and (obj.t1.mapVisImage is not None or obj.t1.mapVisCount != 0 or obj.t1.mapVisSize != 0)
    #end func
                    
    def execute(self, context):
        obj = context.object
        obj.t1.mapVisCount = 0
        obj.t1.mapVisSize = 0
        if obj.t1.mapVisImage is not None:
            obj.t1.mapVisImage = None
        #end if
        return {'FINISHED'}
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TUROK_OT_set_sun_direction(bpy.types.Operator):
    bl_idname = "turok_object.set_sun_direction"
    bl_label = "Set Sunlight Direction"
    bl_description = "Sets the sun direction to the cameras viewport direction"
    bl_options = {'UNDO'}
    
    @classmethod
    def poll(cls, context):
        obj = context.object
        return obj is not None and obj.t1.objType == "MAP ROOT" and kex_utils.cachedRegion3D is not None
    #end func
                    
    def execute(self, context):
        obj = context.object
        region = bpy.context.region
        obj.t1.mapSunlightDirection = bpy_extras.view3d_utils.region_2d_to_vector_3d(region, kex_utils.cachedRegion3D, (region.width / 2, region.height / 2))
        return {'FINISHED'}
    #end func
#end class

# -----------------------------------------------------------------------------
#
class TUROK_PT_object(bpy.types.Panel):
    bl_label = "Turok Object"
    bl_idname = "TUROK_PT_object"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        obj = context.object
        if obj is None:
            return
        
        layout = self.layout
        
        row = layout.row(align=True)
        row.label(text="", icon="MONKEY")
        row.prop(obj.t1, "objType", text="Type")
        row.separator()
        row.operator(TUROK_OT_set_turokobjects_children.bl_idname, icon="AUTOMERGE_ON", text="")
        
        if obj.t1.objType == "MODEL":
            isRootTurokObject = kex_utils.get_root_turok_object(obj, "MODEL") == obj
            
            if isRootTurokObject:
                row = layout.row()
                row.prop(obj.t1, "textureSetIndex", text="Texture Set")
            #end if

            box = layout.box()

            #if obj is parented to a bone and is a mesh type - what variant mesh number is it
            #if obj.type == "MESH" and obj.parent is not None and obj.parent_type == "BONE":
            if obj.type == "MESH" and not isRootTurokObject:
                row = box.row()
                row.prop(obj.t1, "variantIndex")
            #end if
            
            if isRootTurokObject:
                if obj.type == "ARMATURE":
                    row = box.row()
                    row.label(text="Yaw Offset (Animate)")
                    row.prop(obj.t1, "yawOffset", text="")
                #end if
                
                row = box.row()
                row.separator()
                row.operator(TUROK_OT_validate_model.bl_idname, icon="QUESTION", text="Validate Export Turok Model")
                row.separator()
            #end if
                
            #draw key frame action if has animation action
            if obj.animation_data is not None and obj.animation_data.action is not None:
                turok_draw_action_panel(self, context, obj.animation_data.action, True)

            #show bounding box if is root turok object
            if isRootTurokObject:
                row = layout.row()
                row.prop(obj.t1, "showBoundingBox", icon="TRIA_DOWN" if obj.t1.showBoundingBox else "TRIA_RIGHT", icon_only=True, emboss=False)
                row.label(text="Bounding Box", icon="SHADING_BBOX")
                if obj.t1.showBoundingBox:
                    row.operator(TUROK_OT_obj_bb_help.bl_idname, text="", icon="QUESTION")
                    box = layout.box()
                    row = box.row(align=True)
                    row = row.split(factor=0.15)
                    row.label(text="Min")
                    row.prop(obj.t1, "boundingBoxMin", text="")
                    row = box.row(align=True)
                    row = row.split(factor=0.15)
                    row.label(text="Max")
                    row.prop(obj.t1, "boundingBoxMax", text="")
                    bbObj = kex_utils.get_object(kex_utils.turok1BoundingBoxName)
                    pointIcon = "KEYTYPE_EXTREME_VEC" if bbObj is None else "X"
                    pointText = "Show BB" if bbObj is None else "Remove BB"
                    box.row()
                    row = box.row(align=True)
                    row.operator(TUROK_OT_obj_bb_toggle.bl_idname, icon=pointIcon, text=pointText)
                    row.operator(TUROK_OT_bb_update.bl_idname, icon="EXPORT", text="Update BB")
                    row.operator(TUROK_OT_obj_bb_to_min_max.bl_idname, icon="IMPORT", text="Set Min/Max")
                #end if
            #end if
            
        elif obj.t1.objType == "MAP STATICMESH":
            row = layout.row()
            row.prop(obj.t1, "staticmeshModelPath")
            row = layout.row()
            row.prop(obj.t1, "staticmeshRadius")
            row = layout.row()
            row.prop(obj.t1, "staticmeshHeight")
            row = layout.row()
            row.prop(obj.t1, "staticmeshTexture")
            row = layout.row()
            row.label(text="Flags")
            row = layout.row()
            row.props_enum(obj.t1, "staticmeshFlags")
            if kex_utils.show_hidden_data():
                row = layout.row()
                row.prop(obj.t1, "staticmeshCullBits")
                row = layout.row()
                row.prop(obj.t1, "staticmeshSectorIndex")
                row = layout.row()
                row.prop(obj.t1, "staticmeshBoundsMin")
                row = layout.row()
                row.prop(obj.t1, "staticmeshBoundsMax")
            #end if
        elif obj.t1.objType == "MAP ACTOR":
            row = layout.row()
            row.prop(obj.t1, "actorTabs", expand=True)
            box = layout.box()
            row = box.row()
            if obj.t1.actorTabs == 'SETUP':
                row.prop(obj.t1, "actorType")
                row = box.row()
                row.prop(obj.t1, "actorModelPath")
                row = box.row()
                row.prop(obj.t1, "actorAnimPath")
            elif obj.t1.actorTabs == 'SPAWNFLAGS':
                row.prop(obj.t1, "actorFlagsTabs", expand=True)
                row = box.row()
                if obj.t1.actorFlagsTabs == '1':
                    row.props_enum(obj.t1, "actorSpawnFlags1")
                elif obj.t1.actorFlagsTabs == '2':
                    row.props_enum(obj.t1, "actorSpawnFlags2")
                else:
                    row.props_enum(obj.t1, "actorSpawnFlags3")
                #end if
            elif obj.t1.actorTabs == 'AI':
                row.prop(obj.t1, "actorSightRange")
                row = box.row()
                row.prop(obj.t1, "actorSightFOV")
                row = box.row()
                row.prop(obj.t1, "actorLoudRange")
                row = box.row()
                row.prop(obj.t1, "actorQuietRange")
                row = box.row()
                row.prop(obj.t1, "actorLeashRadius")
                row = box.row()
                row.prop(obj.t1, "actorAttackRange")
                row = box.row()
                row.prop(obj.t1, "actorFlyHeight")
                row = box.row()
                row.prop(obj.t1, "actorMeleeRange")
                row = box.row()
                row.prop(obj.t1, "actorAttackChance")
                row = box.row()
                row.prop(obj.t1, "actorMaxRegenerations")
            elif obj.t1.actorTabs == 'GENERAL':
                row.prop(obj.t1, "actorWidth")
                row = box.row()
                row.prop(obj.t1, "actorWallWidth")
                row = box.row()
                row.prop(obj.t1, "actorHeight")
                row = box.row()
                row.prop(obj.t1, "actorDeadHeight")
                row = box.row()
                row.prop(obj.t1, "actorTriggerDelay")
                row = box.row()
                row.prop(obj.t1, "actorHealth")
                row = box.row()
                row.prop(obj.t1, "actorTID")
                row = box.row()
                row.prop(obj.t1, "actorTargetTID")
                row = box.row()
                row.prop(obj.t1, "actorTriggerAnim")
            elif obj.t1.actorTabs == 'MISC':
                row.prop(obj.t1, "actorVariant")
                row = box.row()
                row.prop(obj.t1, "actorTexture")
                row = box.row()
                row.prop(obj.t1, "actorParams1")
                row = box.row()
                row.prop(obj.t1, "actorParams2")
            #end if
        elif obj.t1.objType == "MAP SECTORS":
            if obj.type == "MESH":
                #prefs = kex_utils.prefs()
                #row = layout.row()
                #row.prop(prefs, "sectorAlpha")
                if obj.mode == 'EDIT':
                    bm = handlers.get_sectors_bmesh_edit(obj.data)
                    #bm = bmesh.from_edit_mesh(obj.data)
                    active_face = bm.faces.active
                    #has active face and active face is in selected faces
                    if 'FACE' in bm.select_mode and active_face is not None and True in [x.select for x in bm.faces]:
                        #row = layout.row()
                        # row.label(text="Sector Index: %s" % (active_face.index))
                        #row.label(text="Normal: %f, %f, %f" % (active_face.normal.x, active_face.normal.y, active_face.normal.z))
                        row = layout.row()
                        applyDifText = "Apply Changes"
                        applyAllText = "Apply All"
                        if kex_utils.sectorDirty:
                            applyDifText += "*"
                            applyAllText += "*"
                            row.alert = True
                        #end if
                        row.alignment = 'CENTER'
                        row.operator(TUROK_OT_apply_bmesh_sector_data_difference.bl_idname, text=applyDifText)
                        row.separator()
                        row.operator(TUROK_OT_apply_bmesh_sector_data.bl_idname, text=applyAllText)
                        layout.box()
                        #selection options - select similar sectors that have the same data, flood select (sectors adajcent with same data)
                        #select sectors based on custom options popup menu (hit ok to select them)
                        
                        row = layout.row()
                        row.prop(obj.t1, "sec_tabs", expand=True)
                        box = layout.box()
                        row = box.row(align=True)
                        if obj.t1.sec_tabs == "SETTINGS":
                            row = box.row()
                            row.prop(obj.t1, "sec_fogColor")
                            row = box.row()
                            row.prop(obj.t1, "sec_waterColor")
                            row = box.row()
                            row.label(text="Automap Color")
                            row.prop(obj.t1, "sec_automapColor", text="")
                            row = box.row()
                            row.prop(obj.t1, "sec_fogStart")
                            row = box.row()
                            row.prop(obj.t1, "sec_fogZFar")
                            row = box.row()
                            row.prop(obj.t1, "sec_waterStart")
                            row = box.row()
                            row.prop(obj.t1, "sec_waterZFar")
                            row = box.row()
                            row.prop(obj.t1, "sec_waterHeight")
                            row = box.row()
                            row.prop(obj.t1, "sec_skyHeight")
                            row = box.row()
                            row.prop(obj.t1, "sec_skySpeed")
                            row = box.row()
                            row.prop(obj.t1, "sec_blendLength")
                            row = box.row()
                            row.prop(obj.t1, "sec_arg1")
                            row.prop(obj.t1, "sec_arg4")
                            row = box.row()
                            row.prop(obj.t1, "sec_arg2")
                            row.prop(obj.t1, "sec_arg5")
                            row = box.row()
                            row.prop(obj.t1, "sec_arg3")
                            row.prop(obj.t1, "sec_arg6")
                            row = box.row()
                            row.label(text="Floor Impact ID")
                            row.prop(obj.t1, "sec_floorImpactID", text="")
                            row = box.row()
                            row.label(text="Wall Impact ID")
                            row.prop(obj.t1, "sec_wallImpactID", text="")
                            row = box.row(align=True)
                            row.prop(obj.t1, "sec_ambience")
                            row.label(text=kex_utils.get_ambience_desc(obj.t1.sec_ambience))
                            row.operator(TUROK_OT_play_ambience.bl_idname, icon='PLAY', text="").ambienceID = obj.t1.sec_ambience
                            row.operator(TUROK_OT_stop_all_sounds.bl_idname, icon='PAUSE', text="")
                            row = box.row(align=True)
                            row.prop(obj.t1, "sec_music")
                            row.label(text=kex_utils.get_music_desc(obj.t1.sec_music))
                            row.operator(TUROK_OT_play_music.bl_idname, icon='PLAY', text="").musicID = obj.t1.sec_music
                            row.operator(TUROK_OT_stop_all_sounds.bl_idname, icon='PAUSE', text="")
                            row = box.row()
                            row.prop(obj.t1, "sec_drawOrder")
                            if kex_utils.show_hidden_data():
                                row = box.row()
                                row.prop(obj.t1, "sec_cullBits")
                                row = box.row()
                                row.prop(obj.t1, "sec_unused1")
                                row = box.row()
                                row.prop(obj.t1, "sec_unused2")
                            #end if
                        else: #FLAGS
                            row = box.row(align=True)
                            row.props_enum(obj.t1, "sec_flags")
                        #end if
                        row = layout.row()
                        if kex_utils.sectorDirty:
                            row.alert = True
                        #end if
                        row.alignment = 'CENTER'
                        row.operator(TUROK_OT_apply_bmesh_sector_data_difference.bl_idname, text=applyDifText)
                        row.operator(TUROK_OT_apply_bmesh_sector_data.bl_idname, text=applyAllText)
                    #end if
                #end if
            #end if
        elif obj.t1.objType == "MAP CEILING SECTORS":
            row = layout.row()
            row.operator(TUROK_OT_recreate_sectors_ceiling_mesh.bl_idname, text="Recreate Mesh")
        elif obj.t1.objType == "MAP SUN":
            pass
        elif obj.t1.objType == "MAP ROOT":
            box = layout.box()
            row = box.row()
            row = row.split(factor=0.35)
            row.label(text="Sunlight Direction")
            row.operator(TUROK_OT_set_sun_direction.bl_idname, text="Sunlight Direction From Camera")
            row = box.row()
            row.prop(obj.t1, "mapSunlightDirection", text="")
            row = layout.row()
            row.prop(obj.t1, "mapSunlightColor")
            row = layout.row()
            row.prop(obj.t1, "mapAmbientColor")
            row = layout.row()
            row.prop(obj.t1, "mapSkyMaterial")
            
            if kex_utils.show_hidden_data():
                box = layout.box()
                row = box.row()
                #row = layout.row()
                row.prop(obj.t1, "mapVisCount")
                row = box.row()
                row.prop(obj.t1, "mapVisSize")
                row = box.row()
                row.prop(obj.t1, "mapVisImage")
                row = box.row()
                row.operator(TUROK_OT_clear_visibility.bl_idname)
            else:
                row = layout.row()
                row.operator(TUROK_OT_clear_visibility.bl_idname)
            #end if
        #end if
    #end func
    
#end class
# -----------------------------------------------------------------------------
#
class TUROK_PT_action(bpy.types.Panel):
    bl_label = "Turok Action"
    bl_idname = "TUROK_PT_action"
    bl_space_type = 'DOPESHEET_EDITOR'
    bl_region_type = 'UI'
    bl_context = "action"

    def draw(self, context):
        obj = context.object
        if obj is None or obj.animation_data is None or obj.animation_data.action is None:
            return
            
        action = obj.animation_data.action
        turok_draw_action_panel(self, context, action, False)
    #end func
#end class

# -----------------------------------------------------------------------------
#
def turok_draw_action_panel(self, context, action, showCollapse):
    obj = context.object
    layout = self.layout
    
    tAction = action.t1
    
    row = layout.row()
    if showCollapse:
        row.prop(obj.t1, "showAnimAction", icon="TRIA_DOWN" if obj.t1.showAnimAction else "TRIA_RIGHT", icon_only=True, emboss=False)
        row.label(text="Anim %d: %s" % (tAction.animID, action.name), icon="ANIM", translate=False)
        if not obj.t1.showAnimAction:
            return
    else:
        row.separator()
        row.label(text=action.name, icon="ANIM", translate=False)        

    box = layout.box()
    row = box.row(align=True)
    row.prop(tAction, "animID")
    row.label(text=kex_utils.get_animation_name(tAction.animID))
    row.operator(TUROK_OT_anim_list.bl_idname, icon='COLLAPSEMENU', text="")
    row = box.row()
    row.prop(tAction, "marker")
    row = box.row()
    row.enabled = tAction.marker
    row.prop(tAction, "blend")
    row = box.row(align=True)
    row.enabled = tAction.marker
    row.prop(tAction, "loopFrame")
    row.operator(TUROK_OT_loop_frame_last.bl_idname, icon='FRAME_NEXT', text="")
    row = box.row()
    keyFramesText = 'Action Key Frames'
    if len(tAction.keyFrames) > 0:
        keyFramesText = 'Action Key Frames (%d)' % (len(tAction.keyFrames))
    row.label(text=keyFramesText)
    row = box.row()
    row.template_list('TUROK_UL_key_frames_props', "TurokObjectKeyFramesPropGroup", tAction, "keyFrames", tAction, "keyFramesIndex", type='DEFAULT')
    
    col = row.column(align=True)
    col.operator(TUROK_OT_key_frame_action.bl_idname, icon='ADD', text="").action = 'ADD'
    col.operator(TUROK_OT_key_frame_action.bl_idname, icon='REMOVE', text="").action = 'REMOVE'
    col.separator()
    col.menu(TUROK_MT_key_frame_special_menu.bl_idname, icon='DOWNARROW_HLT', text="")
    col.separator()
    col.operator(TUROK_OT_key_frame_action.bl_idname, icon='TRIA_UP', text="").action = 'UP'
    col.operator(TUROK_OT_key_frame_action.bl_idname, icon='TRIA_DOWN', text="").action = 'DOWN'
    
    if tAction.keyFramesIndex >= 0 and tAction.keyFramesIndex < len(tAction.keyFrames):
        item = tAction.keyFrames[tAction.keyFramesIndex]
        argNames = item.get_arg_names()
        row = box.row(align=True)
        row.prop(item, "frame")
        row.operator(TUROK_OT_key_frame_set_cur_frame.bl_idname, icon='IMPORT', text="")
        row.operator(TUROK_OT_key_frame_set_anim_frame.bl_idname, icon='EXPORT', text="")
        row = box.row()
        row.prop(item, "event")
        row.label(text=kex_utils.get_animation_action_name(item.event))
        row.operator(TUROK_OT_anim_action_list.bl_idname, icon='COLLAPSEMENU', text="")
        
        #Arg 1
        row = box.row(align=True)
        row.prop(item, "arg1", text=argNames[0])
        arg1Text = kex_utils.get_animation_action_arg_lookup(item.event, 0, item.arg1)
        if arg1Text != "":
            row.label(text=arg1Text)
        if kex_utils.is_arg_sound(item.event, 0):
            row.operator(TUROK_OT_play_sound_id.bl_idname, icon='PLAY', text="").soundID = item.arg1
            row.operator(TUROK_OT_stop_all_sounds.bl_idname, icon='PAUSE', text="")
            row.separator()
            row.operator(TUROK_OT_anim_action_arg_sound_list.bl_idname, icon='COLLAPSEMENU', text="").argIndex = 0
        elif kex_utils.is_arg_fx(item.event, 0):
            row.operator(TUROK_OT_anim_action_arg_fx_list.bl_idname, icon='COLLAPSEMENU', text="").argIndex = 0
            
        #Arg 2
        row = box.row(align=True)
        row.prop(item, "arg2", text=argNames[1])
        arg2Text = kex_utils.get_animation_action_arg_lookup(item.event, 1, item.arg2)
        if arg2Text != "":
            row.label(text=arg2Text)
        if kex_utils.is_arg_sound(item.event, 1):
            row.operator(TUROK_OT_play_sound_id.bl_idname, icon='PLAY', text="").soundID = item.arg2
            row.operator(TUROK_OT_stop_all_sounds.bl_idname, icon='PAUSE', text="")
            row.separator()
            row.operator(TUROK_OT_anim_action_arg_sound_list.bl_idname, icon='COLLAPSEMENU', text="").argIndex = 1
        elif kex_utils.is_arg_fx(item.event, 1):
            row.operator(TUROK_OT_anim_action_arg_fx_list.bl_idname, icon='COLLAPSEMENU', text="").argIndex = 1
            
        #Arg 3
        row = box.row(align=True)
        row.prop(item, "arg3", text=argNames[2])
        arg3Text = kex_utils.get_animation_action_arg_lookup(item.event, 2, item.arg3)
        if arg3Text != "":
            row.label(text=arg3Text)
        if kex_utils.is_arg_sound(item.event, 2):
            row.operator(TUROK_OT_play_sound_id.bl_idname, icon='PLAY', text="").soundID = item.arg3
            row.operator(TUROK_OT_stop_all_sounds.bl_idname, icon='PAUSE', text="")
            row.separator()
            row.operator(TUROK_OT_anim_action_arg_sound_list.bl_idname, icon='COLLAPSEMENU', text="").argIndex = 2
        elif kex_utils.is_arg_fx(item.event, 2):
            row.operator(TUROK_OT_anim_action_arg_fx_list.bl_idname, icon='COLLAPSEMENU', text="").argIndex = 2
            
        #Arg 4
        row = box.row(align=True)
        row.prop(item, "arg4", text=argNames[3])
        arg4Text = kex_utils.get_animation_action_arg_lookup(item.event, 3, item.arg4)
        if arg4Text != "":
            row.label(text=arg4Text)
        if kex_utils.is_arg_sound(item.event, 3):
            row.operator(TUROK_OT_play_sound_id.bl_idname, icon='PLAY', text="").soundID = item.arg4
            row.operator(TUROK_OT_stop_all_sounds.bl_idname, icon='PAUSE', text="")
            row.separator()
            row.operator(TUROK_OT_anim_action_arg_sound_list.bl_idname, icon='COLLAPSEMENU', text="").argIndex = 3
        elif kex_utils.is_arg_fx(item.event, 3):
            row.operator(TUROK_OT_anim_action_arg_fx_list.bl_idname, icon='COLLAPSEMENU', text="").argIndex = 3

        #KeyFrameEvent Point options
        posXArgIndex = kex_utils.index_of(argNames, "X Offset")
        posYArgIndex = kex_utils.index_of(argNames, "Y Offset")
        posZArgIndex = kex_utils.index_of(argNames, "Z Offset")
        radiusArgIndex = kex_utils.index_of(argNames, "Radius")
        if posXArgIndex != -1 and posYArgIndex != -1 and posZArgIndex != -1:
            turok1PointObj = kex_utils.get_object(kex_utils.turok1ActionPointName)
            pointIcon = "KEYTYPE_EXTREME_VEC" if turok1PointObj is None else "X"
            pointText = "Show Point" if turok1PointObj is None else "Remove Point"
            box.row()
            row = box.row(align=True)
            row.operator(TUROK_OT_key_frame_toggle_point.bl_idname, icon=pointIcon, text=pointText)
            row.operator(TUROK_OT_key_frame_point_update.bl_idname, icon="EXPORT", text="Update Point")
            row.operator(TUROK_OT_key_frame_point_to_args.bl_idname, icon="IMPORT", text="Set Args")
        #end if
        
    #end if
#end func

# -----------------------------------------------------------------------------
#
def register():
    bpy.utils.register_class(TurokObjectKeyFramesPropGroup)
    bpy.utils.register_class(TUROK_UL_key_frames_props)
    bpy.utils.register_class(TurokObjectSettings)
    bpy.utils.register_class(TurokAnimationActionSettings)
    bpy.utils.register_class(TUROK_OT_loop_frame_last)
    bpy.utils.register_class(TUROK_OT_key_frame_action)
    bpy.utils.register_class(TUROK_OT_key_frame_copy)
    bpy.utils.register_class(TUROK_OT_key_frame_paste)
    bpy.utils.register_class(TUROK_OT_key_frame_set_anim_frame)
    bpy.utils.register_class(TUROK_OT_key_frame_set_cur_frame)
    bpy.utils.register_class(TUROK_OT_key_frame_toggle_point)
    bpy.utils.register_class(TUROK_OT_key_frame_point_update)
    bpy.utils.register_class(TUROK_OT_key_frame_point_to_args)
    bpy.utils.register_class(TUROK_MT_key_frame_special_menu)
    bpy.utils.register_class(TUROK_OT_anim_action_list)
    bpy.utils.register_class(TUROK_OT_play_music)
    bpy.utils.register_class(TUROK_OT_play_sound_id)
    bpy.utils.register_class(TUROK_OT_play_ambience)
    bpy.utils.register_class(TUROK_OT_stop_all_sounds)
    bpy.utils.register_class(TUROK_OT_anim_action_arg_sound_list)
    bpy.utils.register_class(TUROK_OT_anim_action_arg_fx_list)
    bpy.utils.register_class(TUROK_OT_anim_list)
    bpy.utils.register_class(TUROK_OT_set_turokobjects_children)
    bpy.utils.register_class(TUROK_OT_validate_model)
    bpy.utils.register_class(TUROK_OT_obj_bb_toggle)
    bpy.utils.register_class(TUROK_OT_bb_update)
    bpy.utils.register_class(TUROK_OT_obj_bb_to_min_max)
    bpy.utils.register_class(TUROK_OT_obj_bb_help)
    bpy.utils.register_class(TUROK_OT_apply_bmesh_sector_data)
    bpy.utils.register_class(TUROK_OT_apply_bmesh_sector_data_difference)
    bpy.utils.register_class(TUROK_OT_recreate_sectors_ceiling_mesh)
    bpy.utils.register_class(TUROK_OT_clear_visibility)
    bpy.utils.register_class(TUROK_OT_set_sun_direction)
    bpy.utils.register_class(TUROK_PT_object)
    bpy.utils.register_class(TUROK_PT_action)
    bpy.types.Object.t1 = bpy.props.PointerProperty(type=TurokObjectSettings)
    bpy.types.Action.t1 = bpy.props.PointerProperty(type=TurokAnimationActionSettings)
#end func

# -----------------------------------------------------------------------------
#     
def unregister():
    bpy.utils.unregister_class(TurokObjectKeyFramesPropGroup)
    bpy.utils.unregister_class(TUROK_UL_key_frames_props)
    bpy.utils.unregister_class(TurokObjectSettings)
    bpy.utils.unregister_class(TurokAnimationActionSettings)
    bpy.utils.unregister_class(TUROK_OT_loop_frame_last)
    bpy.utils.unregister_class(TUROK_OT_key_frame_action)
    bpy.utils.unregister_class(TUROK_OT_key_frame_copy)
    bpy.utils.unregister_class(TUROK_OT_key_frame_paste)
    bpy.utils.unregister_class(TUROK_OT_key_frame_set_anim_frame)
    bpy.utils.unregister_class(TUROK_OT_key_frame_set_cur_frame)
    bpy.utils.unregister_class(TUROK_OT_key_frame_toggle_point)
    bpy.utils.unregister_class(TUROK_OT_key_frame_point_update)
    bpy.utils.unregister_class(TUROK_OT_key_frame_point_to_args)
    bpy.utils.unregister_class(TUROK_MT_key_frame_special_menu)
    bpy.utils.unregister_class(TUROK_OT_anim_action_list)
    bpy.utils.unregister_class(TUROK_OT_play_music)
    bpy.utils.unregister_class(TUROK_OT_play_sound_id)
    bpy.utils.unregister_class(TUROK_OT_play_ambience)
    bpy.utils.unregister_class(TUROK_OT_stop_all_sounds)
    bpy.utils.unregister_class(TUROK_OT_anim_action_arg_sound_list)
    bpy.utils.unregister_class(TUROK_OT_anim_action_arg_fx_list)
    bpy.utils.unregister_class(TUROK_OT_anim_list)
    bpy.utils.unregister_class(TUROK_OT_set_turokobjects_children)
    bpy.utils.unregister_class(TUROK_OT_validate_model)
    bpy.utils.unregister_class(TUROK_OT_obj_bb_toggle)
    bpy.utils.unregister_class(TUROK_OT_bb_update)
    bpy.utils.unregister_class(TUROK_OT_obj_bb_to_min_max)
    bpy.utils.unregister_class(TUROK_OT_obj_bb_help)
    bpy.utils.unregister_class(TUROK_OT_apply_bmesh_sector_data)
    bpy.utils.unregister_class(TUROK_OT_apply_bmesh_sector_data_difference)
    bpy.utils.unregister_class(TUROK_OT_recreate_sectors_ceiling_mesh)
    bpy.utils.unregister_class(TUROK_OT_clear_visibility)
    bpy.utils.unregister_class(TUROK_OT_set_sun_direction)
    bpy.utils.unregister_class(TUROK_PT_object)
    bpy.utils.unregister_class(TUROK_PT_action)
    del bpy.types.Object.t1
    del bpy.types.Action.t1
#end func
