bl_info = {
    "name": "Render Settings Sets",
    "author": "Gregory Smirnoff",
    "version": (0, 0, 9),
    "blender": (2, 80, 0),
    "location": "Properties Panel > Render Sets",
    "description": "",
    "warning": "",
    "wiki_url": "",
    "category": "Render",
}


import bpy
from bpy_extras.io_utils import ImportHelper
from bpy.app.handlers import persistent
from bpy.types import Operator, AddonPreferences, PropertyGroup, UIList, Menu, Panel
from bpy.props import StringProperty, IntProperty, FloatProperty, FloatVectorProperty, BoolProperty, CollectionProperty
import subprocess
import os

### Preferences

#Base Functions

def home_path(): 
    '''Домашний каталог'''
    from pathlib import Path  
    home = str(Path.home()) 
    return home

def json_wrire(data, path, mode='w'):
    import json
    with open(path, mode) as file:
        json.dump(data, file, indent=3)  
        
def json_read(path):
    import json
    with open(path, 'r') as file:
        file_data = json.load(file)
    return file_data

#Main Functions

def write_render_set_json(name_set, scene, render, eevee, wb_d, wb_ds, cycles, jpath=''):
    
    import os.path
    from time import gmtime, strftime
    import bpy
    
    data = {
        'bpy.context.window_manager.rss_list':name_set,
        'bpy.context.scene':scene, #Filter Data
        'bpy.context.scene.render':render, #Filder Data 
        'bpy.context.scene.eevee':eevee,
        'bpy.context.scene.display':wb_d,
        'bpy.context.scene.display.shading':wb_ds,
        'bpy.context.scene.cycles':cycles                 
    }
    

    if not jpath:
        filename = strftime("rsets_%Y-%m-%d-%H-%M-%S", gmtime())+'.json'
    else: filename = jpath 
    
    
    #_Filter Using Data__________________________________________________________
    
    '''
    
    if not bpy.context.window_manager.rss_load_render_data:
         data['bpy.context.scene.render'] = scene
                
    if not bpy.context.window_manager.rss_load_scene_data:
         data['bpy.context.scene'] = render     
    
    
    ls = bpy.context.window_manager.rss_list
    index = bpy.context.window_manager.rss_list_index
    
    if len(ls):
                      
        json_disk_file = ls[index].json_path    
        
        
        if os.path.exists(json_disk_file):
            load_data = json_read(json_disk_file)
                   
            if bpy.context.window_manager.rss_load_render_data:
                data['bpy.context.scene.render'] = load_data['bpy.context.scene.render']
                
            if bpy.context.window_manager.rss_load_scene_data:
                data['bpy.context.scene'] = load_data['bpy.context.scene']
    '''            
               
             
    #____________________________________________________________________________         
    
    path = os.path.join( home_path(), '.rsets')

    if not os.path.exists(path): os.makedirs(path)    
    
    path = os.path.join( path, filename)
    
    json_wrire(data, path, mode='w')
    
    return path

def set_render_set_json(path):
    
    data = json_read(path)
    
    render = data['bpy.context.scene.render']
    
    keys = [
        'bpy.context.window_manager.rss_list',
        'bpy.context.scene',
        'bpy.context.scene.render', 
        'bpy.context.scene.eevee',
        'bpy.context.scene.display',
        'bpy.context.scene.display.shading',
        'bpy.context.scene.cycles'
    ]
    
    missing_attributes = []
    
    for key in keys:
        
        
        if key == 'bpy.context.window_manager.rss_list':
            ls = bpy.context.window_manager.rss_list
            index = bpy.context.window_manager.rss_list_index
            value = data.get('bpy.context.window_manager.rss_list')
            if value:  
                ls[index].set_name = value 
            else:
                ls[index].set_name = '<Empty name>'
                
                
            '''    
            #_Filter Using Data__________________________________________________________
            
            if key=='bpy.context.scene.render':       
                if not bpy.context.window_manager.rss_load_render_data:
                    continue
             
            if key=='bpy.context.scene':    
                if not bpy.context.window_manager.rss_load_scene_data:
                    continue                     
            #____________________________________________________________________________ 
            '''
                              
                                    
         
        for attr in data[key]:
                 
            if hasattr(eval(key), attr):
                
                value = data[key].get(attr)
                
                try:
                    setattr(eval(key), attr, value)
                    print(attr,':',value)
                    
                except Exception as e: print(e)  
            else:
               missing_attributes.append(attr)
            
    print('Missing attributes ',len(missing_attributes))                
    #setattr(bpy.context.scene.eevee, 'use_gtao', False)   
    

