
#####################################################################################################
# 
#  ooooo   ooooo                             .o8  oooo
#  `888'   `888'                            "888  `888
#   888     888   .oooo.   ooo. .oo.    .oooo888   888   .ooooo.  oooo d8b
#   888ooooo888  `P  )88b  `888P"Y88b  d88' `888   888  d88' `88b `888""8P
#   888     888   .oP"888   888   888  888   888   888  888ooo888  888
#   888     888  d8(  888   888   888  888   888   888  888    .o  888
#  o888o   o888o `Y888""8o o888o o888o `Y8bod88P" o888o `Y8bod8P' d888b
# 
#####################################################################################################



import bpy
from bpy.app.handlers import persistent

from .. utils.extra_utils import dprint
from .. utils.extra_utils import is_rendered_view, all_3d_viewports

# oooooooooo.                                                                          oooo
# `888'   `Y8b                                                                         `888
#  888      888  .ooooo.  oo.ooooo.   .oooo.o  .oooooooo oooo d8b  .oooo.   oo.ooooo.   888 .oo.
#  888      888 d88' `88b  888' `88b d88(  "8 888' `88b  `888""8P `P  )88b   888' `88b  888P"Y88b
#  888      888 888ooo888  888   888 `"Y88b.  888   888   888      .oP"888   888   888  888   888
#  888     d88' 888    .o  888   888 o.  )88b `88bod8P'   888     d8(  888   888   888  888   888
# o888bood8P'   `Y8bod8P'  888bod8P' 8""888P' `8oooooo.  d888b    `Y888""8o  888bod8P' o888o o888o
#                          888                d"     YD                      888
#                         o888o               "Y88888P'                     o888o


from .. scattering.emitter import handler_emitter_check, handler_emitter_if_pinned_mode, handler_scene_emitter_cleanup

@persistent
def scatter5_depsgraph(scene,desp): 

    #debug print
    dprint("HANDLER: 'scatter5_depsgraph'",depsgraph=True)

    #check on emitter prop
    handler_emitter_check()

    #update emitter pointer if in pin mode
    handler_emitter_if_pinned_mode()

    #delete object properties if user altD/ShiftD an emitter object
    handler_scene_emitter_cleanup()

    #update active camera nodegroup
    update_active_camera_nodegroup()

    return None


# oooooooooooo                                                  
# `888'     `8                                                  
#  888         oooo d8b  .oooo.   ooo. .oo.  .oo.    .ooooo.    
#  888oooo8    `888""8P `P  )88b  `888P"Y88bP"Y88b  d88' `88b   
#  888    "     888      .oP"888   888   888   888  888ooo888   
#  888          888     d8(  888   888   888   888  888    .o   
# o888o        d888b    `Y888""8o o888o o888o o888o `Y8bod8P'   



from ..scattering.update_factory import update_active_camera_nodegroup

@persistent
def scatter5_frame_pre(scene,desp): 

    #debug print
    dprint("HANDLER: 'scatter5_frame_pre'",depsgraph=True)

    #update active camera nodegroup
    update_active_camera_nodegroup()

    return None

#from .. masks.map_curve import handler_update_map_curves
#from .. masks.refresh_all import refresh_selected

@persistent
def scatter5_frame_post(scene,desp): 

    scat_scene = bpy.context.scene.scatter5

    #debug print
    dprint("HANDLER: 'scatter5_frame_post'",depsgraph=True)

    #mask update animation
    # if bpy.context.scene.scatter5.mask_animation=="viewport":
    #     dprint("HANDLER: 'scatter5_frame_post'->'refresh_selected' ",depsgraph=True)
    #     refresh_selected()

    #map curve animation
    #handler_update_map_curves()

    return None



# ooooooooo.                               .o8                     
# `888   `Y88.                            "888                     
#  888   .d88'  .ooooo.  ooo. .oo.    .oooo888   .ooooo.  oooo d8b 
#  888ooo88P'  d88' `88b `888P"Y88b  d88' `888  d88' `88b `888""8P 
#  888`88b.    888ooo888  888   888  888   888  888ooo888  888     
#  888  `88b.  888    .o  888   888  888   888  888    .o  888     
# o888o  o888o `Y8bod8P' o888o o888o `Y8bod88P" `Y8bod8P' d888b    


bypassed = []
def set_particle_display(condition, only_visible=False,): #TODO Dorian, you'll need to use switch node instead of this shit
    """will do the equivalent of p.s_display_allow directly in the nodegroup"""

    global bypassed
    all_psys = [p  for o in bpy.data.objects for p in o.scatter5.particle_systems ]

    if (condition==False):

        for p in all_psys: 
            if p.s_display_allow:

                #only visible check, useful for rendered view switch
                if only_visible and p.hide_viewport:
                    continue
                #turn off property witouth touching interface
                p.property_update_bypass("s_display_allow", False,)
                #bypass
                bypassed.append(p.name)

    elif (condition==True):

        for p in all_psys:

            #only visible check, useful for rendered view switch
            if only_visible and p.hide_viewport:
                continue
            #restore correct node values from ui
            p.property_update_bypass("s_display_allow", p.s_display_allow,)    
            if p.name in bypassed:
                bypassed.remove(p.name)

        #make sure there's no leftovers
        if len(bypassed):
            for p in all_psys:
                if p.name in bypassed:
                    p.property_update_bypass("s_display_allow", p.s_display_allow,)    
            bypassed = []

    return None 

