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

import bpy

from typing import List
from dataclasses import dataclass

from bpy.types import Menu, Panel, UILayout

from ..preferences import PreferencesPanel
from ..properties import Properties, AssetSubsetProperty
from ..utils.blender import is_valid_node_space, shader_node_wizard_enabled, is_340_or_gt, is_360_or_gt
from ..registries.icons_registry import IconsRegistry
from ..registries.resource_lists_registry import ResourceListsRegistry
from ..operators.std_node_drop import ASSET_OT_std_node_drop
from ..operators.import_node import ASSET_OT_import_node
from ..operators.multi_purpose import ASSET_OT_multi_purpose
from ..operators.refresh_res_list import ASSET_OT_refresh_res_lists
from ..utils.colors import colors

@dataclass
class ShdStd:
    text: str
    icon: str
    node: str
    operation: str = None
    blend_type: str = None


@dataclass
class GeomStd:
    text: str 
    icon: str
    node: str
    system: str = 'Geometry'
    operation: str = None
    do_show: bool = True


class NODE_MT_awp_nodes_pie_sub_menu(Menu):
    """
    Sub menu to nodes, listing a subset of shaders to add to tree.
    """
    bl_idname = 'NODE_MT_awp_nodes_pie_sub_menu'
    bl_label = 'Asset Wizard Pro'


    def draw(self, context: bpy.types.Context):
        # This is transferred using context pointer, contains category and (optionally) start.
        info = context.awp_ng_subset # type: AssetSubsetProperty
        is_shader = context.space_data.tree_type == 'ShaderNodeTree'
        type = PreferencesPanel.get().pie_node_group_mode

        rlr = ResourceListsRegistry.get()
        entries = []
        if type == 'CATALOG': entries = rlr.shaders_by_catalog(info.id) if is_shader else rlr.geometries_by_catalog(info.id)
        elif type == 'CATEGORY': entries = rlr.shaders_by_category(info.id) if is_shader else rlr.geometries_by_category(info.id)
        elif type == 'TOC': entries = rlr.shaders_by_toc(info.id) if is_shader else rlr.geometries_by_toc(info.id)

        # Draw insert operator for every matching item.
        c = self.layout.column(align=True)
        for tpl in entries:
            asset = rlr.asset_by_uuid(tpl[0])
            ASSET_OT_import_node.create_ui(c, 'SHADER' if is_shader else 'GEOMETRY', asset)