def get_cls_attrs(cls):

    import inspect
    
    d = {}

    for a in inspect.getmembers(cls):
         
        # to remove private and protected
        # functions
        if not a[0].startswith('_'):
             
            # To remove other methods that
            # doesnot start with a underscore
            if not inspect.ismethod(a[1]):
                
                s = isinstance(a[1], str)
                f = isinstance(a[1], float)
                i = isinstance(a[1], int)
                b = isinstance(a[1], bool)
                c = (str(type(a[1])) == "<class 'Color'>")
                
                value = a[1]
                
                if c: value = list(a[1])
                
                if s or f or i or b or c:
                    #print(a)   
                    d[a[0]] = value                
                    
    return d
 
        
### UI


class RSS_PT_MAIN(Panel):
    bl_label = "Render Sets"
    bl_order = 21
    bl_idname = "RSS_PT_MAINPANEL"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "render"
    bl_options = {'DEFAULT_CLOSED'}

    def draw(self, context):
        
        layout = self.layout
        
        wm = context.window_manager
        
        main = layout.row()
        
        ul_list = main.column()
        ul_list_opers = main.column()
     
        ul_list.template_list("RENDER_SETS_UL_LIST", "", wm, "rss_list", wm, "rss_list_index", rows=6)
        
        if len(context.window_manager.rss_list):
            
            ls = context.window_manager.rss_list
            lsi = context.window_manager.rss_list_index
                     
            ul_list.separator()             
            ul_list.label(text='File: '+ls[lsi].json_path, icon='FILE')
            
       
        
        ul_list_opers.operator('rss.add', text='', icon = 'ADD')      
        ul_list_opers.operator('rss.remove',text='', icon = 'REMOVE')
        
        ul_list_opers.separator()
             
        ul_list_opers.operator("rss.move", text='', icon="TRIA_UP").direction = 'UP'
        ul_list_opers.operator("rss.move",text='', icon="TRIA_DOWN").direction = 'DOWN'  
        
        ul_list_opers.separator() 
        
        #ul_list_opers.popover(panel="RSS_PT_filter_popover",icon='FILTER',text="")
        
        ul_list_opers.separator()              
        
        ul_list_opers.menu("RSS_MT_context_menu", icon='PREFERENCES', text="")       
         
        '''if not context.window_manager.rss_autoset_selection:  
            ul_list_opers.separator()       
            ul_list_opers.operator('rss.apply',text='', icon = 'ANIM_DATA')'''                       
        
        
        
class RSS_MT_context_menu(Menu):
    bl_label = "PSS Menu"

    def draw(self, context):
        layout = self.layout

        #layout.label(text='Menu')
        layout.operator(RSS_OP_Load_JSON_Filebrowser.bl_idname, text="Import JSON Set", icon='FILE')
        layout.separator()
        #layout.prop(context.window_manager, "rss_autoset_selection", text="Autoset settings", emboss=False) 
        layout.prop(context.window_manager, "rss_remove_json", text="Remove JSON With Set", emboss=False) 
 
        
class RSS_PT_filter_popover(Panel):
    bl_label = "Save/Load Data Filters"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW' 
    #bl_parent_id = 'RSS_PT_MAINPANEL'
    bl_options = {'INSTANCED'}

    def draw(self, context):
        
        layout = self.layout    
        wm = context.window_manager

        ui = layout.column()
        
        row1 = ui.row()
        row2 = ui.column(align=True)
        
        row1.label(text='Using Data Filters')
        row2.prop(wm, "rss_load_render_data", text="Use Render Data", icon='OUTPUT')        
        row2.prop(wm, "rss_load_scene_data", text="Use Scene Data", icon='SCENE_DATA')        

        