overlay_to_restore = []
def set_overlay(boolean):
    """will toggle off overlay while in renderede view""" 

    if (boolean==True):

        global overlay_to_restore
        for space in overlay_to_restore:
            try: space.overlay.show_overlays = True
            except: pass #perhaps space do not exists anymore so we need to be carreful 
        overlay_to_restore = []

    elif (boolean==False):

        for space in all_3d_viewports():
            if space.shading.type=="RENDERED":
                if space.overlay.show_overlays: 
                    overlay_to_restore.append(space)
                    space.overlay.show_overlays = False

    return None 

@persistent
def scatter5_render_pre(scene,desp): 

    scat_scene = bpy.context.scene.scatter5

    #debug print
    dprint("HANDLER: 'scatter5_render_pre'",depsgraph=True)

    # if scat_scene.mask_animation=="final":
    #     dprint("HANDLER: 'scatter5_render_pre'->'refresh_selected' ",depsgraph=True)
    #     refresh_selected()

    #TODO will need to use switch node later
    if scat_scene.update_auto_display_final_render:
        set_particle_display(False)

    return None

@persistent
def scatter5_render_post(scene,desp): 

    scat_scene = bpy.context.scene.scatter5

    #debug print
    dprint("HANDLER: 'scatter5_render_post'",depsgraph=True)

    #TODO will need to use switch node later
    if scat_scene.update_auto_display_final_render:
        set_particle_display(True)
    
    return None


# ooooo                                  .o8       ooooooooo.                          .
# `888'                                 "888       `888   `Y88.                      .o8
#  888          .ooooo.   .oooo.    .oooo888        888   .d88'  .ooooo.   .oooo.o .o888oo
#  888         d88' `88b `P  )88b  d88' `888        888ooo88P'  d88' `88b d88(  "8   888
#  888         888   888  .oP"888  888   888        888         888   888 `"Y88b.    888
#  888       o 888   888 d8(  888  888   888        888         888   888 o.  )88b   888 .
# o888ooooood8 `Y8bod8P' `Y888""8o `Y8bod88P"      o888o        `Y8bod8P' 8""888P'   "888"



@persistent
def scatter5_load_post(scene,desp): 

    scat_scene = bpy.context.scene.scatter5

    scat_scene.beta_active = True
    
    #debug print
    dprint(f"HANDLER: 'scatter5_load_post'", depsgraph=True)

    #check for renderedview states
    scatter5_launch_rendered_view_checks()

    return None


# ooooooooo.                               .o8                                     .o8       oooooo     oooo  o8o
# `888   `Y88.                            "888                                    "888        `888.     .8'   `"'
#  888   .d88'  .ooooo.  ooo. .oo.    .oooo888   .ooooo.  oooo d8b  .ooooo.   .oooo888         `888.   .8'   oooo   .ooooo.  oooo oooo    ooo
#  888ooo88P'  d88' `88b `888P"Y88b  d88' `888  d88' `88b `888""8P d88' `88b d88' `888          `888. .8'    `888  d88' `88b  `88. `88.  .8'
#  888`88b.    888ooo888  888   888  888   888  888ooo888  888     888ooo888 888   888           `888.8'      888  888ooo888   `88..]88..8'
#  888  `88b.  888    .o  888   888  888   888  888    .o  888     888    .o 888   888            `888'       888  888    .o    `888'`888'
# o888o  o888o `Y8bod8P' o888o o888o `Y8bod88P" `Y8bod8P' d888b    `Y8bod8P' `Y8bod88P"            `8'       o888o `Y8bod8P'     `8'  `8'


#TODO 
#SHOULD PROLLY USE A NODEGROUP FOR THIS PURPOSE ... (2x)
#->Create a 'is_rendered_view' nodegroup and let blender do the switching job

def scatter5_launch_rendered_view_checks():
    """launch rendered view check system"""

    #context un-available?
    #if it is the case that means that i try to use this function from the registering state of the plugin, 
    #so let just add a timer that will retry this function later 
    
    if ("bpy_restrict_state" in str(bpy.context)):
        def in_1_second():
            scatter5_launch_rendered_view_checks()
        bpy.app.timers.register(in_1_second, first_interval=1)
        
        return None 

    if (not bpy.app.timers.is_registered(scatter5_check_for_rendered_view)) or (not bpy.app.timers.is_registered(scatter5_check_for_non_rendered_view)):
        dprint("---LAUNCHING RENDERED VIEW CHECK---", depsgraph=True)
        bpy.app.timers.register(scatter5_check_for_rendered_view)

    return None 

