

from typing import Set
#import shutil
from pathlib import Path
import subprocess
import importlib
import time
import json

import bpy
from bpy_extras import asset_utils
from bpy.types import Context, Operator
from bpy.props import (
    BoolProperty, 
    EnumProperty, 
    StringProperty, 
    IntProperty)

#from asset_library.constants import (DATA_TYPES, DATA_TYPE_ITEMS, MODULE_DIR)
import asset_library
from asset_library.common.bl_utils import (
    attr_set,
    get_addon_prefs, 
    get_bl_cmd, 
    get_view3d_persp,
    #suitable_areas, 
    refresh_asset_browsers,
    load_datablocks)

from asset_library.common.file_utils import open_blender_file, synchronize
from asset_library.common.functions import get_active_library, asset_warning_callback

from textwrap import dedent
from tempfile import gettempdir
import gpu
from gpu_extras.batch import batch_for_shader
import blf
import bgl


class ASSETLIB_OT_remove_assets(Operator):
    bl_idname = "assetlib.remove_assets"
    bl_options = {"REGISTER", "UNDO", "INTERNAL"}
    bl_label = 'Remove Assets'
    bl_description = 'Remove Selected Assets'

    @classmethod
    def poll(cls, context):
        if not asset_utils.SpaceAssetInfo.is_asset_browser(context.space_data):
            return False

        sp = context.space_data
        if sp.params.asset_library_ref == 'LOCAL':
            return False

        return True

    def execute(self, context: Context) -> Set[str]:
        asset = context.active_file
    
        lib = get_active_library()
        lib_type = lib.library_type

        catalog = lib.read_catalog()

        if not catalog.context.item:
            self.report({'ERROR'}, 'The active asset is not in the catalog')
            return {'CANCELLED'}

        asset_name = context.asset_file_handle.name
        asset_path = lib_type.format_path(asset.asset_data['filepath'])
        asset_catalog = catalog.context.path
        
        img_path = lib_type.get_image_path(name=asset_name, catalog=asset_catalog, filepath=asset_path)
        video_path = lib_type.get_video_path(name=asset_name, catalog=asset_catalog, filepath=asset_path)

        if asset_path and asset_path.exists():
            asset_path.unlink()
        if img_path and img_path.exists():
            img_path.unlink()
        if video_path and video_path.exists():
            video_path.unlink()
        #open_blender_file(filepath)

        try:
            asset_path.parent.rmdir()
        except Exception:#Directory not empty
            pass

        bpy.ops.assetlib.bundle(name=lib.name, blocking=True)

        return {'FINISHED'}