class RSS_OP_Load_JSON_Filebrowser(Operator, ImportHelper):

    bl_idname = "rss.load_json"
    bl_label = "Load JSON Set From Disk"
    
    filter_glob: StringProperty(
        default='*.json',
        options={'HIDDEN'}
    )
    

    def execute(self, context):
        
        try:
            p = context.window_manager
            item = p.rss_list.add()
            
            p.rss_list_index = (len(p.rss_list)-1)        
            
            print('Selected file:', self.filepath)
            
            path = self.filepath
            
            p.rss_list[p.rss_list_index].json_path=self.filepath
            
            set_render_set_json(path)
            
        except Exception as e: 
                print('Error: ', e)   
        
        return {'FINISHED'}                         


class RSS_OP_ADD(Operator):
    bl_label = "Add Set"
    bl_description = 'Save Current Render Settings To Set And JSON File In Home Directory .rsets'
    bl_idname = 'rss.add'
    
    def execute(self, context): 
         
        p = context.window_manager
        item = p.rss_list.add()
        
        p.rss_list_index = (len(p.rss_list)-1)         
        
        item.set_name = 'Render_Settings_'+str(len(context.window_manager.rss_list))
        
        ls = context.window_manager.rss_list
        index = context.window_manager.rss_list_index
                  
        name_set = ls[index].set_name
        scene = get_cls_attrs(bpy.context.scene)
        cycles_sets = get_cls_attrs(bpy.context.scene.cycles)
        eevee_sets = get_cls_attrs(bpy.context.scene.eevee)
        wb_sets_a = get_cls_attrs(bpy.context.scene.display)
        wb_sets_b = get_cls_attrs(bpy.context.scene.display.shading)
        scene_render_sets = get_cls_attrs(bpy.context.scene.render) 

        path = write_render_set_json(name_set, scene, scene_render_sets, eevee_sets, wb_sets_a, wb_sets_b, cycles_sets)         
        
        item.json_path = path
                    
        return {"FINISHED"}
    
class RSS_OP_MOVE(Operator):
    
    bl_idname = "rss.move"
    bl_label = ""
    bl_description = "Move selected set up or down"

    direction:bpy.props.EnumProperty(items=( \
        ('UP', "Up", "Move up"), \
        ('DOWN', "Down", "Move down"))
    )
    

    def execute(self, context):
        length = len(context.window_manager.rss_list)
        if length > 1:
            c = context.window_manager
            d = -1 if self.direction == 'UP' else 1
            new_index = (c.rss_list_index + d) % len(c.rss_list)
            c.rss_list.move(c.rss_list_index, new_index)
            c.rss_list_index = new_index
            
        return {'FINISHED'}     


class RSS_OP_REMOVE(Operator):
    bl_label = "Remove Set"
    bl_description = 'Remome Set And JSON File From Home Directory .rsets'
    bl_idname = 'rss.remove'
    
    def execute(self, context):
        
        ls = context.window_manager.rss_list
        lsi = context.window_manager.rss_list_index
        
        if len(ls):
            filepath = ls[lsi].json_path         
            try:  
                if context.window_manager.rss_remove_json:     
                    os.remove(filepath)
            except Exception as e: 
                print('Error: ', e)
                self.report({'INFO'}, "File JSON Not Found")           
        
        p = context.window_manager
        p.rss_list.remove(p.rss_list_index)
        if p.rss_list_index == 0:
            p.rss_list_index -= 1
            
        if p.rss_list_index > (len(ls)-1):
            p.rss_list_index = (len(ls)-1)                      
        

        return {"FINISHED"}

  