class NODE_MT_awp_nodes_pie_menu(Menu):
    """
    The primary PIE menu, shown when hitting the primary addon button ('S').
    """
    bl_idname = 'NODE_MT_awp_nodes_pie_menu'
    bl_label = 'Asset Wizard Pro'


    @classmethod
    def poll(cls, context):
        return is_valid_node_space(context, ['ShaderNodeTree', 'GeometryNodeTree'])       

 
    def __std_shader(self, l: UILayout, node: ShdStd, color: str = None):
        ASSET_OT_std_node_drop.create_ui(
            l, 
            'Shader', 
            node.node, 
            f"'{node.operation}'" if node.operation else '', 
            f"'{node.blend_type}'" if node.blend_type else '',
            node.text, 
            node.icon, 
            f'Drop a "{node.text}" node', 
            color=color
        )


    def __std_geometry(self, l: UILayout, node: GeomStd, color: str = None):
        ASSET_OT_std_node_drop.create_ui(
            l, 
            node.system, 
            node.node, 
            f"'{node.operation}'" if node.operation else '', 
            '', 
            node.text, 
            node.icon, 
            f'Drop a "{node.text}" node', 
            color=color
        )


    def __draw_std_nodes_shader(self, l: UILayout):
        nodes = {
            'Utils': [
                ShdStd( 'Map Range', 'UV_SYNC_SELECT', 'MapRange' ),
                ShdStd( 'Combine XYZ', 'FULLSCREEN_EXIT', 'CombineXYZ' ),
                ShdStd( 'Separate XYZ', 'FULLSCREEN_ENTER', 'SeparateXYZ' ),
                ShdStd( 'Blackbody', 'LIGHT_SUN', 'Blackbody' ),
            ],
            'Info': [
                ShdStd( 'Geometry', 'MOD_WIREFRAME', 'NewGeometry' ),
                ShdStd( 'Texture Coordinate', 'UV', 'TexCoord' ),
                ShdStd( 'Layer Weight', 'MATSPHERE', 'LayerWeight' ),
                ShdStd( 'Light Path', 'LIGHT_SPOT', 'LightPath' ),
                ShdStd( 'Ambient Occlusion', 'OVERLAY', 'AmbientOcclusion' ),
            ],
            'Texture': [
                ShdStd( 'Noise Texture', 'MOD_NOISE', 'TexNoise' ),
                ShdStd( 'Musgrave Texture', 'RNDCURVE', 'TexMusgrave' ),
                ShdStd( 'Voronoi Texture', 'LIGHTPROBE_PLANAR', 'TexVoronoi' ),
                ShdStd( 'White Noise', 'SEQ_HISTOGRAM', 'TexWhiteNoise' ),
                ShdStd( 'Image Texture', 'IMAGE_DATA', 'TexImage' ),
            ],
            'Color': [
                ShdStd( 'Hue Saturation', 'COLOR', 'HueSaturation' ),
                ShdStd( 'Brightness Contrast', 'IMAGE_ZDEPTH', 'BrightContrast' ),
                ShdStd( 'ColorRamp', 'NODE_TEXTURE', 'ValToRGB' ),
                ShdStd( 'Gamma', 'IPO_EASE_OUT', 'Gamma' ),
                ShdStd( 'Invert', 'OVERLAY', 'Invert' ),
            ],
            'Normal': [
                ShdStd( 'Bump Map', 'FORCE_TURBULENCE', 'Bump' ),
                ShdStd( 'Normal Map', 'MOD_OCEAN', 'NormalMap' ),
            ],
            'Shader': [
                ShdStd( 'Principled BSDF', 'MATERIAL', 'BsdfPrincipled' ),
                ShdStd( 'Mix Shader', 'ORIENTATION_GIMBAL', 'MixShader' ),
                ShdStd( 'Emission', 'LIGHT', 'Emission' ),
                ShdStd( 'Transparent', 'MOD_UVPROJECT', 'BsdfTransparent' ),
                ShdStd( 'Principled Volume', 'OUTLINER_DATA_VOLUME', 'VolumePrincipled' ),
                ShdStd( 'Glass BSDF', 'NODE_MATERIAL', 'BsdfGlass' ),
            ],
            'Input': [
                ShdStd( 'Attribute', 'NODETREE', 'Attribute' ),
                ShdStd( 'RGB', 'COLOR', 'RGB' ),
                ShdStd( 'Value', 'ONIONSKIN_ON', 'Value' ),
            ],
            'Math': [
                ShdStd( 'Add', 'DRIVER_TRANSFORM', 'Math', operation='ADD' ),
                ShdStd( 'Subtract', 'DRIVER_TRANSFORM', 'Math', operation='SUBTRACT' ),
                ShdStd( 'Multiply', 'DRIVER_TRANSFORM', 'Math', operation='MULTIPLY'),
                ShdStd( 'Divide', 'DRIVER_TRANSFORM', 'Math', operation='DIVIDE'),
                ShdStd( 'Multiply Add', 'DRIVER_TRANSFORM', 'Math', operation='MULTIPLY_ADD'),
                (),
                ShdStd( 'Power', 'DRIVER_TRANSFORM', 'Math', operation='POWER'),
                ShdStd( 'Absolute', 'DRIVER_TRANSFORM', 'Math', operation='ABSOLUTE'),
                (),
                ShdStd( 'Minimum', 'DRIVER_TRANSFORM', 'Math', operation='MINIMUM'),
                ShdStd( 'Maximum', 'DRIVER_TRANSFORM', 'Math', operation='MAXIMUM'),
                ShdStd( 'Less Than', 'DRIVER_TRANSFORM', 'Math', operation='LESS_THAN'),
                ShdStd( 'Greater Than', 'DRIVER_TRANSFORM', 'Math', operation='GREATER_THAN'),
                ShdStd( 'Smooth Min', 'DRIVER_TRANSFORM', 'Math', operation='SMOOTH_MIN'),
                ShdStd( 'Smooth Max', 'DRIVER_TRANSFORM', 'Math', operation='SMOOTH_MAX'),
                (),
                ShdStd( 'Round', 'DRIVER_TRANSFORM', 'Math', operation='ROUND'),
                ShdStd( 'Floor', 'DRIVER_TRANSFORM', 'Math', operation='FLOOR'),
                ShdStd( 'Ceil', 'DRIVER_TRANSFORM', 'Math', operation='CEIL'),
                (),
                ShdStd( 'Fraction', 'DRIVER_TRANSFORM', 'Math', operation='FRACT'),
                ShdStd( 'Modulo', 'DRIVER_TRANSFORM', 'Math', operation='MODULO'),
                ShdStd( 'Warp', 'DRIVER_TRANSFORM', 'Math', operation='WRAP'),
                ShdStd( 'Snap', 'DRIVER_TRANSFORM', 'Math', operation='SNAP'),
                ShdStd( 'Ping Pong', 'DRIVER_TRANSFORM', 'Math', operation='PINGPONG'),
                (),
                ShdStd( 'Sin', 'DRIVER_TRANSFORM', 'Math', operation='SINE'),
                ShdStd( 'Cos', 'DRIVER_TRANSFORM', 'Math', operation='COSINE'),
                ShdStd( 'Tan', 'DRIVER_TRANSFORM', 'Math', operation='TANGENT'),
            ],
            'VMath': [
                ShdStd( 'Add', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='ADD' ),
                ShdStd( 'Subtract', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='SUBTRACT'),
                ShdStd( 'Multiply', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='MULTIPLY'),
                ShdStd( 'Divide', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='DIVIDE'),
                ShdStd( 'Multiply Add', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='MULTIPLY_ADD'),
                (),
                ShdStd( 'Cross', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='CROSS_PRODUCT'),
                ShdStd( 'Project', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='PROJECT'),
                ShdStd( 'Reflect', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='REFLECT'),
                ShdStd( 'Refract', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='REFRACT'),
                ShdStd( 'Faceforward', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='FACEFORWARD'),
                ShdStd( 'Dot', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='DOT_PRODUCT'),
                (),
                ShdStd( 'Distance', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='DISTANCE'),
                ShdStd( 'Lenth', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='LENGTH'),
                ShdStd( 'Scale', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='SCALE'),
                ShdStd( 'Normalize', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='NORMALIZE'),
                (),
                ShdStd( 'Absolute', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='ABSOLUTE'),
                ShdStd( 'Minimum', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='MINIMUM'),
                ShdStd( 'Maximum', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='MAXIMUM'),
                ShdStd( 'Floor', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='FLOOR'),
                ShdStd( 'Ceil', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='CEIL'),
                ShdStd( 'Fraction', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='FRACTION'),
                ShdStd( 'Modulo', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='MODULO'),
                ShdStd( 'Warp', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='WRAP'),
                ShdStd( 'Snap', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', operation='SNAP'),
            ],
            'Mix': [
                ShdStd( 'Mix', 'IMAGE', 'MixRGB', blend_type='MIX' ),
                ShdStd( 'Darken', 'IMAGE', 'MixRGB', blend_type='DARKEN'),
                ShdStd( 'Multiply', 'IMAGE', 'MixRGB', blend_type='MULTIPLY'),
                ShdStd( 'Burn', 'IMAGE', 'MixRGB', blend_type='BURN'),
                (),
                ShdStd( 'Lighten', 'IMAGE', 'MixRGB', blend_type='LIGHTEN'),
                ShdStd( 'Screen', 'IMAGE', 'MixRGB', blend_type='SCREEN'),
                ShdStd( 'Dodge', 'IMAGE', 'MixRGB', blend_type='DODGE'),
                ShdStd( 'Add', 'IMAGE', 'MixRGB', blend_type='ADD'),
                (),
                ShdStd( 'Overlay', 'IMAGE', 'MixRGB', blend_type='OVERLAY'),
                ShdStd( 'Soft Light', 'IMAGE', 'MixRGB', blend_type='SOFT_LIGHT'),
                ShdStd( 'Linear Light', 'IMAGE', 'MixRGB', blend_type='LINEAR_LIGHT'),
                (),
                ShdStd( 'Difference', 'IMAGE', 'MixRGB', blend_type='DIFFERENCE'),
                ShdStd( 'Subract', 'IMAGE', 'MixRGB', blend_type='SUBTRACT'),
                ShdStd( 'Divide', 'IMAGE', 'MixRGB', blend_type='DIVIDE'),
                (),
                ShdStd( 'Hue', 'IMAGE', 'MixRGB', blend_type='HUE'),
                ShdStd( 'Saturation', 'IMAGE', 'MixRGB', blend_type='SATURATION'),
                ShdStd( 'Color', 'IMAGE', 'MixRGB', blend_type='COLOR'),
                ShdStd( 'Value', 'IMAGE', 'MixRGB', blend_type='VALUE'),
            ]
        }

        block_colors = {
            'Utils': colors[0],
            'Info': colors[1],
            'Texture': colors[2],
            'Color': colors[3],
            'Normal': colors[5],
            'Shader': colors[6],
            'Input': colors[4],
            'Math': colors[14],
            'VMath': colors[15],
            'Mix': colors[7],
        }

        columns = [
            [ 'Info', 'Texture', 'Color', 'Shader', 'Input', 'Normal', 'Utils', ],
            [ 'Math' ],
            [ 'VMath' ],
            [ 'Mix' ],
        ]

        l.ui_units_x = 25
        l.ui_units_y = 20
        c = l.grid_flow(columns=len(columns), even_columns=False)

        for _, col in enumerate(columns):
            r = c.column(align=True)
            prev = None
            for k in col:
                #r.label(text=f'{i}{k}:')
                for node in nodes[k]:
                    if isinstance(node, ShdStd):
                        self.__std_shader(r, node, color=block_colors[k])
                    else:
                        # Prevent double seperators (due to do_show flag).
                        if isinstance(prev, ShdStd):
                            r.separator(factor=0.5)
                    prev = node
                r.separator()


    def __draw_std_nodes_geometry(self, l: UILayout):
        nodes = {
            'Attribute': [
                GeomStd( 'Attribute Statistic', 'PROPERTIES', 'AttributeStatistic' ),
                GeomStd( 'Capture Attribute', 'MOD_HUE_SATURATION', 'CaptureAttribute' ),
                GeomStd( 'Transfer Attribute', 'MOD_DATA_TRANSFER', 'AttributeTransfer', do_show=not is_340_or_gt() ),
                GeomStd( 'Store Named Attribute', 'FONT_DATA', 'StoreNamedAttribute' ),
                GeomStd( 'Named Attribute', 'TRACKING_FORWARDS_SINGLE', 'InputNamedAttribute' ),
            ],
            'Curve': [
                GeomStd( 'Curve to Mesh', 'GP_SELECT_STROKES', 'CurveToMesh' ),
                GeomStd( 'Curve to Points', 'DRIVER', 'CurveToPoints' ),
                GeomStd( 'Fillet Curve', 'GP_SELECT_BETWEEN_STROKES', 'FilletCurve' ),
                GeomStd( 'Sample Curve', 'IPO_BOUNCE', 'SampleCurve' ),
                GeomStd( 'Resample Curve', 'IPO_ELASTIC', 'ResampleCurve' ),
                (),
                GeomStd( 'Set Curve Normal', 'MOD_OCEAN', 'SetCurveNormal', do_show=is_340_or_gt() ),
                GeomStd( 'Set Curve Radius', 'LIGHT_POINT', 'SetCurveRadius' ),
                GeomStd( 'Set Curve Tilt', 'GIZMO', 'SetCurveTilt' ),
                (),
                GeomStd( 'Curve Length', 'TRACKING_FORWARDS_SINGLE', 'CurveLength' ),
                GeomStd( 'Spline Parameter', 'TRACKING_FORWARDS_SINGLE', 'SplineParameter' ),

            ],
            'Curve Primitives': [
                GeomStd( 'Curve Circle', 'CURVE_BEZCIRCLE', 'CurvePrimitiveCircle' ),
                GeomStd( 'Curve Line', 'CURVE_PATH', 'CurvePrimitiveLine' ),
            ],
            'Geometry': [
                GeomStd( 'Bounding Box', 'META_PLANE', 'BoundBox' ),
                GeomStd( 'Convex Hull', 'META_ELLIPSOID', 'ConvexHull' ),
                GeomStd( 'Join Geometry', 'MOD_BUILD', 'JoinGeometry' ),
                GeomStd( 'Delete Geometry', 'PANEL_CLOSE', 'DeleteGeometry' ),
                GeomStd( 'Duplicate Elements', 'DUPLICATE', 'DuplicateElements' ),
                GeomStd( 'Merge by Distance', 'AUTOMERGE_OFF', 'MergeByDistance' ),
                GeomStd( 'Transform', 'PARTICLES', 'Transform' ),
                GeomStd( 'Separate Components', 'MOD_ARRAY', 'SeparateComponents' ),
                GeomStd( 'Separate Geometry', 'MOD_EXPLODE', 'SeparateGeometry' ),
                GeomStd( 'Geometry To Instance', 'PACKAGE', 'GeometryToInstance' ),
                (),
                GeomStd( 'Geometry Proximity', 'MOD_SHRINKWRAP', 'Proximity'),
                GeomStd( 'Raycast', 'LIGHT_AREA', 'Raycast'),
                GeomStd( 'Sample Index', 'OUTLINER_DATA_LATTICE', 'SampleIndex', do_show=is_340_or_gt() ),
                GeomStd( 'Sample Nearest', 'MOD_SIMPLEDEFORM', 'SampleNearest', do_show=is_340_or_gt() ),
                (),
                GeomStd( 'Set Position', 'CON_ROTLIMIT', 'SetPosition' ),
                GeomStd( 'Set ID', 'COPYDOWN', 'SetID' ),
                (),
                GeomStd( 'Position', 'TRACKING_FORWARDS_SINGLE', 'InputPosition' ),
                GeomStd( 'ID', 'TRACKING_FORWARDS_SINGLE', 'InputID' ),
                GeomStd( 'Index', 'TRACKING_FORWARDS_SINGLE', 'InputIndex' ),
                GeomStd( 'Normal', 'TRACKING_FORWARDS_SINGLE', 'InputNormal' ),
            ],
            'Input': [
                GeomStd( 'Collection Info', 'TRACKING_FORWARDS_SINGLE', 'CollectionInfo' ),
                GeomStd( 'Object Info', 'TRACKING_FORWARDS_SINGLE', 'ObjectInfo' ),
                GeomStd( 'Is Viewport', 'TRACKING_FORWARDS_SINGLE', 'IsViewport' ),
                GeomStd( 'Scene Time', 'TRACKING_FORWARDS_SINGLE', 'InputSceneTime' ),
                GeomStd( 'Boolean Constant', 'TRACKING_FORWARDS_SINGLE', 'InputBool', 'Function' ),
                GeomStd( 'Integer Constant', 'TRACKING_FORWARDS_SINGLE', 'InputInt', 'Function' ),
                GeomStd( 'Value Constant', 'TRACKING_FORWARDS_SINGLE', 'Value', 'Shader' ),
                GeomStd( 'Color Constant', 'TRACKING_FORWARDS_SINGLE', 'InputColor', 'Function' ),
                GeomStd( 'String Constant', 'TRACKING_FORWARDS_SINGLE', 'InputString', 'Function' ),
                GeomStd( 'Vector Constant', 'TRACKING_FORWARDS_SINGLE', 'InputVector', 'Function' ),
            ],
            'Instances': [
                GeomStd( 'Instance on Points', 'FORCE_LENNARDJONES', 'InstanceOnPoints' ),
                GeomStd( 'Realize Instances', 'PIVOT_ACTIVE', 'RealizeInstances' ),
                GeomStd( 'Rotate Instances', 'DRIVER_ROTATIONAL_DIFFERENCE', 'RotateInstances' ),
                GeomStd( 'Scale Instances', 'MOD_WIREFRAME', 'ScaleInstances' ),
                GeomStd( 'Translate Instances', 'CON_LOCLIKE', 'TranslateInstances' ),
                GeomStd( 'Geometry to Instance', 'OUTLINER_OB_GROUP_INSTANCE', 'GeometryToInstance' ),
            ],
            'Material': [
                GeomStd( 'Set Material', 'MATERIAL', 'SetMaterial' ),
                GeomStd( 'Set Material Index', 'NODE_MATERIAL', 'SetMaterialIndex' ),
                (),
                GeomStd( 'Material', 'TRACKING_FORWARDS_SINGLE', 'InputMaterial' ),
                GeomStd( 'Material Index', 'TRACKING_FORWARDS_SINGLE', 'InputMaterialIndex' ),
            ],
            'Mesh': [
                GeomStd( 'Mesh to Curve', 'CURVE_NCURVE', 'MeshToCurve' ),
                GeomStd( 'Mesh to Points', 'MOD_PARTICLE_INSTANCE', 'MeshToPoints' ),
                GeomStd( 'Mesh to Volume', 'VOLUME_DATA', 'MeshToVolume' ),
                (),
                GeomStd( 'Extrude Mesh', 'MOD_SOLIDIFY', 'ExtrudeMesh' ),
                GeomStd( 'Flip Faces', 'ARROW_LEFTRIGHT', 'FlipFaces' ),
                GeomStd( 'Mesh Boolean', 'MOD_BOOLEAN', 'MeshBoolean' ),
                GeomStd( 'Scale Elements', 'MOD_WIREFRAME', 'ScaleElements' ),
                GeomStd( 'Split Edges', 'MOD_EDGESPLIT', 'SplitEdges' ),
                GeomStd( 'Subdivide Mesh', 'MOD_MULTIRES', 'SubdivideMesh' ),
                GeomStd( 'Subdivision Surface', 'MOD_SUBSURF', 'SubdivisionSurface' ),
                (),
                GeomStd( 'Sample Nearest Surface', 'MOD_NORMALEDIT', 'SampleNearestSurface', do_show=is_340_or_gt() ),
                GeomStd( 'Sample UV Surface', 'FORCE_TEXTURE', 'SampleUVSurface', do_show=is_340_or_gt() ),
                (),
                GeomStd( 'Pack UV Islands', 'GROUP', 'UVPackIslands' ),
                GeomStd( 'UV Unwrap', 'UV', 'UVUnwrap' ),
                (),
                GeomStd( 'Set Shade Smooth', 'NODE_MATERIAL', 'SetShadeSmooth' ),
            ],
            'Mesh Primitives': [
                GeomStd( 'Cube', 'MESH_CUBE', 'MeshCube' ),
                GeomStd( 'Cylinder', 'MESH_CYLINDER', 'MeshCylinder' ),
                GeomStd( 'Grid', 'MESH_GRID', 'MeshGrid' ),
                GeomStd( 'Ico Sphere', 'MESH_ICOSPHERE', 'MeshIcoSphere' ),        
                GeomStd( 'Mesh Line', 'EMPTY_SINGLE_ARROW', 'MeshLine' ),
            ],
            'Point': [
                GeomStd( 'Distribute Points on Faces', 'LIGHTPROBE_PLANAR', 'DistributePointsOnFaces' ),
                GeomStd( 'Distribute Points in Volume', 'LIGHTPROBE_CUBEMAP', 'DistributePointsInVolume', do_show=is_340_or_gt() ),
            ],
            'Textures': [
                GeomStd( 'Noise Texture', 'MOD_NOISE', 'TexNoise', 'Shader' ),
                GeomStd( 'Musgrave Texture', 'RNDCURVE', 'TexMusgrave', 'Shader' ),
                GeomStd( 'Voronoi Texture', 'LIGHTPROBE_PLANAR', 'TexVoronoi', 'Shader' ),
                GeomStd( 'White Noise', 'SEQ_HISTOGRAM', 'TexWhiteNoise', 'Shader' ),
            ],
            'Utilities': [
                GeomStd( 'Align Euler to Vector', 'HANDLE_ALIGNED', 'AlignEulerToVector', 'Function' ),
                GeomStd( 'Boolean Math', 'SHORTDISPLAY', 'BooleanMath', 'Function' ),
                GeomStd( 'Compare', 'SHORTDISPLAY', 'Compare', 'Function' ),
                GeomStd( 'Field at Index', 'SURFACE_NCIRCLE', 'FieldAtIndex' ),
                GeomStd( 'Map Range', 'UV_SYNC_SELECT', 'MapRange', 'Shader' ),
                GeomStd( 'Random Value', 'MOD_NOISE', 'RandomValue', 'Function' ),
                GeomStd( 'Switch', 'CENTER_ONLY', 'Switch' ),
            ],
            'Vector': [
                GeomStd( 'Combine XYZ' , 'FULLSCREEN_EXIT', 'CombineXYZ', 'Shader' ),
                GeomStd( 'Separate XYZ' , 'FULLSCREEN_ENTER', 'SeparateXYZ', 'Shader' ),
                
            ],
            'Volume': [
                GeomStd( 'Volume To Mesh' , 'VOLUME_DATA', 'VolumeToMesh' ),
            ],
            'Math': [
                GeomStd( 'Add', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='ADD' ),
                GeomStd( 'Subtract', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='SUBTRACT' ),
                GeomStd( 'Multiply', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='MULTIPLY'),
                GeomStd( 'Divide', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='DIVIDE'),
                GeomStd( 'Multiply Add', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='MULTIPLY_ADD'),
                (),
                GeomStd( 'Power', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='POWER'),
                GeomStd( 'Absolute', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='ABSOLUTE'),
                (),
                GeomStd( 'Minimum', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='MINIMUM'),
                GeomStd( 'Maximum', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='MAXIMUM'),
                GeomStd( 'Less Than', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='LESS_THAN'),
                GeomStd( 'Greater Than', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='GREATER_THAN'),
                GeomStd( 'Smooth Min', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='SMOOTH_MIN'),
                GeomStd( 'Smooth Max', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='SMOOTH_MAX'),
                (),
                GeomStd( 'Round', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='ROUND'),
                GeomStd( 'Floor', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='FLOOR'),
                GeomStd( 'Ceil', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='CEIL'),
                (),
                GeomStd( 'Fraction', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='FRACT'),
                GeomStd( 'Modulo', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='MODULO'),
                GeomStd( 'Warp', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='WRAP'),
                GeomStd( 'Snap', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='SNAP'),
                GeomStd( 'Ping Pong', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='PINGPONG'),
                (),
                GeomStd( 'Sin', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='SINE'),
                GeomStd( 'Cos', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='COSINE'),
                GeomStd( 'Tan', 'DRIVER_TRANSFORM', 'Math', 'Shader', operation='TANGENT'),
            ],
            'VMath': [
                GeomStd( 'Add', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='ADD' ),
                GeomStd( 'Subtract', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='SUBTRACT'),
                GeomStd( 'Multiply', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='MULTIPLY'),
                GeomStd( 'Divide', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='DIVIDE'),
                GeomStd( 'Multiply Add', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='MULTIPLY_ADD'),
                (),
                GeomStd( 'Cross', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='CROSS_PRODUCT'),
                GeomStd( 'Project', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='PROJECT'),
                GeomStd( 'Reflect', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='REFLECT'),
                GeomStd( 'Refract', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='REFRACT'),
                GeomStd( 'Faceforward', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='FACEFORWARD'),
                GeomStd( 'Dot', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='DOT_PRODUCT'),
                (),
                GeomStd( 'Distance', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='DISTANCE'),
                GeomStd( 'Lenth', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='LENGTH'),
                GeomStd( 'Scale', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='SCALE'),
                GeomStd( 'Normalize', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='NORMALIZE'),
                (),
                GeomStd( 'Absolute', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='ABSOLUTE'),
                GeomStd( 'Minimum', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='MINIMUM'),
                GeomStd( 'Maximum', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='MAXIMUM'),
                GeomStd( 'Floor', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='FLOOR'),
                GeomStd( 'Ceil', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='CEIL'),
                GeomStd( 'Fraction', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='FRACTION'),
                GeomStd( 'Modulo', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='MODULO'),
                GeomStd( 'Warp', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='WRAP'),
                GeomStd( 'Snap', 'TRACKING_CLEAR_FORWARDS', 'VectorMath', 'Shader', operation='SNAP'),
            ]
        }

        block_colors = {
            'Attribute': colors[0],
            'Curve': colors[1],
            'Curve Primitives': colors[2],
            'Geometry': colors[3],
            'Input': colors[4],
            'Instances': colors[5],
            'Material': colors[6],
            'Mesh': colors[7],
            'Mesh Primitives': colors[8],
            'Point': colors[9],
            'Textures': colors[10],
            'Utilities': colors[11],
            'Vector': colors[12],
            'Volume': colors[13],
            'Math': colors[14],
            'VMath': colors[15],
        }

        columns = [
            [ 'Mesh', 'Curve', 'Volume', ],
            [ 'Geometry',   ],
            [ 'Mesh Primitives', 'Curve Primitives', 'Textures', 'Utilities', 'Point', 'Instances', ],
            [ 'Attribute', 'Vector', 'Input', 'Material', ],
            [ 'Math' ],
            [ 'VMath' ],
        ]

        l.ui_units_x = 40
        l.ui_units_y = 20
        c = l.grid_flow(columns=len(columns), even_columns=True)

        for i, col in enumerate(columns):
            r = c.column(align=True)
            prev = None
            for k in col:
                #r.label(text=f'{i}{k}:')
                for node in nodes[k]:
                    if isinstance(node, GeomStd):
                        if node.do_show:
                            self.__std_geometry(r, node, color=block_colors[k])
                    else:
                        # Prevent double seperators (due to do_show flag).
                        if isinstance(prev, GeomStd):
                            r.separator(factor=0.5)
                    prev = node
                r.separator()

            # Add in first row (Blender >= 3.6)
            if i == 0 and is_360_or_gt():
                #r.operator('node.add_simulation_zone')
                ASSET_OT_multi_purpose.create_ui(r, 'add_simulation', 'Add Simulation Zone', 'PHYSICS', color=colors[16])


    def __draw_imports(self, l: UILayout, is_shader: bool):
        l.ui_units_y = 20

        type = PreferencesPanel.get().pie_node_group_mode
        props = Properties.get()

        # Get list of categories depending on type from preferences.
        entries = {
            'CATALOG': props.shader_nodes_by_catalog if is_shader else props.geometry_nodes_by_catalog,
            'CATEGORY': props.shader_nodes_by_category if is_shader else props.geometry_nodes_by_category,
            'TOC': props.shader_nodes_by_toc if is_shader else props.geometry_nodes_by_toc,
        }.get(type, []) # type: List[AssetSubsetProperty]

        # Create menu entry for each item.
        g = l.column(align=True) #l.grid_flow(row_major=True, columns=0, even_columns=True, align=True)
        if entries:
            for entry in entries:
                r = g.row(align=True)
                r.context_pointer_set('awp_ng_subset', entry)
                r.menu(NODE_MT_awp_nodes_pie_sub_menu.bl_idname, text=entry.text)
            g.separator()
        else:
            g.label(text='No assets found in cache, consider rescan')

        ASSET_OT_refresh_res_lists.create_ui(g, 'Refresh Cache')


    def __draw_others_menu(self, l: UILayout):
        if shader_node_wizard_enabled():
            op = l.operator('wm.call_menu_pie', text='Shader Node Wizard', icon_value=IconsRegistry.get_icon('snw-logo-mini'))
            op.name = 'NODE_MT_snw_main_pie_menu'


    def draw(self, context: bpy.types.Context):
        is_shader = context.space_data.tree_type == 'ShaderNodeTree'

        pie = self.layout.menu_pie()

        if is_shader:
            self.__draw_std_nodes_shader(pie.row().column(align=True)) # W
        else:
            self.__draw_std_nodes_geometry(pie.row().column(align=True)) # W

        self.__draw_imports(pie.row().column(align=True), is_shader) # E

        if is_shader:
            self.__draw_others_menu(pie.row().column(align=True)) # S

        c = pie.row().column(align=True) # N
        c.ui_units_x = 6
        c.menu('NODE_MT_add', text='All Nodes') 