#Timer double loop to detect rednered view changing states

def scatter5_check_for_rendered_view():
    """infinite rendered view state check loops, forced to do this because blender no longer send update signal when switching to rendered view"""

    try:     

        if bpy.context.scene.scatter5.emergency_timers_stops:
            return None 

        if not is_rendered_view():
            dprint("scatter5_check_for_rendered_view()", depsgraph=True)
            return 0.150

        dprint("---RENDERED_VIEW_DETECTED--- launching check_for_NON_rendered_view()", depsgraph=True)

        if bpy.context.scene.scatter5.update_auto_display_rendered:
            set_particle_display(False, only_visible=True)

        if bpy.context.scene.scatter5.update_auto_overlay_rendered:
            set_overlay(False)

        #launching another timer...
        if not bpy.app.timers.is_registered(scatter5_check_for_non_rendered_view):
            bpy.app.timers.register(scatter5_check_for_non_rendered_view)

        return None  

    except Exception as e:
        print("Scatter5 error with timer fct `scatter5_check_for_rendered_view`", e)
        return None 

def scatter5_check_for_non_rendered_view():
    """infinite rendered view state check loops, forced to do this because blender no longer send update signal when switching to rendered view"""

    try:     

        if bpy.context.scene.scatter5.emergency_timers_stops:
            return None 

        if is_rendered_view():
            dprint("check_for_NON_rendered_view()", depsgraph=True)
            return 0.150

        dprint("---RENDERED_VIEW_QUITED--- launching scatter5_check_for_rendered_view()", depsgraph=True)

        if bpy.context.scene.scatter5.update_auto_display_rendered:
            set_particle_display(True, only_visible=True)

        if bpy.context.scene.scatter5.update_auto_overlay_rendered:
            set_overlay(True)

        #launching another timer...
        if not bpy.app.timers.is_registered(scatter5_check_for_rendered_view):
            bpy.app.timers.register(scatter5_check_for_rendered_view)

        return None 

    except Exception as e:
        print("Scatter5 error with timer fct `scatter5_check_for_non_rendered_view`", e)
        return None 



#   ooooooooo.
#   `888   `Y88.
#    888   .d88'  .ooooo.   .oooooooo
#    888ooo88P'  d88' `88b 888' `88b
#    888`88b.    888ooo888 888   888
#    888  `88b.  888    .o `88bod8P'
#   o888o  o888o `Y8bod8P' `8oooooo.
#                          d"     YD
#                          "Y88888P'


def all_handlers():
    """return a list of handler stored in .blend"""
    return_list = []
    for oh in bpy.app.handlers:
        try:
            for h in oh:
                return_list.append(h)
        except: pass
    return return_list

def register():
    
    #depsgraph

    if scatter5_depsgraph not in all_handlers():
        bpy.app.handlers.depsgraph_update_post.append(scatter5_depsgraph)

    #frame_change

    if scatter5_frame_pre not in all_handlers():
        bpy.app.handlers.frame_change_pre.append(scatter5_frame_pre)

    if scatter5_frame_post not in all_handlers():
        bpy.app.handlers.frame_change_post.append(scatter5_frame_post)
        
    #render

    if scatter5_render_pre not in all_handlers():
        bpy.app.handlers.render_pre.append(scatter5_render_pre)

    if scatter5_render_post not in all_handlers():
        bpy.app.handlers.render_post.append(scatter5_render_post)

    #blend open 

    if scatter5_load_post not in all_handlers():
        bpy.app.handlers.load_post.append(scatter5_load_post)

    return 

def unregister():

    #remove the two tiemrs 

    if bpy.app.timers.is_registered(scatter5_check_for_non_rendered_view):
        bpy.app.timers.unregister(scatter5_check_for_non_rendered_view)

    if bpy.app.timers.is_registered(scatter5_check_for_rendered_view):
        bpy.app.timers.unregister(scatter5_check_for_rendered_view)


    for h in all_handlers():

        #depsgraph

        if(h==scatter5_depsgraph):
            bpy.app.handlers.depsgraph_update_post.remove(h)

        #frame_change

        if(h==scatter5_frame_pre):
            bpy.app.handlers.frame_change_pre.remove(h)

        if(h==scatter5_frame_post):
            bpy.app.handlers.frame_change_post.remove(h)

        #render 

        if(h==scatter5_render_pre):
            bpy.app.handlers.render_pre.remove(h)

        if(h==scatter5_render_post):
            bpy.app.handlers.render_post.remove(h)

        #blend open 

        if(h==scatter5_load_post):
            bpy.app.handlers.load_post.remove(h)

    return 