class ASSETLIB_OT_edit_data(Operator):
    bl_idname = "assetlib.edit_data"
    bl_label = "Edit Asset Data"
    bl_description = "Edit Current Asset Data"
    bl_options = {"REGISTER", "UNDO"}

    warning: StringProperty(name='')
    path: StringProperty(name='Path')
    catalog: StringProperty(name='Catalog', update=asset_warning_callback, options={'TEXTEDIT_UPDATE'})
    name: StringProperty(name='Name', update=asset_warning_callback, options={'TEXTEDIT_UPDATE'})
    tags: StringProperty(name='Tags', description='Tags need to separate with a comma (,)')
    description: StringProperty(name='Description')

    @classmethod
    def poll(cls, context):
        if not asset_utils.SpaceAssetInfo.is_asset_browser(context.space_data):
            return False
        return True

    def execute(self, context: Context) -> Set[str]:
        prefs = get_addon_prefs()

        lib = get_active_library()
        if lib.merge_libraries:
            lib = prefs.libraries[lib.store_library]

        new_name = lib.library_type.norm_file_name(self.name)
        new_asset_path = lib.library_type.get_asset_path(name=new_name, catalog=self.catalog)

        #asset_data = lib.library_type.get_asset_data(self.asset)
        asset_data = dict(
            tags=[t.strip() for t in self.tags.split(',') if t],
            description=self.description,
        )

        #lib.library_type.set_asset_catalog(asset, asset_data, catalog_data)
        self.asset.name = self.name
        lib.library_type.set_asset_tags(self.asset, asset_data)
        lib.library_type.set_asset_info(self.asset, asset_data)

        self.old_asset_path.unlink()
        lib.library_type.write_asset(asset=self.asset, asset_path=new_asset_path)

        if self.old_image_path.exists():
            new_img_path = lib.library_type.get_image_path(new_name, self.catalog, new_asset_path)
            self.old_image_path.rename(new_img_path)

        if self.old_video_path.exists():
            new_video_path = lib.library_type.get_video_path(new_name, self.catalog, new_asset_path)
            self.old_video_path.rename(new_video_path)

        #if self.old_description_path.exists():
        #    self.old_description_path.unlink()

        try:
            self.old_asset_path.parent.rmdir()
        except Exception: #The folder is not empty
            pass

        diff_path = Path(bpy.app.tempdir, 'diff.json')
        diff = [dict(name=self.old_asset_name, catalog=self.old_catalog, filepath=str(self.old_asset_path), operation='REMOVE')]

        asset_data = lib.library_type.get_asset_data(self.asset)
        diff += [dict(asset_data,
            image=str(new_img_path),
            filepath=str(new_asset_path),
            type=lib.data_type,
            library_id=lib.id,
            catalog=self.catalog,
            operation='ADD'
            )]

        print(diff)

        diff_path.write_text(json.dumps(diff, indent=4), encoding='utf-8')

        bpy.ops.assetlib.bundle(name=lib.name, diff=str(diff_path), blocking=True)

        return {"FINISHED"}

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

        layout.use_property_split = True

        lib = get_active_library()

        if lib.merge_libraries:
            layout.prop(lib, 'store_library', expand=False)

        layout.prop(self, "catalog", text="Catalog")
        layout.prop(self, "name", text="Name")
        layout.prop(self, 'tags')
        layout.prop(self, 'description')

        #layout.prop()

        layout.separator()
        col = layout.column()
        col.use_property_split = False
        #row.enabled = False

        if self.path:
            col.label(text=self.path)

        if self.warning:
            col.label(icon='ERROR', text=self.warning)

    def invoke(self, context, event):

        lib = get_active_library()

        active_lib = lib.library_type.get_active_asset_library()

        lib.store_library = active_lib.name

        asset_handle = context.asset_file_handle

        catalog_file = lib.library_type.read_catalog()
        catalog_ids = {v['id']: {'path': k, 'name': v['name']} for k,v in catalog_file.items()}

        #asset_handle = context.asset_file_handle
        self.old_asset_name = asset_handle.name
        self.old_asset_path = lib.library_type.get_active_asset_path()
        
        self.asset = load_datablocks(self.old_asset_path, self.old_asset_name, type=lib.data_types)

        if not self.asset:
            self.report({'ERROR'}, 'No asset found')

        self.name = self.old_asset_name
        self.description = asset_handle.asset_data.description

        tags = [t.strip() for t in self.asset.asset_data.tags.keys() if t]
        self.tags = ', '.join(tags)
        #asset_path
        self.old_catalog = catalog_ids[asset_handle.asset_data.catalog_id]['path']
        self.catalog = self.old_catalog

        self.old_image_path = lib.library_type.get_image_path(name=self.name, catalog=self.catalog, filepath=self.old_asset_path)
        self.old_video_path = lib.library_type.get_video_path(name=self.name, catalog=self.catalog, filepath=self.old_asset_path)

        #self.old_description_path = lib.library_type.get_description_path(self.old_asset_path)
        
        #self.old_asset_info = lib.library_type.read_asset_info_file(self.old_asset_path)
        #self.old_asset_info = lib.library_type.norm_asset_datas([self.old_asset_info])[0]





        return context.window_manager.invoke_props_dialog(self, width=450)

    def cancel(self, context):
        print('Cancel Edit Data, removing the asset')

        lib = get_active_library()
        active_lib = lib.library_type.get_active_asset_library()

        getattr(bpy.data, active_lib.data_types).remove(self.asset)

class ASSETLIB_OT_remove_user_library(Operator):
    bl_idname = "assetlib.remove_user_library"
    bl_options = {"REGISTER", "UNDO"}
    bl_label = 'Remove User Library'
    bl_description = 'Remove User Library'

    index : IntProperty(default=-1)

    def execute(self, context: Context) -> Set[str]:
        prefs = get_addon_prefs()

        prefs.user_libraries.remove(self.index)

        return {'FINISHED'}


