import bpy
from . import fn


class GPEXP_OT_reconnect_render_layer(bpy.types.Operator):
    bl_idname = "gp.reconnect_render_layer"
    bl_label = "Reconnect Render Layer"
    bl_description = "Reconnect selected render layers"
    bl_options = {"REGISTER"}

    @classmethod
    def poll(cls, context):
        return True

    # mode : bpy.props.StringProperty(default='NORMAL', options={'SKIP_SAVE'})

    def execute(self, context):
        node_tree = context.scene.node_tree
        nodes = node_tree.nodes

        changed = []
        for n in nodes:
            if not n.select or not n.type == 'R_LAYERS':
                continue

            if not ' / ' in n.layer:
                continue

            if n.outputs[0].is_linked: # already connected
                continue

             # get namme
            obname = n.layer.split()[0]
            grp_name = f'NG_{obname}'


            # get nodegroup
            grp = nodes.get(grp_name)
            if not grp:
                print(f'{n.name} Node group not found : {n.layer} !-> {grp_name}')
                continue

            inp = grp.inputs.get(n.layer)
            if not inp:
                print(f'{n.name} no inputs name "{n.layer}" in group {grp_name}')
                continue

            # reconnect
            node_tree.links.new(n.outputs[0], inp)
            changed.append(f'{n.name} ({n.layer}) to {grp_name}')

        if changed:
            self.report({'INFO'}, f'{len(changed)} nodes reconnected')
        else:
            self.report({'WARNING'}, f'Could not reconnect, see console')

        return {"FINISHED"}

class GPEXP_OT_delete_render_layer(bpy.types.Operator):
    bl_idname = "gp.delete_render_layer"
    bl_label = "Delete Render Layer"
    bl_description = "Delete selected render layers"
    bl_options = {"REGISTER"}

    @classmethod
    def poll(cls, context):
        return True

    def execute(self, context): 
        ## Compositing scene where nodes lives
        compo_scene = context.scene

        ## render scene where viewlayer lives
        rd_scn = fn.get_render_scene(create=False)
        if not rd_scn:
            self.report({'ERROR'}, 'No render scene found')
            return {'CANCELLED'}

        nodes = compo_scene.node_tree.nodes
        # list layers and viewlayers
        # vls = [rd_scn.view_layers.get(l.viewlayer_render) for l in layers
        #     if l.viewlayer_render and l.viewlayer_render != act.viewlayer_render and rd_scn.view_layers.get(l.viewlayer_render)]

        rlayers_nodes = [n for n in nodes if n.select and n.type == 'R_LAYERS']

        vls = [rd_scn.view_layers.get(n.layer) for n in rlayers_nodes if rd_scn.view_layers.get(n.layer)]

        vl_names = [v.name for v in vls]
        ## disable layers using those VL
        for ob in [o for o in rd_scn.objects if o.type == 'GPENCIL']:
            for l in ob.data.layers:
                if l.viewlayer_render in vl_names:
                    l.viewlayer_render = fn.get_view_layer('exclude').name

        for n in reversed(rlayers_nodes):
            # Disconnect linked
            for lnk in n.outputs[0].links:
                grp = lnk.to_node
                if grp.type != 'GROUP':
                    continue
                if not grp.name.startswith('NG'):
                    continue
                sockin = lnk.to_socket
                sockout = grp.outputs.get(sockin.name)
                if not sockout:
                    continue
                for grplink in sockout.links:
                    if grplink.to_node.type != 'OUTPUT_FILE':
                        continue
                    fo_socket = grplink.to_socket
                    fo = grplink.to_node
                    fo.file_slots.remove(fo_socket)

                inside_nodes = []
                ngroup = grp.node_tree
                
                if bpy.app.version < (4,0,0):
                    for i in range(len(grp.inputs))[::-1]:
                        if grp.inputs[i].name == sockin.name:
                            ngroup.inputs.remove(ngroup.inputs[i])

                            gp_in_socket = ngroup.nodes['Group Input'].outputs[i]
                            for lnk in gp_in_socket.links:
                                inside_nodes += fn.all_connected_forward(lnk.to_node)
                            list(set(inside_nodes))
                            break
                    for i in range(len(grp.outputs))[::-1]:
                        if grp.outputs[i].name == sockout.name:
                            ngroup.outputs.remove(ngroup.outputs[i])
                            break
                else:
                    g_inputs = [s for s in ngroup.interface.items_tree if s.in_out == 'INPUT']
                    g_outputs = [s for s in ngroup.interface.items_tree if s.in_out == 'OUTPUT']
                    for i in range(len(grp.inputs))[::-1]:
                        if grp.inputs[i].name == sockin.name:
                            ngroup.interface.remove(g_inputs[i])

                            gp_in_socket = ngroup.nodes['Group Input'].outputs[i]
                            for lnk in gp_in_socket.links:
                                inside_nodes += fn.all_connected_forward(lnk.to_node)
                            list(set(inside_nodes))
                            break

                    for i in range(len(grp.outputs))[::-1]:
                        if grp.outputs[i].name == sockout.name:
                            ngroup.interface.remove(g_outputs[i])
                            break

                for sub_n in reversed(inside_nodes):
                    ngroup.nodes.remove(sub_n)

            # Remove render_layer node
            nodes.remove(n)

        return {"FINISHED"}


