import bpy
import os
from pathlib import Path
import re
from time import time


def renumber_sequence_on_disk_from_file_slots(apply=True, active_scene_only=False):
    '''renumber sequence on disk from scenes file slots'''

    scn = bpy.context.scene
    blend = Path(bpy.data.filepath)
    render = blend.parent / 'render'

    prenum = re.compile(r'\d{3}_')

    print('-- starting rename sequences numbers from fileslots number')
    if not apply:
        print('-- Dry run')

    t0 = time()
    ct = 0

    if active_scene_only:
        # Only on currrent scene
        file_outs = [n for n in bpy.context.scene.node_tree.nodes if n.type == 'OUTPUT_FILE' and n.name.startswith('OUT_') and not n.mute]
    else:
        # multi scene check:
        file_outs = []
        for S in bpy.data.scenes:
            if not S.node_tree or not S.use_nodes: # S.name == 'Scene' or 
                continue
            file_outs += [n for n in S.node_tree.nodes if n.type == 'OUTPUT_FILE' and n.name.startswith('OUT_') and not n.mute]


    if not file_outs:
        return 'No file output found (should be unmuted nodes with name starting with OUT_)', '_'

    for fo in file_outs:
        obj_full = fo.base_path.split('/')[-1]
        obj = prenum.sub('', obj_full)
        obj_num = prenum.search(obj_full)
        if obj_num:
            obj_num = obj_num.group(0)

        ## check if folder exists
        folder_path = None

        for d in os.scandir(render):
            if d.is_dir() and prenum.sub('', d.name) == obj:
                folder_path = render / d.name
                break

        if not folder_path:
            print(f'Could not find obj folder for: {obj}')
            continue

        # rename inside folder dirst so that root path isn't changed while iterating
        for fs in fo.file_slots:
            img_full = fs.path.split('/')[0]
            img = prenum.sub('', img_full)
            img_num = prenum.search(img_full)
            if img_num:
                img_num = img_num.group(0)
            else:
                print(f'! no num : {fo.base_path} : {img_full}')
                continue # If no img_num no point in renaming sequences

            img_dir_path = None

            for img_dir in os.scandir(folder_path):
                if img_dir.is_dir() and prenum.sub('', img_dir.name) == img:
                    img_dir_path = folder_path / img_dir.name
                    break

            if not img_dir_path:
                print(f'Could not find img folder for: {img}')
                continue

            # if folder exists check if full name is ok
            if img_full == img_dir_path.name:
                continue # name already (maybe not in sequence but should be good)


            # rename sequence and image folder
            for frame in os.scandir(img_dir_path):
                good = img_num + prenum.sub('', frame.name)
                if frame.name != good:
                    print(f'  img: {frame.name} > {good}')
                    ct += 1
                    if apply:
                        fp = Path(frame.path)
                        fp.rename(fp.parent / good)

            # rename image folder
            if img_dir_path.name != img_full:
                print(f' dir:{img_dir_path.name} > {img_full}')
                ct += 1
                if apply:
                    img_dir_path.rename(img_dir_path.parent / img_full)

        # rename object folder
        if obj_num and folder_path.name != obj_full:
            print(f'obj: {folder_path.name} > {obj_full}')
            ct += 1
            if apply:
                folder_path.rename(folder_path.parent / obj_full)

    elapsed = f'{time() - t0:.2f}s'
    print(f'Eslapsed time: {elapsed}')
    return ct, elapsed


class GPEXP_OT_renumber_files_on_disk(bpy.types.Operator):
    bl_idname = "gp.renumber_files_on_disk"
    bl_label = "Renumber Files On Disk"
    bl_description = "Rename folder/files in render folder on disk according to unmuted file output numbering"
    bl_options = {"REGISTER"}

    def invoke(self, context, event):
        # return self.execute(context)
        return context.window_manager.invoke_props_dialog(self)

    dry_run: bpy.props.BoolProperty(name='Dry-run (no actions, prints in console only)',
        default=False,
        description='Test mode. If checked, no action is actually performed')

    active_scene_only: bpy.props.BoolProperty(name='Only Active Scene',
        default=False,
        description='use only file output of active scene instead of all scenes (skipping "Scene")')

    def draw(self, context):
        layout = self.layout
        layout.prop(self, 'dry_run')
        layout.prop(self, 'active_scene_only')

    def execute(self, context):
        ct, timing = renumber_sequence_on_disk_from_file_slots(apply = not self.dry_run, active_scene_only=self.active_scene_only)
        if isinstance(ct, str):
            self.report({'ERROR'}, ct)
            return {"CANCELLED"}

        if not ct:
            self.report({'WARNING'}, 'Already good or nothing to rename')
            return {"CANCELLED"}

        if self.dry_run:
            mess = f'Dry run : {ct} items would have been renamed, see console'
        else:
            mess = f'{ct} items renamed in {timing}'

        self.report({'INFO'}, mess)
        return {"FINISHED"}



classes=(
GPEXP_OT_renumber_files_on_disk,
)

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

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