from bge import logic
from math import radians, cos

# Helper functions:

def get_material(object, name):
    """Return a object material."""    
    for mesh in object.meshes:  
        for mat in mesh.materials:     
            if str(mat) == "MA" + name:
                return mat

def get_view_matrix(camera):
    """Return view matrix."""
    view_mat = camera.worldTransform
    view_mat_inv = view_mat.inverted()
    return view_mat_inv
    
def _lights_transform_handler():
    """Update position and orientation properties."""       
    scene = logic.getCurrentScene()
    view = get_view_matrix(scene.active_camera)   
    
    for light in scene.lights:
        transform = light.worldTransform
        light['_position'] = (view * light.position)      
        light['_orientation'] = (view * -(transform.col[2].normalized()))
   
   
LIGHTS_HANDLER = _lights_transform_handler
LIGHT_TYPES = {'SPOT':0, 'SUN':1, 'POINT':2, 'HEMI':3}
  
  
# Light uniforms:
        
def update_light_transform(shader, scene, names=["lightPosition", "lightOrientation"]):
    """Update uniforms for light position and orientation."""    
    
    if not LIGHTS_HANDLER in scene.pre_draw:
        LIGHTS_HANDLER()
        scene.pre_draw.append(LIGHTS_HANDLER)
    
    for i, light in enumerate(scene.lights, 0):   
        shader.setUniform3f(names[0] + "[%s]"%i, *light['_position'])
        shader.setUniform3f(names[1] + "[%s]"%i, *light['_orientation'].xyz)
            
def update_light_settings(shader, scene, names=["lightType", "lightDistance", "lightColor", "lightSpotsize", "lightSpotblend", "lightCount"]):
    """Update uniforms for light settings."""    
    for i, light in enumerate(scene.lights, 0):
    
        if 'type' in light:
            # Lighttype must set manually via property ('SPOT', 'SUN', 'POINT', 'HEMI') because hemi lamps can't be detected ingame.
            shader.setUniform1i(names[0] + "[%s]"%i, LIGHT_TYPES[light['type']])     
        else:
            # Upbge can detect all light types without property.
            shader.setUniform1i(names[0] + "[%s]"%i, light.type)
            
        shader.setUniform1f(names[1] + "[%s]"%i, light.distance)
        
        color = light.color
        energy = light.energy
        shader.setUniform3f(names[2] + "[%s]"%i, color[0] * energy, color[1] * energy, color[2] * energy)
        
        spotsize = cos(radians(light.spotsize) * 0.5)
        shader.setUniform1f(names[3] + "[%s]"%i, spotsize)
        
        spotblend = (1.0 - spotsize) * light.spotblend
        shader.setUniform1f(names[4] + "[%s]"%i, spotblend)
        
        shader.setUniform1i(names[5], i+1)

        
# World uniforms:

def update_ambient_color(shader, scene, name="ambientColor"):
    """Update uniform for ambient color."""
    shader.setUniform3f(name, *scene.world.ambientColor)  
    
def update_mist(shader, scene, names=["mistEnable", "mistStart", "mistDistance", "mistIntensity", "mistType", "mistColor"]):
    """Update uniforms for mist."""  
    shader.setUniform1f(names[0], scene.world.mistEnable)
    shader.setUniform1f(names[1], scene.world.mistStart)
    shader.setUniform1f(names[2], scene.world.mistDistance)
    shader.setUniform1f(names[3], scene.world.mistIntensity)
    shader.setUniform1f(names[4], scene.world.mistType)
    shader.setUniform3f(names[5], *scene.world.mistColor)
    
def update_world(shader, scene, names=["ambientColor", "mistEnable", "mistStart", "mistDistance", "mistIntensity", "mistType", "mistColor"]):
    """Update uniforms for world settings"""
    update_ambient_color(shader, scene, name=names[0])
    update_mist(shader, scene, names=[names[1], names[2], names[3], names[4], names[5], names[6]])
    
    
# Material uniforms:

def update_material(shader, material, names=["diffuseColor", "specularColor", "hardness", "emit"]):
    """Update uniforms for material settings."""
    update_diffuse_color(shader, material, name=names[0])
    update_specular(shader, material, names=[names[1], names[2]])
    update_emit(shader, material, name=names[3])

def update_diffuse_color(shader, material, name="diffuseColor"):
    """Update uniform for diffuse color."""
    shader.setUniform3f(name, *material.diffuseColor * material.diffuseIntensity)

def update_specular(shader, material, names=["specularColor", "hardness"]):
    """Update uniforms for specular."""
    update_specular_color(shader, material, name=names[0])
    update_specular_hardness(shader, material, name=names[1])

def update_specular_color(shader, material, name="specularColor"):
    """Update uniform for specular color."""
    shader.setUniform3f(name, *material.specularColor * material.specularIntensity)
    
def update_specular_hardness(shader, material, name="hardness"):
    """Update uniform for specular hardness."""
    shader.setUniform1f(name, material.hardness*4)#Blender returns wrong value (bug)  
    
def update_emission(shader, material, name="emission"):
    """Update uniform for emit."""
    shader.setUniform1f(name, material.emit)  