class ASSETLIB_OT_add_user_library(Operator):
    bl_idname = "assetlib.add_user_library"
    bl_options = {"REGISTER", "UNDO"}
    bl_label = 'Add User Library'
    bl_description = 'Add User Library'


    def execute(self, context: Context) -> Set[str]:
        prefs = get_addon_prefs()

        lib = prefs.user_libraries.add()
        lib.expand = True

        return {'FINISHED'}


class ASSETLIB_OT_open_blend(Operator):
    bl_idname = "assetlib.open_blend"
    bl_options = {"REGISTER", "UNDO"}
    bl_label = 'Open Blender File'
    bl_description = 'Open blender file'

    #filepath : StringProperty(subtype='FILE_PATH') 

    def execute(self, context: Context) -> Set[str]:
        #asset = context.active_file
        #prefs = get_addon_prefs()

        lib = get_active_library()

        #filepath = lib.library_type.format_path(asset.asset_data['filepath'])

        filepath = lib.library_type.get_active_asset_path()

        open_blender_file(filepath)

        return {'FINISHED'}


class ASSETLIB_OT_set_paths(Operator):
    bl_idname = "assetlib.set_paths"
    bl_options = {"REGISTER", "UNDO", "INTERNAL"}
    bl_label = 'Set Paths'
    bl_description = 'Set Library Paths'

    name: StringProperty()
    all: BoolProperty(default=False)

    def execute(self, context: Context) -> Set[str]:
        prefs = get_addon_prefs()
        print('Set Paths')
        if self.all:
            libs = prefs.libraries
        else:
            libs = [prefs.libraries[self.name]]
        
        for lib in libs:
            lib.clear_library_path()
            lib.set_library_path()

        return {'FINISHED'}


class ASSETLIB_OT_bundle_library(Operator):
    bl_idname = "assetlib.bundle"
    bl_options = {"INTERNAL"}
    bl_label = 'Bundle Library'
    bl_description = 'Bundle all matching asset found inside one blend'

    name : StringProperty()
    diff : StringProperty()
    blocking : BoolProperty(default=False)
    mode : EnumProperty(items=[(i.replace(' ', '_').upper(), i, '') for i in ('None', 'All', 'Auto Bundle')], default='NONE')
    directory : StringProperty(subtype='DIR_PATH')
    #conform : BoolProperty(default=False)
    #def refresh(self):
    #    for area in suitable_areas(bpy.context.screen):
    #        bpy.ops.asset.library_refresh({"area": area, 'region': area.regions[3]})
            #space_data.activate_asset_by_id(asset, deferred=deferred)

    def execute(self, context: Context) -> Set[str]:
        prefs = get_addon_prefs()

        libs = []
        if self.name:
            libs += [prefs.libraries[self.name]]

        if self.mode == 'ALL':
            libs += prefs.libraries.values()
        elif self.mode == 'AUTO_BUNDLE':
            libs += [l for l in prefs.libraries if l.auto_bundle]
        
        if not libs:
            return {"CANCELLED"}
                
        lib_datas = [l.to_dict() for l in libs]

        print(f'Bundle Libraries: {[l.name for l in libs]}')

        script_code = dedent(f"""
            import bpy
            prefs = bpy.context.preferences.addons["asset_library"].preferences

            for lib_data in {lib_datas}:
                lib = prefs.env_libraries.add()
                lib.set_dict(lib_data)
                lib.library_type.bundle(cache_diff='{self.diff}')
            
            bpy.ops.wm.quit_blender()
            """)

        script_path = Path(bpy.app.tempdir) / 'bundle_library.py'
        script_path.write_text(script_code)

        print(script_code)

        #raise Exception()

        cmd = get_bl_cmd(script=str(script_path), background=True)

        #print(cmd) 
        if self.blocking:
            subprocess.call(cmd)
            bpy.app.timers.register(refresh_asset_browsers, first_interval=0.2)
        else:
            subprocess.Popen(cmd)
        
        return {'FINISHED'}


class ASSETLIB_OT_reload_addon(Operator):
    bl_idname = "assetlib.reload_addon"
    bl_options = {"UNDO"}
    bl_label = 'Reload Asset Library Addon'
    bl_description = 'Reload The Asset Library Addon and the addapters'

    def execute(self, context: Context) -> Set[str]:

        print('Execute reload')

        asset_library.unregister()
        importlib.reload(asset_library)
        asset_library.register()

        return {'FINISHED'}