class GPEXP_OT_set_active_fileout_to_compout(bpy.types.Operator):
    bl_idname = "gp.set_active_fileout_to_compout"
    bl_label = "Set Active File Output To Composite"
    bl_description = "Use active slot of active file output node to set scene output settings (swap connection)"
    bl_options = {"REGISTER"}

    @classmethod
    def poll(cls, context):
        return context.scene.use_nodes\
            and context.scene.node_tree\
            and context.scene.node_tree.nodes.active\
                and context.scene.node_tree.nodes.active.type == 'OUTPUT_FILE'

    relink_composite : bpy.props.BoolProperty(
        name='Relink Composite',
        default=True,
        description='In case file slot is linked, swap link to Composite file',
        options={'SKIP_SAVE'},
        )

    def invoke(self, context, event):
        self.fo = context.scene.node_tree.nodes.active
        if not len(self.fo.file_slots):
            self.report({'ERROR'}, 'no slots in active file output')
            return {'CANCELLED'}

        # check if active slot has a source
        if not self.fo.inputs[self.fo.active_input_index].is_linked:
            return self.execute(context)

        # check if composite linked
        out = context.scene.node_tree.nodes.get('Composite')
        if not out or not out.inputs[0].is_linked:
            self.compo_out_from_link = ''
            return self.execute(context)

        # compo linked, pop panel to choose replace or not
        self.compo_out_from_link = out.inputs[0].links[0].from_node.name
        return context.window_manager.invoke_props_dialog(self)


    def draw(self, context):
        layout = self.layout
        col = layout.column()
        col.label(text=f'Composite node connected to: {self.compo_out_from_link}')
        col.label(text=f'Would you like to replace by file output slot source ?')
        layout.prop(self, 'relink_composite')

    def execute(self, context):
        # if comp
        fn.set_scene_output_from_active_fileout_item()
        idx = self.fo.active_input_index
        sl = self.fo.file_slots[idx]
        sk = self.fo.inputs[idx]

        if not sk.is_linked:
            self.report({'INFO'}, f'Outut changed to match {sl.path} (slot was not linked)')
            return {'FINISHED'}

        ## If linked replace links to Composite node
        if not self.relink_composite:
            return {'FINISHED'}

        ntree = context.scene.node_tree
        links = context.scene.node_tree.links
        nodes = context.scene.node_tree.nodes

        out = nodes.get('Composite')
        if not out:
            out = fn.create_node('COMPOSITE', tree=ntree)
        fo_loc = fn.real_loc(self.fo)
        out.location = (fo_loc.x, fo_loc.y + 160)

        # if out.inputs[0].is_linked:
        #     self.report({'WARNING'}, f'Outut changed to match {sl.path} (Composite node already linked)')

        lnk = sk.links[0]
        from_sk = sk.links[0].from_socket
        links.remove(lnk)
        links.new(from_sk, out.inputs[0])

        return {"FINISHED"}



classes=(
GPEXP_OT_reconnect_render_layer,
GPEXP_OT_delete_render_layer,
GPEXP_OT_set_active_fileout_to_compout,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)

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