# Copyright (C) 2022 Thomas Hoppe (h0bB1T). All rights reserved.
#
# Unauthorized copying of this file via any medium is strictly prohibited.
# Proprietary and confidential.

import bpy, math

from bpy.types import AddonPreferences, UILayout
from bpy.props import StringProperty, IntProperty, BoolProperty, EnumProperty, FloatProperty

from typing import List, Tuple

from .constants import texture_pack_options
from .properties import Properties
from .registries.key_registry import KeyRegistry
from .registries.config_registry import ConfigRegistry
from .operators.tag_manager import ASSET_OT_tag_manager
from .headers.view3d_header import set_view3d_header


class PreferencesPanel(AddonPreferences):
    bl_idname = __package__

    # Preview rendering

    render_engine: EnumProperty(
        name='Preview Renderer', 
        default='CYCLES', 
        items=[
            ( 'CYCLES', 'Cycles', 'Cycles'),
            ( 'BLENDER_EEVEE', 'Eevee', 'Eevee' ),
        ]
    ) # type: ignore
    cycles_samples: IntProperty(name='Number of Samples', default=10, min=1, max=1024) # type: ignore
    cycles_device: EnumProperty(
        name='Preview Render Device',
        default='GPU',
        items=(
            ( 'CPU', 'CPU', 'CPU' ),
            ( 'GPU', 'GPU', 'GPU' ),
        )
    ) # type: ignore
    cycles_denoiser: EnumProperty(
        name='Preview Denoiser',
        default='OPTIX',
        items=(
            ( 'NONE', 'No Denoiser', 'Do not denoise image (fallback, use only for good reason)'),
            ( 'OPTIX', 'Optix', 'Use NVIDIA Optix denoiser' ),
            ( 'OPENIMAGEDENOISE', 'OpenImageDenoise', 'Use OpenImageDenoise' ),
        )
    ) # type: ignore
    preview_image_format: EnumProperty(
        name='Preview Image Format',
        default='PNG',
        items=(
            ( 'PNG', 'PNG', 'Portable Network Graphics Format'),
            ( 'JPEG2000', 'JPEG 2000', 'JPEG 2000 Format' ),
        )
    ) # type: ignore
    dimension: IntProperty(name='Preview Render Size', default=256) # type: ignore

    # Export settings.

    relative_paths: BoolProperty(
        name='Relative Paths',
        description='If enabled, Paths to external data (images, volumes) is relative, not absolute',
        default=True
    ) # type: ignore
    auto_place: BoolProperty(name='Autoplace Objects in Grid', default=True) # type: ignore
    author: StringProperty(name='Default Author') # type: ignore
    texture_pack_mode: EnumProperty(
        name='Texture pack mode',
        description='Select style of texture packing',
        items=texture_pack_options
    ) # type: ignore

    # Tag management

    new_tag: StringProperty() # type: ignore
    new_shader_tag: StringProperty() # type: ignore
    new_geometry_tag: StringProperty() # type: ignore

    # O + M cleanup

    oc_remove_animation_data: BoolProperty(name='Remove animation Data', description='Remove animation Data from selected Meshes', default=False) # type: ignore
    oc_unparent: BoolProperty(name='Remove parent Empties', description='Remove all Parent Empties, leaving Transformation (Clear animation Data may be necessary)', default=True) # type: ignore
    oc_merge_objects: BoolProperty(name='Merge Objects', description='Merge Meshes in selected Hierarchy', default=False) # type: ignore

    mc_clear_custom_split_normals: BoolProperty(name='Clear custom split Normals', description='Clear these on all selected Meshes', default=True) # type: ignore
    mc_clear_sharp: BoolProperty(name='Clear Sharp', description='Clear sharp edges on all selected Meshes', default=True) # type: ignore
    mc_tris_to_quads: BoolProperty(name='Tris to Quads', description='Convert mesh triangles to quads', default=True) # type: ignore
    mc_set_auto_smooth: BoolProperty(name='Enabled Auto Smooth', description='Enable Auto Smooth and set Angle', default=True) # type: ignore
    mc_recalculate_normals_outside: BoolProperty(name='Recalculate Normals', description='Recalculate Normals to Outside on all selected Meshes', default=True) # type: ignore
    mc_join_vertices: BoolProperty(name='Join Vertices', description='Join Vertices close together, which isn\'t needed in Blender, but some 3D Formats', default=True) # type: ignore
    mc_limited_dissolve: BoolProperty(name='Limited Dissolve', description='Delete all unnesseary Edges and Vertices to get large N-Gons', default=True) # type: ignore
    mc_auto_smooth_angle: FloatProperty(name='Auto Smooth Angle', default=math.radians(30), subtype='ANGLE', precision=2) # type: ignore
    mc_join_vertices_distance: FloatProperty(name='Max Distance', description='Maximum Distance of Vertices when joining', default=0.0001, precision=5) # type: ignore
    mc_limited_dissolve_angle: FloatProperty(name='Dissolve Max Angle', description='Maximum Angle for Limited Dissolve', default=math.radians(0.5), subtype='ANGLE', precision=2) # type: ignore

    place_quick: BoolProperty(name='Quick Transform', description='Quick place Mode', default=True) # type: ignore
    place_linked_copy: BoolProperty(name='Linked Copy', description='Create a linked Copy', default=True) # type: ignore
    place_auto_parent: BoolProperty(name='Auto Parent', description='Automatically set Target Object as Parent', default=False) # type: ignore

    # UI settings

    prefer_snw: BoolProperty(
        name='Prefer SNW Pie-Menu', 
        description='If you\'ve installed and enabled SNW, Asset Wizard Pro will invoke' + 
            ' SNW\'s Pie Menu in Shader Node Editor. Useful when using the same hotkey (first Addon wins in this case)',
        default=True
    ) # type: ignore

    view3d_category_mode: EnumProperty(
        name='Asset Import Mode',
        description='Mode of asset listing in pie menu',
        default='CATALOG',
        items=(
            ( 'CATALOG', 'Catalog', 'Use catalog hierarchy' ),
            ( 'CATEGORY', 'Category index', 'Show categories, sub pie with items from this category' ),
            ( 'TOC', 'Table of contents', 'Show categories and node groups with starting letter, sub pie with items from this category with given letter' ),
        )
    ) # type: ignore

    view3d_preview_scale: FloatProperty(name='Asset Preview Size', default=8.0, min=1.0, max=15.0) # type: ignore
    view3d_preview_scale_popup: FloatProperty(name='Asset Preview Popup Size', default=6.0, min=1.0, max=15.0) # type: ignore

    pie_node_group_mode: EnumProperty(
        name='Import Node Group Mode',
        description='Mode of node group listing in pie menu',
        default='CATEGORY',
        items=(
            ( 'CATALOG', 'Catalog', 'Use catalog hierarchy' ),
            ( 'CATEGORY', 'Category index', 'Show categories, sub pie with items from this category' ),
            ( 'TOC', 'Table of contents', 'Show categories and node groups with starting letter, sub pie with items from this category with given letter' ),
        )
    ) # type: ignore

    hide_placer_panel_by_default: BoolProperty(
        name='Hide Placer Panel by default',
        description='Whether the info/help overlay Panel is initiallly shown when starting Placer',
        default=False
    ) # type: ignore

    # Import from library to scene.

    import_relative_paths: BoolProperty(
        name='Import relative',
        description='When importing Objects linked, whether use relative Paths instead of absolute',
        default=False
    ) # type: ignore

    # Experimental settings.

    experimental_inline_import: BoolProperty(
        name='Inline import Object*',
        description='Import Assets into Scene from 3DView Header Icon and via Ctrl-E',
        default=False,
        update=lambda self, _: self.toggle_experimental()
    ) # type: ignore


    def toggle_experimental(self):
        set_view3d_header(self.experimental_inline_import)


    def __draw_tag_controls(self, g: UILayout, name: str, tags: List[Tuple[str, str, str]], prp: str, a_mode: str, r_mode: str):
        r = g.column(align=True)
        r.label(text=f'{name}:')
        s = r.row(align=True)
        s.prop(self, prp, text='')
        ASSET_OT_tag_manager.create_ui(s, 'ADD', True, a_mode, '', getattr(self, prp))
        for t, _, __ in tags:
            s = r.row(align=True)
            s.label(text=t)
            ASSET_OT_tag_manager.create_ui(s, 'REMOVE', True, r_mode, '', t)


    def draw(self, context: bpy.types.Context):
        l = self.layout # type: bpy.types.UILayout
        c = l.column(align=False)
        c.use_property_decorate = True
        c.use_property_split = False

        b = c.box().column(align=True)
        b.use_property_split = True
        b.label(text='Preview Render Options:')
        b.separator()
        b.row(align=True).prop(self, 'render_engine', expand=True)
        if self.render_engine == 'CYCLES':
            b.prop(self, 'cycles_samples')
            b.row(align=True).prop(self, 'cycles_device', expand=True)
            b.prop(self, 'cycles_denoiser')
        b.row(align=True).prop(self, 'preview_image_format', expand=True)
        b.prop(self, 'dimension')

        b = c.box().column(align=True)
        b.use_property_split = True
        b.label(text='UI Settings:')
        b.separator()
        b.prop(self, 'prefer_snw', toggle=True, icon='NODE')
        b.prop(self, 'view3d_preview_scale')
        b.prop(self, 'view3d_preview_scale_popup')
        b.prop(self, 'experimental_inline_import', toggle=True, icon='VIEW3D')
        b.prop(self, 'hide_placer_panel_by_default', toggle=True, icon='HELP')

        b = c.box().column(align=True)
        b.use_property_split = True
        b.label(text='Hotkeys and Settings:')
        b.separator()
        KeyRegistry.instance().render_prefs(b)
        b.separator()

        b = c.box().column(align=True)
        b.use_property_split = True
        b.label(text='Export Options:')
        b.separator()
        b.prop(self, 'relative_paths', icon='OUTLINER', toggle=True)
        b.prop(self, 'auto_place', icon='VIEW_ORTHO', toggle=True)
        b.prop(self, 'author')
        b.prop(self, 'texture_pack_mode')

        b = c.box().column(align=True)
        b.use_property_split = True
        b.label(text='Import Options:')
        b.separator() 
        b.row(align=True).prop(self, 'view3d_category_mode', expand=True)
        b.row(align=True).prop(self, 'pie_node_group_mode', expand=True)
        b.prop(self, 'import_relative_paths', icon='OUTLINER', toggle=True)

        b = c.box().column(align=True)
        b.use_property_split = True
        b.label(text='Preferred Tool Panel Options:')
        b.separator()
        sp = b.split(factor=0.5, align=True)
        lc = sp.column(align=True)

        lc.label(text='Object Cleanup:')
        lc.prop(self, 'oc_remove_animation_data')
        lc.prop(self, 'oc_unparent')
        lc.prop(self, 'oc_merge_objects')

        lc.separator()

        lc.label(text='Place Object:')
        lc.prop(self, 'place_quick')
        lc.prop(self, 'place_linked_copy')
        lc.prop(self, 'place_auto_parent')

        rc = sp.column(align=True)

        rc.label(text='Mesh Cleanup:')
        rc.prop(self, 'mc_clear_custom_split_normals')
        rc.prop(self, 'mc_clear_sharp')
        rc.prop(self, 'mc_set_auto_smooth')
        rc.prop(self, 'mc_auto_smooth_angle')
        rc.prop(self, 'mc_recalculate_normals_outside')
        rc.prop(self, 'mc_join_vertices')
        rc.prop(self, 'mc_join_vertices_distance')
        rc.prop(self, 'mc_limited_dissolve')
        rc.prop(self, 'mc_limited_dissolve_angle')


        #b.separator()
        b = c.box().column(align=True)
        b.label(text='Tags and Node Group Types:')
        b.separator()
        g = b.grid_flow(row_major=True, columns=3, even_columns=True)
        self.__draw_tag_controls(g, 'Asset Tags', ConfigRegistry.get().tags(), 'new_tag', 'ADD_TAG', 'REMOVE_TAG')
        self.__draw_tag_controls(g, 'Shader Types', ConfigRegistry.get().shader_tags(), 'new_shader_tag', 'ADD_SHADER_TAG', 'REMOVE_SHADER_TAG')
        self.__draw_tag_controls(g, 'Geometry Types', ConfigRegistry.get().geometry_tags(), 'new_geometry_tag', 'ADD_GEOMETRY_TAG', 'REMOVE_GEOMETRY_TAG')


    def transfer_defaults(self):
        """
        Copy user default settings to properties.
        """
        p = Properties.get()
        pr = PreferencesPanel.get()
        p.author = pr.author
        p.texture_pack_mode = pr.texture_pack_mode
        p.oc_remove_animation_data = pr.oc_remove_animation_data
        p.oc_unparent = pr.oc_unparent
        p.oc_merge_objects = pr.oc_merge_objects
        p.place_quick = pr.place_quick
        p.place_linked_copy = pr.place_linked_copy
        p.place_auto_parent = pr.place_auto_parent
        p.mc_clear_custom_split_normals = pr.mc_clear_custom_split_normals
        p.mc_clear_sharp = pr.mc_clear_sharp
        p.mc_tris_to_quads = pr.mc_tris_to_quads
        p.mc_set_auto_smooth = pr.mc_set_auto_smooth
        p.mc_auto_smooth_angle = pr.mc_auto_smooth_angle
        p.mc_recalculate_normals_outside = pr.mc_recalculate_normals_outside
        p.mc_join_vertices = pr.mc_join_vertices
        p.mc_join_vertices_distance = pr.mc_join_vertices_distance
        p.mc_limited_dissolve = pr.mc_limited_dissolve
        p.mc_limited_dissolve_angle = pr.mc_limited_dissolve_angle


    @staticmethod
    def get() -> 'PreferencesPanel':
        return bpy.context.preferences.addons[__package__].preferences
        