class RSS_OP_APPLY(Operator):
    bl_label = "Apply To Scene"
    bl_description = 'Apply Selected Set'
    bl_idname = 'rss.apply'
    
    def execute(self, context):
        
        l = len(context.window_manager.rss_list)
        
        if l:
            ls = context.window_manager.rss_list
            lsi = context.window_manager.rss_list_index
            filepath = ls[lsi].json_path 
            
            try:
                       
                set_render_set_json(filepath)
                                
            except Exception as e: 
                print('Error: ',e)
                self.report({'INFO'}, "File JSON Not Found")        
        
        if not l: self.report({'INFO'}, "Sets is empty")    
        
        
        return {"FINISHED"}       
    
    

####_UI_List________________________###############################

class RenderSetsItem(PropertyGroup):

    set_name : StringProperty()
    json_path : StringProperty(options={'HIDDEN'})
    
    
#______________________________________________________________________________________________________________________________

class RENDER_SETS_UL_LIST(UIList):
    
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):

        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            
                if index!=context.window_manager.rss_list_index:
                    layout.prop(item, "set_name", text="", emboss=False, icon='DOT') #, icon="DOT"
                
                if index==context.window_manager.rss_list_index:
                    layout.prop(item, "set_name", text="", emboss=False, icon='ANIM') #, icon="ANIM"
                    
        elif self.layout_type in {'GRID'}:
            pass 
                       

#______________________________________________________________________________________________________________________________    

classes = [
    RSS_PT_MAIN,
    RSS_MT_context_menu,
    RSS_PT_filter_popover,
    RSS_OP_Load_JSON_Filebrowser,
    RSS_OP_ADD,
    RSS_OP_MOVE,
    RSS_OP_REMOVE,   
    RSS_OP_APPLY,
    RenderSetsItem,
    RENDER_SETS_UL_LIST, 
]  


def rss_update_func(self, context):
    #if context.window_manager.rss_autoset_selection:
    bpy.ops.rss.apply()



@persistent        
def handler_update_ui_event(dummy):
    
    ls = bpy.context.window_manager.rss_list
    index = bpy.context.window_manager.rss_list_index
           
    if len(ls):
        #if bpy.context.window_manager.rss_autoset_selection:                
        filepath = ls[index].json_path
        
        #try: set_render_set_json(filepath)         
        #except Exception as e: print('Error: ',e)        
            
        name_set = ls[index].set_name
        scene = get_cls_attrs(bpy.context.scene)
        cycles_sets = get_cls_attrs(bpy.context.scene.cycles)
        eevee_sets = get_cls_attrs(bpy.context.scene.eevee)
        wb_sets_a = get_cls_attrs(bpy.context.scene.display)
        wb_sets_b = get_cls_attrs(bpy.context.scene.display.shading)
        scene_render_sets = get_cls_attrs(bpy.context.scene.render) 

        write_render_set_json(name_set, scene, scene_render_sets, eevee_sets, wb_sets_a, wb_sets_b, cycles_sets, filepath) 
                  
        
     
        
def register():
    
    for cls in classes:
        bpy.utils.register_class(cls)
    
    bpy.types.WindowManager.rss_list = CollectionProperty(type=RenderSetsItem)
    bpy.types.WindowManager.rss_list_index = IntProperty(min=0, update=rss_update_func)    
    bpy.types.WindowManager.rss_load_render_data = BoolProperty(default=True, description = 'Load Render Data From JSON File')    
    bpy.types.WindowManager.rss_load_scene_data = BoolProperty(default=False, description = 'Load Scene Data From JSON File')    
    #bpy.types.WindowManager.rss_autoset_selection = BoolProperty(default=True)    
    bpy.types.WindowManager.rss_remove_json = BoolProperty(default=True)
    
    bpy.app.handlers.depsgraph_update_pre.append(handler_update_ui_event)    

def unregister():
    
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)

    del bpy.types.WindowManager.rss_list
    del bpy.types.WindowManager.rss_list_index
    del bpy.types.WindowManager.rss_load_scene_data
    #del bpy.types.WindowManager.rss_autoset_selection    
    del bpy.types.WindowManager.rss_remove_json
    
    bpy.app.handlers.depsgraph_update_pre.remove(handler_update_ui_event)

if __name__ == "__main__":
    register()        