class ASSETLIB_OT_diff(Operator):
    bl_idname = "assetlib.diff"
    bl_options = {"REGISTER", "UNDO"}
    bl_label = 'Synchronize'
    bl_description = 'Synchronize Action Lib to Local Directory'

    name : StringProperty()
    conform : BoolProperty(default=False)

    def execute(self, context: Context) -> Set[str]:
        prefs = get_addon_prefs()

        lib = prefs.libraries.get(self.name)
        lib.library_type.diff()

        return {'FINISHED'}

'''
class ASSETLIB_OT_conform_library(Operator):
    bl_idname = "assetlib.conform_library"
    bl_options = {"REGISTER", "UNDO"}
    bl_label = "Conform Library"
    bl_description = "Split each assets per blend and externalize preview"

    name : StringProperty()
    template_image : StringProperty()
    template_video : StringProperty()
    directory : StringProperty(subtype='DIR_PATH', name='Filepath')

    def execute(self, context: Context) -> Set[str]:
        prefs = get_addon_prefs()

        lib = prefs.libraries.get(self.name)
        #lib.library_type.conform(self.directory)

        templates = {}
        if self.template_image:
            templates['image'] = self.template_image
        if self.template_video:
            templates['video'] = self.template_video


        script_path = Path(bpy.app.tempdir) / 'bundle_library.py'
        script_code = dedent(f"""
            import bpy
            prefs = bpy.context.preferences.addons["asset_library"].preferences
            lib = prefs.env_libraries.add()
            lib.set_dict({lib.to_dict()})
            lib.library_type.conform(directory='{self.directory}', templates={templates})
            """)

        script_path.write_text(script_code)

        cmd = get_bl_cmd(script=str(script_path), background=True)

        subprocess.Popen(cmd)

        return {'FINISHED'}
    
    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}
'''

