import bpy

from bpy.types import Menu, UILayout

from ..utils.blender import tool_mode_string, tool_of_mode
from ..snw.ui import insert_brush
from ..operators.support_cascade_pie_menu import UI_OT_support_cascade_pie_menu
from ..registries.texture_registry import RepositoryType, TextureRegistry
from ..registries.database import Database


class VIEW3D_MT_snw_paint_pie_menu(Menu):
    """
    The primary PIE menu, shown when hitting the primary addon button ('D').
    """
    bl_idname = "VIEW3D_MT_snw_paint_pie_menu"
    bl_label = "Shader Node Wizard"


    @classmethod
    def poll(cls, context: bpy.types.Context):
        return context.space_data.type == 'VIEW_3D' and (
            context.mode == 'PAINT_TEXTURE' or context.mode == 'SCULPT'
        )

    
    """
    The following 3 methods are copied from blenders script code base.
    """

    @staticmethod
    def prop_unified(layout, context, brush, prop_name, unified_name=None, pressure_name=None, icon='NONE', text=None, slider=False, header=False):
        """ Generalized way of adding brush options to the UI,
            along with their pen pressure setting and global toggle, if they exist. """
        row = layout.row(align=True)
        ups = context.tool_settings.unified_paint_settings
        prop_owner = brush
        if unified_name and getattr(ups, unified_name):
            prop_owner = ups

        row.prop(prop_owner, prop_name, icon=icon, text=text, slider=slider)

        if pressure_name:
            row.prop(brush, pressure_name, text="")

        if unified_name and not header:
            row.prop(ups, unified_name, text="", icon='BRUSHES_ALL')

        return row


    @staticmethod
    def prop_unified_color(parent, context, brush, prop_name, *, text=None):
        ups = context.tool_settings.unified_paint_settings
        prop_owner = ups if ups.use_unified_color else brush
        parent.prop(prop_owner, prop_name, text=text)


    @staticmethod
    def prop_unified_color_picker(parent: UILayout, context, brush, prop_name, value_slider=True):
        ups = context.tool_settings.unified_paint_settings
        prop_owner = ups if ups.use_unified_color else brush
        parent.template_color_picker(prop_owner, prop_name, value_slider=value_slider)    


    def __draw_west(self, context: bpy.types.Context, pie: UILayout, tool_mode: str):
        """
        West side menu, brush options like blend mode, falloff, etc.
        """
        col = pie.column(align=True)

        ip = tool_of_mode(tool_mode)
        brush = ip.brush
        capabilities = brush.image_paint_capabilities

        # Brush color, size + strength.
        if capabilities.has_color:
            split = col.split(factor=0.1)
            VIEW3D_MT_snw_paint_pie_menu.prop_unified_color(split, context, brush, 'color', text='')
            VIEW3D_MT_snw_paint_pie_menu.prop_unified_color_picker(split, context, brush, 'color', value_slider=True)
            col.prop(brush, 'blend', text='')

        if capabilities.has_radius:
            VIEW3D_MT_snw_paint_pie_menu.prop_unified(col, context, brush, 'size', unified_name='use_unified_size',  pressure_name='use_pressure_size', slider=True)
            VIEW3D_MT_snw_paint_pie_menu.prop_unified(col, context, brush, 'strength', unified_name='use_unified_strength', pressure_name='use_pressure_strength', slider=True)

        col.separator()

        # Falloff options.
        col.row(align=True).prop(brush, 'curve_preset', text='', expand=True)
        if hasattr(brush, 'use_falloff'):
            col.prop(ip, 'use_normal_falloff', toggle=True, text='Normal Falloff')
            if ip.use_normal_falloff:
                col.prop(ip, 'normal_angle', slider=True, text='Falloff Angle')

        col.separator()

        # Mapping settings.
        tex_slot = brush.texture_slot
        col.prop(tex_slot, 'map_mode', expand=True)
        if tex_slot.map_mode == 'STENCIL' and brush.texture and brush.texture.type == 'IMAGE':
            col.separator()
            col.operator("brush.stencil_fit_image_aspect")
            col.operator("brush.stencil_reset_transform")

        col.separator()

        # Advanced Options            
        col.prop(brush, 'use_alpha', toggle=True)
        col.prop(brush, 'use_accumulate', toggle=True)



    def __draw_east(self, context: bpy.types.Context, pie: UILayout, tool_mode: str):
        c = pie.column()
        c.ui_units_x = 35

        recent = Database.instance().get_most_recent_brushes()
        favourites = Database.instance().get_favourite_brushes()

        if recent:
            c.label(text='Recent:')
            g = c.row().grid_flow(columns=8, align=True, even_columns=True)
            for e in recent:
                category, image = TextureRegistry.instance().get_by_bin_hash(e)
                entry = TextureRegistry.instance().get_image(RepositoryType.Brush, category, image, True)
                insert_brush(g, entry, True, entry.info.get_bin_hash() in favourites, tool_mode)
            for _ in range(8 - len(recent)):
                g.label()

        if favourites:
            fc = 0
            c.label(text='Favourites:')
            g = c.row().grid_flow(columns=8, align=True, even_columns=True)
            for e in favourites:
                category, image = TextureRegistry.instance().get_by_bin_hash(e)
                if category and image:
                    entry = TextureRegistry.instance().get_image(RepositoryType.Brush, category, image, True)
                    if entry:
                        fc += 1
                        insert_brush(g, entry, True, True, tool_mode)
            for _ in range(8 - fc):
                g.label()

        # Remove brush.
        op = c.operator('snw.support_select_brush', text='Remove Brush', icon='REMOVE')
        op.mode = 'Rem'
        op.tool_mode = tool_mode

        c.label(text='Library')
        g = c.row().grid_flow(columns=4, align=True, even_columns=True)
        for t in TextureRegistry.instance().get_category_enums(RepositoryType.Brush):
            op = g.operator(UI_OT_support_cascade_pie_menu.bl_idname, text=t[1])
            op.mode = 'Brush'
            op.param = t[0]


    def draw(self, context):
        tool_mode = tool_mode_string(context)

        pie = self.layout.menu_pie()
        self.__draw_west(context, pie.row(), tool_mode)
        self.__draw_east(context, pie, tool_mode)