class ASSETLIB_OT_make_custom_preview(Operator):
    bl_idname = "assetlib.make_custom_preview"
    bl_label = "Custom Preview"
    bl_description = "Set a camera to preview an asset"

    image_size : IntProperty(default=512)
    modal : BoolProperty(default=False)

    def modal(self, context, event):
        if event.type in {'ESC'}:  # Cancel
            self.restore()
            return {'CANCELLED'}

        elif event.type in {'RET', 'NUMPAD_ENTER'}:  # Cancel
            return self.execute(context)
            #return {'FINISHED'}

        return {'PASS_THROUGH'}

    def execute(self, context):

        prefs = get_addon_prefs()
        bpy.ops.render.opengl(write_still=True)

        img_path = context.scene.render.filepath

        #print('Load Image to previews')
        prefs.previews.load(Path(img_path).stem, img_path, 'IMAGE')
        #img = bpy.data.images.load(context.scene.render.filepath)
        #img.update()
        #img.preview_ensure()


        #Copy the image with a new name
        # render = bpy.data.images['Render Result']

        # render_pixels = [0] * self.image_size * self.image_size * 4
        # render.pixels.foreach_get(render_pixels)
        # img = bpy.data.images.new(name=img_name, width=self.image_size, height=self.image_size, is_data=True, alpha=True)
        # img.pixels.foreach_set(render_pixels)

        #img.scale(128, 128)
        #img.preview_ensure()

        # preview_size = render.size

        # pixels = [0] * preview_size[0] * preview_size[1] * 4
        # render.pixels.foreach_get(pixels)
        
        # image.preview.image_size = preview_size
        # image.preview.image_pixels_float.foreach_set(pixels)



        self.restore()

        #self.is_running = False
        prefs.preview_modal = False

        return {"FINISHED"}

    def restore(self):
        print('RESTORE')
        try:
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
        except:
            print('Failed remove handler')
            pass

        bpy.data.objects.remove(self.camera)
        self.attr_changed.restore()

    def draw_callback_px(self, context):
        if context.space_data != self._space_data:
            return

        dpi = context.preferences.system.dpi

        bg_color = (0.8, 0.1, 0.1, 0.5)
        font_color = (1, 1, 1, 1)
        text = f'Escape: Cancel Enter: Make Preview'
        font_id = 0
        dim = blf.dimensions(font_id, text)

        #gpu.state.line_width_set(100)
        # bgl.glLineWidth(100)
        # self.shader_2d.bind()
        # self.shader_2d.uniform_float("color", bg_color)
        # self.screen_framing.draw(self.shader_2d)
        
        # # Reset
        # gpu.state.line_width_set(1)

        # -dim[0]/2, +dim[1]/2 + 5

        # Display Text
        blf.color(font_id, *font_color) # unpack color
        blf.position(font_id, context.region.width/2 -dim[0]/2, dim[1]/2 + 5, 0)
        blf.size(font_id, 12, dpi)
        blf.draw(font_id, f'Escape: Cancel Enter: Make Preview')
    
    def get_image_name(self):
        prefs = get_addon_prefs()
        preview_names = [p for p in prefs.previews.keys()]
        preview_names.sort()

        index = 0
        if preview_names:
            index = int(preview_names[-1][-2:]) + 1

        return f'preview_{index:03d}'

    def invoke(self, context, event):
        prefs = get_addon_prefs()
        cam_data = bpy.data.cameras.new(name='Preview Camera')
        self.camera = bpy.data.objects.new(name='Preview Camera', object_data=cam_data)

        #view_3d = get_view3d_persp()

        scn = context.scene
        space = context.space_data

        matrix = space.region_3d.view_matrix.inverted()
        if space.region_3d.view_perspective == 'CAMERA':
            matrix = scn.camera.matrix_world
                        
        self.camera.matrix_world = matrix

        img_name = self.get_image_name()
        img_path = Path(bpy.app.tempdir, img_name).with_suffix('.webp')

        self.attr_changed = attr_set([
            (space.overlay, 'show_overlays', False),
            (space.region_3d, 'view_perspective', 'CAMERA'),
            (space.region_3d, 'view_camera_offset'),
            (space.region_3d, 'view_camera_zoom'),
            (space, 'lock_camera', True),
            (space, 'show_region_ui', False),
            (scn, 'camera', self.camera),
            (scn.render, 'resolution_percentage', 100),
            (scn.render, 'resolution_x', self.image_size),
            (scn.render, 'resolution_y', self.image_size),
            (scn.render, 'film_transparent', True),
            (scn.render.image_settings, 'file_format', 'WEBP'),
            (scn.render.image_settings, 'color_mode', 'RGBA'),
            #(scn.render.image_settings, 'color_depth', '8'),
            (scn.render, 'use_overwrite', True),
            (scn.render, 'filepath', str(img_path)),
        ])

        bpy.ops.view3d.view_center_camera()
        space.region_3d.view_camera_zoom -= 6
        space.region_3d.view_camera_offset[1] += 0.03

        w, h = (context.region.width, context.region.height)

        self._space_data = context.space_data

        if self.modal:
            prefs.preview_modal = True

            self.shader_2d = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
            self.screen_framing = batch_for_shader(
            self.shader_2d, 'LINE_LOOP', {"pos": [(0,0), (0,h), (w,h), (w,0)]})

            self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, (context,), 'WINDOW', 'POST_PIXEL')
            context.window_manager.modal_handler_add(self)

            return {'RUNNING_MODAL'}
        else:
            return self.execute(context)


class ASSETLIB_OT_generate_previews(Operator):
    bl_idname = "assetlib.generate_previews"
    bl_options = {"REGISTER", "UNDO"}
    bl_label = "Generate Previews"
    bl_description = "Generate and write the image for assets"

    cache : StringProperty()
    preview_blend : StringProperty()
    name : StringProperty()
    blocking : BoolProperty(default=True)

    def execute(self, context: Context) -> Set[str]:
        prefs = get_addon_prefs()
        lib = prefs.libraries.get(self.name)
        # self.write_file(self.diff_file, self.diff)

        # preview_assets = [(a.asset_data['filepath'], self.data_types, a.name) for a in assets]

        # self.preview_assets_file.write_text(json.dumps(preview_assets), encoding='utf-8')

        # cmd = [
        #     bpy.app.binary_path, '-b', '--use-system-env',
        #     '--python', str(PREVIEW_ASSETS_SCRIPT), '--',
        #     '--preview-blend', str(self.preview_blend), 
        #     '--preview-assets-file', str(self.preview_assets_file)
        # ]
        # subprocess.call(cmd)
        preview_blend = self.preview_blend or lib.library_type.preview_blend
        
        if not preview_blend or not Path(preview_blend).exists():
            preview_blend = MODULE_DIR / 'common' / 'preview.blend'

        script_path = Path(bpy.app.tempdir) / 'generate_previews.py'
        script_code = dedent(f"""
            import bpy
            prefs = bpy.context.preferences.addons["asset_library"].preferences
            lib = prefs.env_libraries.add()
            lib.set_dict({lib.to_dict()})

            bpy.ops.wm.open_mainfile(filepath='{preview_blend}', load_ui=True)
            lib.library_type.generate_previews(cache='{self.cache}')
            """)

        script_path.write_text(script_code)

        cmd = get_bl_cmd(script=str(script_path), background=True)

        if self.blocking:
            subprocess.call(cmd)
        else:
            subprocess.Popen(cmd)


        return {'FINISHED'}


class ASSETLIB_OT_play_preview(Operator):
    bl_idname = "assetlib.play_preview"
    bl_options = {"REGISTER", "UNDO", "INTERNAL"}
    bl_label = 'Play Preview'
    bl_description = 'Play Preview'

    @classmethod
    def poll(cls, context: Context) -> bool:
        if not context.active_file:
            return False

        if not asset_utils.SpaceAssetInfo.is_asset_browser(context.space_data):
            cls.poll_message_set("Current editor is not an asset browser")
            return False

        lib = get_active_library()
        if not lib:
            return False

        return True

    def execute(self, context: Context) -> Set[str]:
        asset = context.active_file
        prefs = get_addon_prefs()

        lib = get_active_library()

        #filepath = lib.library_type.format_path(asset.asset_data['filepath'])
        asset_path = lib.library_type.get_active_asset_path()

        asset_image = lib.library_type.get_image(asset.name, asset_path)
        asset_video = lib.library_type.get_video(asset.name, asset_path)
        
        if not asset_image and not asset_video:
            self.report({'ERROR'}, f'Preview for {asset.name} not found.')
            return {"CANCELLED"}
        
        if asset_video:
            self.report({'INFO'}, f'Video found. {asset_video}.')

            if prefs.video_player:
                subprocess.Popen([prefs.video_player, asset_video])
            else:
                bpy.ops.wm.path_open(filepath=str(asset_video))
        else:
            self.report({'INFO'}, f'Image found. {asset_image}.')

            if prefs.image_player:
                subprocess.Popen([prefs.image_player, asset_image])
            else:
                bpy.ops.wm.path_open(filepath=str(asset_image))


        return {"FINISHED"}


class ASSETLIB_OT_synchronize(Operator):
    bl_idname = "assetlib.synchronize"
    bl_options = {"REGISTER", "UNDO"}
    bl_label = 'Synchronize'
    bl_description = 'Synchronize Action Lib to Local Directory'

    clean : BoolProperty(default=False)
    only_new : BoolProperty(default=False)
    only_recent : BoolProperty(default=False)
    name: StringProperty()
    all: BoolProperty(default=False)

    def execute(self, context: Context) -> Set[str]:

        print('Not yet Implemented, have to be replace by Bundle instead')
        return {'FINISHED'}

        prefs = get_addon_prefs()
        print('Synchronize')
        if self.all:
            libs = prefs.libraries
        else:
            libs = [prefs.libraries.get(self.name)]

        for lib in libs:            
            if self.clean and Path(lib.path_local).exists():
                pass
                print('To check first')
                #shutil.rmtree(path_local)

            if not lib.path_local:
                continue

            synchronize(
                src=lib.path,
                dst=lib.path_local,
                only_new=self.only_new,
                only_recent=self.only_recent
            )

        return {'FINISHED'}

classes = (
    ASSETLIB_OT_play_preview,
    ASSETLIB_OT_open_blend,
    ASSETLIB_OT_set_paths,
    ASSETLIB_OT_synchronize,
    ASSETLIB_OT_add_user_library,
    ASSETLIB_OT_remove_user_library,
    ASSETLIB_OT_diff,
    ASSETLIB_OT_generate_previews,
    ASSETLIB_OT_bundle_library,
    ASSETLIB_OT_remove_assets,
    ASSETLIB_OT_edit_data,
    #ASSETLIB_OT_conform_library,
    ASSETLIB_OT_reload_addon,
    ASSETLIB_OT_make_custom_preview
)

def register():
    #bpy.types.UserAssetLibrary.is_env = False

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

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