import bpy
from bpy.types import Operator, Panel, PropertyGroup
from bpy.props import StringProperty, PointerProperty, FloatVectorProperty, IntProperty

# =========================================================
# アドオン宣言
# =========================================================
bl_info = {
    "name": "Grease Pencil Object Manager",
    "author": "AI",
    "version": (4, 3, 2),
    "blender": (5, 0, 0),
    "location": "View3D > Sidebar > GP Manager",
    "description": "GPオブジェクト作成 → ツール/太さ/色 → レイヤー → エフェクト → 完成",
    "category": "Grease Pencil",
}

PREFIX_VAL = "ZIONAD"
ADDON_TITLE = "GP Manager"
DEFAULT_COL_NAME = "GreaseObjects"
DEFAULT_BASE_NAME = "GP_"

# =========================================================
# 1. プロパティグループ (12色のカラー設定)
# =========================================================
class ZIONAD_Properties(PropertyGroup):
    col_name: StringProperty(name="主コレクション", default=DEFAULT_COL_NAME)
    obj_base_name: StringProperty(name="ベース名", default=DEFAULT_BASE_NAME)

    col_01_name: StringProperty(default="黒"); col_01_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.05, 0.05, 0.05, 1.0), min=0.0, max=1.0); col_01_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.3, 0.3, 0.3, 0.5), min=0.0, max=1.0)
    col_02_name: StringProperty(default="白"); col_02_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(1.0, 1.0, 1.0, 1.0), min=0.0, max=1.0); col_02_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.9, 0.9, 0.5), min=0.0, max=1.0)
    col_03_name: StringProperty(default="灰"); col_03_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.4, 0.4, 1.0), min=0.0, max=1.0); col_03_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.6, 0.6, 0.6, 0.5), min=0.0, max=1.0)
    col_04_name: StringProperty(default="赤"); col_04_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.8, 0.1, 0.1, 1.0), min=0.0, max=1.0); col_04_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.4, 0.4, 0.5), min=0.0, max=1.0)
    col_05_name: StringProperty(default="青"); col_05_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.1, 0.2, 0.8, 1.0), min=0.0, max=1.0); col_05_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.5, 0.9, 0.5), min=0.0, max=1.0)
    col_06_name: StringProperty(default="緑"); col_06_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.1, 0.7, 0.2, 1.0), min=0.0, max=1.0); col_06_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.9, 0.5, 0.5), min=0.0, max=1.0)
    col_07_name: StringProperty(default="黄"); col_07_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.8, 0.1, 1.0), min=0.0, max=1.0); col_07_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.9, 0.5, 0.5), min=0.0, max=1.0)
    col_08_name: StringProperty(default="水"); col_08_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.1, 0.8, 0.9, 1.0), min=0.0, max=1.0); col_08_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.5, 0.9, 0.9, 0.5), min=0.0, max=1.0)
    col_09_name: StringProperty(default="紫"); col_09_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.6, 0.1, 0.8, 1.0), min=0.0, max=1.0); col_09_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.8, 0.5, 0.9, 0.5), min=0.0, max=1.0)
    col_10_name: StringProperty(default="橙"); col_10_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.4, 0.1, 1.0), min=0.0, max=1.0); col_10_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.6, 0.4, 0.5), min=0.0, max=1.0)
    col_11_name: StringProperty(default="茶"); col_11_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.2, 0.1, 1.0), min=0.0, max=1.0); col_11_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.6, 0.4, 0.3, 0.5), min=0.0, max=1.0)
    col_12_name: StringProperty(default="桃"); col_12_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.3, 0.6, 1.0), min=0.0, max=1.0); col_12_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.6, 0.8, 0.5), min=0.0, max=1.0)

# =========================================================
# 2. ユーティリティ (ジオメトリノード自動生成)
# =========================================================
def is_gp_object(obj):
    return obj and obj.type in {'GPENCIL', 'GREASEPENCIL'}

def get_next_name(base_name):
    max_num = 0
    for obj in bpy.data.objects:
        if is_gp_object(obj) and obj.name.startswith(base_name):
            try: max_num = max(max_num, int(obj.name.replace(base_name, "")))
            except: pass
    return f"{base_name}{max_num + 1:03d}"

# ★ ボイリング(震え) ノードツリー生成
def create_noise_node_group():
    name = "GP_Boiling_Noise_FX"
    if name in bpy.data.node_groups: return bpy.data.node_groups[name]
    
    ng = bpy.data.node_groups.new(name, 'GeometryNodeTree')
    ng.interface.new_socket("Geometry", in_out='INPUT', socket_type='NodeSocketGeometry')
    
    # 【変更】震えの強さを -10.0 から 10.0 まで許可
    sock_fac = ng.interface.new_socket("Factor", in_out='INPUT', socket_type='NodeSocketFloat')
    sock_fac.default_value = 0.05
    sock_fac.min_value = -10.0  # マイナスを許可
    sock_fac.max_value = 10.0   
    
    ng.interface.new_socket("Geometry", in_out='OUTPUT', socket_type='NodeSocketGeometry')
    
    node_in = ng.nodes.new('NodeGroupInput')
    node_out = ng.nodes.new('NodeGroupOutput')
    
    noise = ng.nodes.new('ShaderNodeTexNoise'); noise.noise_dimensions = '4D'
    time = ng.nodes.new('GeometryNodeInputSceneTime')
    math_mul = ng.nodes.new('ShaderNodeMath'); math_mul.operation = 'MULTIPLY'; math_mul.inputs[1].default_value = 15.0
    vmath_sub = ng.nodes.new('ShaderNodeVectorMath'); vmath_sub.operation = 'SUBTRACT'; vmath_sub.inputs[1].default_value = (0.5, 0.5, 0.5)
    vmath_scale = ng.nodes.new('ShaderNodeVectorMath'); vmath_scale.operation = 'SCALE'
    set_pos = ng.nodes.new('GeometryNodeSetPosition')
    
    ng.links.new(time.outputs['Seconds'], math_mul.inputs[0])
    ng.links.new(math_mul.outputs['Value'], noise.inputs['W'])
    ng.links.new(noise.outputs['Color'], vmath_sub.inputs[0])
    ng.links.new(vmath_sub.outputs['Vector'], vmath_scale.inputs[0])
    ng.links.new(node_in.outputs['Factor'], vmath_scale.inputs['Scale'])
    ng.links.new(node_in.outputs['Geometry'], set_pos.inputs['Geometry'])
    ng.links.new(vmath_scale.outputs['Vector'], set_pos.inputs['Offset'])
    ng.links.new(set_pos.outputs['Geometry'], node_out.inputs['Geometry'])
    return ng

# ★ 太さ追加 ノードツリー生成
def create_thickness_node_group():
    name = "GP_Outline_Thickness_FX"
    if name in bpy.data.node_groups: return bpy.data.node_groups[name]
    
    ng = bpy.data.node_groups.new(name, 'GeometryNodeTree')
    ng.interface.new_socket("Geometry", in_out='INPUT', socket_type='NodeSocketGeometry')
    
    # 太さはマイナスにすると形状が破綻するため 0.0 固定が安全です
    sock_thick = ng.interface.new_socket("Add_Thickness", in_out='INPUT', socket_type='NodeSocketFloat')
    sock_thick.default_value = 0.02
    sock_thick.min_value = 0.0   
    sock_thick.max_value = 10.0  
    
    ng.interface.new_socket("Geometry", in_out='OUTPUT', socket_type='NodeSocketGeometry')
    
    node_in = ng.nodes.new('NodeGroupInput')
    node_out = ng.nodes.new('NodeGroupOutput')
    
    rad_in = ng.nodes.new('GeometryNodeInputRadius')
    math_add = ng.nodes.new('ShaderNodeMath'); math_add.operation = 'ADD'
    set_rad = ng.nodes.new('GeometryNodeSetCurveRadius')
    
    ng.links.new(rad_in.outputs['Radius'], math_add.inputs[0])
    ng.links.new(node_in.outputs['Add_Thickness'], math_add.inputs[1])
    ng.links.new(node_in.outputs['Geometry'], set_rad.inputs['Curve'])
    ng.links.new(math_add.outputs['Value'], set_rad.inputs['Radius'])
    ng.links.new(set_rad.outputs['Curve'], node_out.inputs['Geometry'])
    return ng

# =========================================================
# 3. オペレーター群
# =========================================================
class ZIONAD_OT_CreateGP(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.create_gp"
    bl_label = "① + 新規 GPオブジェクトを作成"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        props = context.scene.zionad_props
        curr_frame = context.scene.frame_current
        if context.mode != 'OBJECT':
            try: bpy.ops.object.mode_set(mode='OBJECT')
            except: pass
        col = bpy.data.collections.get(props.col_name)
        if not col:
            col = bpy.data.collections.new(props.col_name)
            context.scene.collection.children.link(col)
        try: bpy.ops.object.grease_pencil_add(type='EMPTY')
        except: bpy.ops.object.gpencil_add(type='EMPTY')
        gp = context.active_object
        gp.name = get_next_name(props.obj_base_name)
        for c in gp.users_collection: c.objects.unlink(gp)
        col.objects.link(gp)

        gp_data = gp.data
        if hasattr(gp_data, "layers"):
            try:
                if len(gp_data.layers) == 0:
                    layer_fill = gp_data.layers.new(name="Fill")
                    if hasattr(layer_fill, "frames"): layer_fill.frames.new(curr_frame)
                    layer_line = gp_data.layers.new(name="Line")
                    if hasattr(layer_line, "frames"): layer_line.frames.new(curr_frame)
                    gp_data.layers.active = layer_line
            except: pass
        mat_name = "GP_Mat_01"
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(name=mat_name)
            try: bpy.data.materials.create_gpencil_data(mat)
            except: pass
        if hasattr(mat, "grease_pencil") and mat.grease_pencil:
            mat.grease_pencil.color = props.col_01_stroke
            mat.grease_pencil.fill_color = props.col_01_fill
        mat.diffuse_color = props.col_01_stroke
        gp.data.materials.append(mat)
        gp.active_material_index = 0
        try: bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
        except: 
            try: bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
            except: pass
        try: bpy.ops.grease_pencil.insert_blank_frame()
        except: pass
        try: bpy.ops.gpencil.blank_frame_add()
        except: pass
        self.report({'INFO'}, f"'{gp.name}' を作成しました。")
        return {'FINISHED'}

class ZIONAD_OT_SetBrushTool(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_brush_tool"
    bl_label = "ツール変更"
    tool_type: StringProperty()
    def execute(self, context):
        if context.mode not in {'PAINT_GPENCIL', 'PAINT_GREASE_PENCIL'}:
            try: bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
            except: pass
        if self.tool_type == 'ERASER':
            try: bpy.ops.wm.tool_set_by_id(name="builtin_brush.Erase")
            except: pass
        else:
            try: bpy.ops.wm.tool_set_by_id(name="builtin_brush.Draw")
            except: pass
            ts = context.scene.tool_settings
            paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
            if paint:
                t_name = {"PEN":"Pen", "PENCIL":"Pencil", "MARKER":"Marker"}.get(self.tool_type, "Pen")
                for b in bpy.data.brushes:
                    if t_name.lower() in b.name.lower():
                        try: paint.brush = b
                        except:
                            try: bpy.ops.brush.asset_activate(asset_id=b.name)
                            except: pass
                        break
        return {'FINISHED'}

class ZIONAD_OT_ToggleCanvas(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.toggle_canvas"
    bl_label = "グリッド表示"
    def execute(self, context):
        for area in context.screen.areas:
            if area.type == 'VIEW_3D':
                for space in area.spaces:
                    if space.type == 'VIEW_3D':
                        state = space.overlay.show_floor
                        space.overlay.show_floor = not state
                        space.overlay.show_ortho_grid = not state
        return {'FINISHED'}

class ZIONAD_OT_SetBrushSize(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_brush_size"
    bl_label = "太さ"
    size: IntProperty(default=10)
    def execute(self, context):
        ts = context.scene.tool_settings
        paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
        if paint and hasattr(paint, "brush") and paint.brush:
            try: paint.brush.size = self.size
            except: pass
        try: ts.unified_paint_settings.size = self.size
        except: pass
        return {'FINISHED'}

class ZIONAD_OT_ApplyColor(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.apply_color"
    bl_label = "色"
    color_index: IntProperty(default=1)
    def execute(self, context):
        obj = context.active_object
        if not is_gp_object(obj): return {'CANCELLED'}
        props = context.scene.zionad_props
        stroke_col = getattr(props, f"col_{self.color_index:02d}_stroke")
        fill_col = getattr(props, f"col_{self.color_index:02d}_fill")
        mat_name = f"GP_Mat_{self.color_index:02d}"
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(name=mat_name)
            try: bpy.data.materials.create_gpencil_data(mat)
            except: pass
        if hasattr(mat, "grease_pencil") and mat.grease_pencil:
            mat.grease_pencil.color = stroke_col
            mat.grease_pencil.fill_color = fill_col
        mat.diffuse_color = stroke_col
        found = False
        for i, slot_mat in enumerate(obj.data.materials):
            if slot_mat == mat:
                obj.active_material_index = i; found = True; break
        if not found:
            obj.data.materials.append(mat)
            obj.active_material_index = len(obj.data.materials) - 1
        for area in context.screen.areas:
            if area.type == 'VIEW_3D': area.tag_redraw()
        return {'FINISHED'}

class ZIONAD_OT_AddModifier(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.add_modifier"
    bl_label = "エフェクト追加"
    mod_type: StringProperty()
    def execute(self, context):
        obj = context.active_object
        if not is_gp_object(obj): return {'CANCELLED'}
        mods = getattr(obj, "modifiers", [])
        if self.mod_type == 'NOISE':
            m_name = "Boiling_Noise"
            if m_name not in mods:
                mod = mods.new(name=m_name, type='NODES')
                mod.node_group = create_noise_node_group()
        elif self.mod_type == 'THICKNESS':
            m_name = "Outline_Thickness"
            if m_name not in mods:
                mod = mods.new(name=m_name, type='NODES')
                mod.node_group = create_thickness_node_group()
        return {'FINISHED'}

class ZIONAD_OT_ResetModifiers(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.reset_modifiers"
    bl_label = "設定を初期値に戻す"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        obj = context.active_object
        if not is_gp_object(obj): return {'CANCELLED'}
        mods = getattr(obj, "modifiers", [])
        for m in mods:
            if m.type == 'NODES' and m.node_group:
                try:
                    if m.name == "Boiling_Noise":
                        id_name = [i.identifier for i in m.node_group.interface.items_tree if i.name == "Factor"][0]
                        m[id_name] = 0.05
                    elif m.name == "Outline_Thickness":
                        id_name = [i.identifier for i in m.node_group.interface.items_tree if i.name == "Add_Thickness"][0]
                        m[id_name] = 0.02
                except: pass
        self.report({'INFO'}, "エフェクトの数値を初期化しました")
        return {'FINISHED'}

class ZIONAD_OT_AddLayer(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.add_layer"
    bl_label = "新規レイヤー"
    def execute(self, context):
        obj = context.active_object
        if is_gp_object(obj) and hasattr(obj.data, "layers"):
            try:
                layer = obj.data.layers.new(name="Layer")
                obj.data.layers.active = layer
                if hasattr(layer, "frames"): layer.frames.new(context.scene.frame_current)
            except: pass
        return {'FINISHED'}

class ZIONAD_OT_SetLayerActive(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_layer_active"
    bl_label = "レイヤー選択"
    layer_name: StringProperty()
    def execute(self, context):
        obj = context.active_object
        if is_gp_object(obj) and hasattr(obj.data, "layers"):
            layer = obj.data.layers.get(self.layer_name)
            if layer: obj.data.layers.active = layer
        return {'FINISHED'}

class ZIONAD_OT_FinishDrawing(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.finish_drawing"
    bl_label = "⑥ ✓ オブジェクト完成"
    def execute(self, context):
        if not is_gp_object(context.active_object): return {'CANCELLED'}
        try: bpy.ops.object.mode_set(mode='OBJECT')
        except: pass
        try: getattr(bpy.ops, PREFIX_VAL.lower()).create_gp()
        except: pass
        return {'FINISHED'}

class ZIONAD_OT_EditPalette(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.edit_palette"
    bl_label = "🎨 パレットの色を編集"
    def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self, width=400)
    def draw(self, context):
        layout = self.layout
        props = context.scene.zionad_props
        layout.label(text="名前 / Stroke(線) / Fill(塗り)", icon='COLOR')
        for i in range(1, 13):
            row = layout.row(align=True)
            row.prop(props, f"col_{i:02d}_name", text="")
            row.prop(props, f"col_{i:02d}_stroke", text="")
            row.prop(props, f"col_{i:02d}_fill", text="")
    def execute(self, context): return {'FINISHED'}

class ZIONAD_OT_RemoveAllPanels(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.remove_all_panels"
    bl_label = "Unregister Addon"
    def execute(self, context): unregister(); return {'FINISHED'}

# =========================================================
# 4. パネル UI
# =========================================================
class ZIONAD_PT_Workflow(Panel):
    bl_label = "1. 描画ワークフロー"
    bl_idname = "ZIONAD_PT_workflow"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 0

    def draw(self, context):
        layout = self.layout
        props = context.scene.zionad_props
        
        # ① 新規作成
        col = layout.column(align=True)
        col.scale_y = 1.5
        col.operator(ZIONAD_OT_CreateGP.bl_idname, icon='ADD')
        layout.separator()
        
        # ② ツールとグリッド
        box_tool = layout.box()
        box_tool.label(text="② ツール & グリッド:", icon='TOOL_SETTINGS')
        row_tool = box_tool.row(align=True)
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="ペン", icon='GREASEPENCIL').tool_type = 'PEN'
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="鉛筆", icon='EDITMODE_HLT').tool_type = 'PENCIL'
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="マーカー", icon='BRUSH_DATA').tool_type = 'MARKER'
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="消しゴム", icon='TRASH').tool_type = 'ERASER'
        box_tool.operator(ZIONAD_OT_ToggleCanvas.bl_idname, text="マス目ガイド ON/OFF", icon='GRID')
        
        # ③ 太さ
        box_size = layout.box()
        box_size.label(text="③ ペンの太さ:", icon='BRUSH_DATA')
        r1 = box_size.row(align=True)
        r1.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="極細 (3)").size = 3
        r1.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="細 (10)").size = 10
        r1.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="中 (20)").size = 20
        r2 = box_size.row(align=True)
        r2.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="太 (30)").size = 30
        r2.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="極太 (50)").size = 50
        r2.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="特大 (100)").size = 100
        
        # ④ 色選択
        box_col = layout.box()
        r_col = box_col.row()
        r_col.label(text="④ 色を選ぶ:", icon='COLOR')
        r_col.operator(ZIONAD_OT_EditPalette.bl_idname, text="", icon='PREFERENCES')
        grid = box_col.grid_flow(columns=3, align=True)
        for i in range(1, 13):
            name = getattr(props, f"col_{i:02d}_name")
            c = grid.column(align=True)
            r = c.row(align=True)
            r.scale_x = 0.85
            r.prop(props, f"col_{i:02d}_stroke", text="")
            r.prop(props, f"col_{i:02d}_fill", text="")
            op = r.operator(ZIONAD_OT_ApplyColor.bl_idname, text=name)
            op.color_index = i

        # ⑤ レイヤー & エフェクト
        obj = context.active_object
        if is_gp_object(obj):
            box_layer = layout.box()
            row_head = box_layer.row()
            row_head.label(text="⑤ レイヤー:", icon='GROUP')
            row_head.operator(ZIONAD_OT_AddLayer.bl_idname, text="追加", icon='ADD')
            
            if hasattr(obj.data, "layers") and obj.data.layers:
                layers_list = []
                try: layers_list = list(obj.data.layers)
                except: pass
                for layer in reversed(layers_list):
                    row_layer = box_layer.row(align=True)
                    is_active = False
                    try: is_active = (obj.data.layers.active == layer)
                    except: pass
                    icon = 'RADIOBUT_ON' if is_active else 'RADIOBUT_OFF'
                    op_act = row_layer.operator(ZIONAD_OT_SetLayerActive.bl_idname, text=layer.name, icon=icon)
                    op_act.layer_name = layer.name
                    if hasattr(layer, "hide"): row_layer.prop(layer, "hide", text="", icon='HIDE_ON' if layer.hide else 'HIDE_OFF')
                    elif hasattr(layer, "hide_viewport"): row_layer.prop(layer, "hide_viewport", text="", icon='HIDE_ON' if layer.hide_viewport else 'HIDE_OFF')
                    if hasattr(layer, "lock"): row_layer.prop(layer, "lock", text="", icon='LOCKED' if layer.lock else 'UNLOCKED')
                    if hasattr(layer, "opacity"): row_layer.prop(layer, "opacity", text="", slider=True)
            else:
                box_layer.label(text="レイヤーなし")

            # エフェクト (ジオメトリノード)
            box_fx = layout.box()
            box_fx.label(text="★ エフェクト追加 (再生で動きます):", icon='MODIFIER')
            row_fx = box_fx.row(align=True)
            row_fx.operator(ZIONAD_OT_AddModifier.bl_idname, text="ボイリング(震え)").mod_type = 'NOISE'
            row_fx.operator(ZIONAD_OT_AddModifier.bl_idname, text="線を太くする").mod_type = 'THICKNESS'
            
            mods = getattr(obj, "modifiers", [])
            has_effect = False
            for m in mods:
                if m.type == 'NODES' and m.node_group:
                    if m.name in ["Boiling_Noise", "Outline_Thickness"]: has_effect = True
                    try:
                        if m.name == "Boiling_Noise":
                            id_name = [i.identifier for i in m.node_group.interface.items_tree if i.name == "Factor"][0]
                            if id_name not in m: m[id_name] = 0.05
                            # ラベルを -10〜10 に変更
                            box_fx.prop(m, f'["{id_name}"]', text="震えの強さ (-10〜10)", slider=True)
                        elif m.name == "Outline_Thickness":
                            id_name = [i.identifier for i in m.node_group.interface.items_tree if i.name == "Add_Thickness"][0]
                            if id_name not in m: m[id_name] = 0.02
                            box_fx.prop(m, f'["{id_name}"]', text="追加する太さ (0〜10)", slider=True)
                    except: pass
            
            if has_effect:
                box_fx.operator(ZIONAD_OT_ResetModifiers.bl_idname, text="設定を初期値に戻す", icon='FILE_REFRESH')

        layout.separator()
        col = layout.column(align=True)
        col.scale_y = 1.5
        col.operator(ZIONAD_OT_FinishDrawing.bl_idname, icon='CHECKMARK')

class ZIONAD_PT_Manager(Panel):
    bl_label = "2. オブジェクト管理"
    bl_idname = "ZIONAD_PT_manager"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 1
    bl_options = {'DEFAULT_CLOSED'}

    def draw(self, context):
        layout = self.layout
        box = layout.box()
        props = context.scene.zionad_props
        col = bpy.data.collections.get(props.col_name)
        if col:
            for obj in col.objects:
                if is_gp_object(obj):
                    row = box.row(align=True)
                    icon = 'RADIOBUT_ON' if context.active_object == obj else 'RADIOBUT_OFF'
                    row.prop(obj, "name", text="", icon=icon)
                    row.prop(obj, "hide_viewport", text="", icon='HIDE_ON' if obj.hide_viewport else 'HIDE_OFF')
                    row.prop(obj, "hide_select", text="", icon='RESTRICT_SELECT_ON' if obj.hide_select else 'UNLOCKED')
        else: box.label(text="まだ描画がありません")

class ZIONAD_PT_RemovePanel(Panel):
    bl_label = "3. アドオン削除"
    bl_idname = "ZIONAD_PT_remove"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 2
    bl_options = {'DEFAULT_CLOSED'}
    def draw(self, context):
        layout = self.layout
        layout.operator(ZIONAD_OT_RemoveAllPanels.bl_idname, icon='CANCEL')

# =========================================================
# 5. 登録・解除
# =========================================================
classes = (
    ZIONAD_Properties,
    ZIONAD_OT_CreateGP, ZIONAD_OT_SetBrushTool, ZIONAD_OT_ToggleCanvas,
    ZIONAD_OT_SetBrushSize, ZIONAD_OT_ApplyColor, ZIONAD_OT_AddModifier,
    ZIONAD_OT_ResetModifiers, ZIONAD_OT_AddLayer, ZIONAD_OT_SetLayerActive,
    ZIONAD_OT_FinishDrawing, ZIONAD_OT_EditPalette, ZIONAD_OT_RemoveAllPanels,
    ZIONAD_PT_Workflow, ZIONAD_PT_Manager, ZIONAD_PT_RemovePanel,
)

def register():
    for cls in classes: bpy.utils.register_class(cls)
    bpy.types.Scene.zionad_props = PointerProperty(type=ZIONAD_Properties)

def unregister():
    if hasattr(bpy.types.Scene, 'zionad_props'): del bpy.types.Scene.zionad_props
    for cls in reversed(classes):
        try: bpy.utils.unregister_class(cls)
        except: pass

if __name__ == "__main__": register()
import bpy
from bpy.types import Operator, Panel, PropertyGroup
from bpy.props import StringProperty, PointerProperty, FloatVectorProperty, IntProperty

# =========================================================
# アドオン宣言
# =========================================================
bl_info = {
    "name": "Grease Pencil Object Manager",
    "author": "AI",
    "version": (4, 3, 1),
    "blender": (5, 0, 0),
    "location": "View3D > Sidebar > GP Manager",
    "description": "GPオブジェクト作成 → ツール/太さ/色 → レイヤー → エフェクト → 完成",
    "category": "Grease Pencil",
}

PREFIX_VAL = "ZIONAD"
ADDON_TITLE = "GP Manager"
DEFAULT_COL_NAME = "GreaseObjects"
DEFAULT_BASE_NAME = "GP_"

# =========================================================
# 1. プロパティグループ (12色のカラー設定)
# =========================================================
class ZIONAD_Properties(PropertyGroup):
    col_name: StringProperty(name="主コレクション", default=DEFAULT_COL_NAME)
    obj_base_name: StringProperty(name="ベース名", default=DEFAULT_BASE_NAME)

    col_01_name: StringProperty(default="黒"); col_01_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.05, 0.05, 0.05, 1.0), min=0.0, max=1.0); col_01_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.3, 0.3, 0.3, 0.5), min=0.0, max=1.0)
    col_02_name: StringProperty(default="白"); col_02_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(1.0, 1.0, 1.0, 1.0), min=0.0, max=1.0); col_02_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.9, 0.9, 0.5), min=0.0, max=1.0)
    col_03_name: StringProperty(default="灰"); col_03_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.4, 0.4, 1.0), min=0.0, max=1.0); col_03_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.6, 0.6, 0.6, 0.5), min=0.0, max=1.0)
    col_04_name: StringProperty(default="赤"); col_04_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.8, 0.1, 0.1, 1.0), min=0.0, max=1.0); col_04_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.4, 0.4, 0.5), min=0.0, max=1.0)
    col_05_name: StringProperty(default="青"); col_05_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.1, 0.2, 0.8, 1.0), min=0.0, max=1.0); col_05_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.5, 0.9, 0.5), min=0.0, max=1.0)
    col_06_name: StringProperty(default="緑"); col_06_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.1, 0.7, 0.2, 1.0), min=0.0, max=1.0); col_06_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.9, 0.5, 0.5), min=0.0, max=1.0)
    col_07_name: StringProperty(default="黄"); col_07_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.8, 0.1, 1.0), min=0.0, max=1.0); col_07_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.9, 0.5, 0.5), min=0.0, max=1.0)
    col_08_name: StringProperty(default="水"); col_08_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.1, 0.8, 0.9, 1.0), min=0.0, max=1.0); col_08_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.5, 0.9, 0.9, 0.5), min=0.0, max=1.0)
    col_09_name: StringProperty(default="紫"); col_09_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.6, 0.1, 0.8, 1.0), min=0.0, max=1.0); col_09_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.8, 0.5, 0.9, 0.5), min=0.0, max=1.0)
    col_10_name: StringProperty(default="橙"); col_10_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.4, 0.1, 1.0), min=0.0, max=1.0); col_10_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.6, 0.4, 0.5), min=0.0, max=1.0)
    col_11_name: StringProperty(default="茶"); col_11_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.2, 0.1, 1.0), min=0.0, max=1.0); col_11_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.6, 0.4, 0.3, 0.5), min=0.0, max=1.0)
    col_12_name: StringProperty(default="桃"); col_12_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.3, 0.6, 1.0), min=0.0, max=1.0); col_12_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.6, 0.8, 0.5), min=0.0, max=1.0)

# =========================================================
# 2. ユーティリティ (ジオメトリノード自動生成)
# =========================================================
def is_gp_object(obj):
    return obj and obj.type in {'GPENCIL', 'GREASEPENCIL'}

def get_next_name(base_name):
    max_num = 0
    for obj in bpy.data.objects:
        if is_gp_object(obj) and obj.name.startswith(base_name):
            try: max_num = max(max_num, int(obj.name.replace(base_name, "")))
            except: pass
    return f"{base_name}{max_num + 1:03d}"

# ★ ボイリング(震え) ノードツリー生成
def create_noise_node_group():
    name = "GP_Boiling_Noise_FX"
    if name in bpy.data.node_groups: return bpy.data.node_groups[name]
    
    ng = bpy.data.node_groups.new(name, 'GeometryNodeTree')
    ng.interface.new_socket("Geometry", in_out='INPUT', socket_type='NodeSocketGeometry')
    
    # 【制限追加】震えの強さのパラメータ設定
    sock_fac = ng.interface.new_socket("Factor", in_out='INPUT', socket_type='NodeSocketFloat')
    sock_fac.default_value = 0.05
    sock_fac.min_value = 0.0   # マイナス禁止
    sock_fac.max_value = 10.0  # 最大10まで
    
    ng.interface.new_socket("Geometry", in_out='OUTPUT', socket_type='NodeSocketGeometry')
    
    node_in = ng.nodes.new('NodeGroupInput')
    node_out = ng.nodes.new('NodeGroupOutput')
    
    noise = ng.nodes.new('ShaderNodeTexNoise'); noise.noise_dimensions = '4D'
    time = ng.nodes.new('GeometryNodeInputSceneTime')
    math_mul = ng.nodes.new('ShaderNodeMath'); math_mul.operation = 'MULTIPLY'; math_mul.inputs[1].default_value = 15.0
    vmath_sub = ng.nodes.new('ShaderNodeVectorMath'); vmath_sub.operation = 'SUBTRACT'; vmath_sub.inputs[1].default_value = (0.5, 0.5, 0.5)
    vmath_scale = ng.nodes.new('ShaderNodeVectorMath'); vmath_scale.operation = 'SCALE'
    set_pos = ng.nodes.new('GeometryNodeSetPosition')
    
    ng.links.new(time.outputs['Seconds'], math_mul.inputs[0])
    ng.links.new(math_mul.outputs['Value'], noise.inputs['W'])
    ng.links.new(noise.outputs['Color'], vmath_sub.inputs[0])
    ng.links.new(vmath_sub.outputs['Vector'], vmath_scale.inputs[0])
    ng.links.new(node_in.outputs['Factor'], vmath_scale.inputs['Scale'])
    ng.links.new(node_in.outputs['Geometry'], set_pos.inputs['Geometry'])
    ng.links.new(vmath_scale.outputs['Vector'], set_pos.inputs['Offset'])
    ng.links.new(set_pos.outputs['Geometry'], node_out.inputs['Geometry'])
    return ng

# ★ 太さ追加 ノードツリー生成
def create_thickness_node_group():
    name = "GP_Outline_Thickness_FX"
    if name in bpy.data.node_groups: return bpy.data.node_groups[name]
    
    ng = bpy.data.node_groups.new(name, 'GeometryNodeTree')
    ng.interface.new_socket("Geometry", in_out='INPUT', socket_type='NodeSocketGeometry')
    
    # 【制限追加】太さのパラメータ設定
    sock_thick = ng.interface.new_socket("Add_Thickness", in_out='INPUT', socket_type='NodeSocketFloat')
    sock_thick.default_value = 0.02
    sock_thick.min_value = 0.0   # マイナス禁止
    sock_thick.max_value = 10.0  # 最大10まで
    
    ng.interface.new_socket("Geometry", in_out='OUTPUT', socket_type='NodeSocketGeometry')
    
    node_in = ng.nodes.new('NodeGroupInput')
    node_out = ng.nodes.new('NodeGroupOutput')
    
    rad_in = ng.nodes.new('GeometryNodeInputRadius')
    math_add = ng.nodes.new('ShaderNodeMath'); math_add.operation = 'ADD'
    set_rad = ng.nodes.new('GeometryNodeSetCurveRadius')
    
    ng.links.new(rad_in.outputs['Radius'], math_add.inputs[0])
    ng.links.new(node_in.outputs['Add_Thickness'], math_add.inputs[1])
    ng.links.new(node_in.outputs['Geometry'], set_rad.inputs['Curve'])
    ng.links.new(math_add.outputs['Value'], set_rad.inputs['Radius'])
    ng.links.new(set_rad.outputs['Curve'], node_out.inputs['Geometry'])
    return ng

# =========================================================
# 3. オペレーター群
# =========================================================
class ZIONAD_OT_CreateGP(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.create_gp"
    bl_label = "① + 新規 GPオブジェクトを作成"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        props = context.scene.zionad_props
        curr_frame = context.scene.frame_current
        if context.mode != 'OBJECT':
            try: bpy.ops.object.mode_set(mode='OBJECT')
            except: pass
        col = bpy.data.collections.get(props.col_name)
        if not col:
            col = bpy.data.collections.new(props.col_name)
            context.scene.collection.children.link(col)
        try: bpy.ops.object.grease_pencil_add(type='EMPTY')
        except: bpy.ops.object.gpencil_add(type='EMPTY')
        gp = context.active_object
        gp.name = get_next_name(props.obj_base_name)
        for c in gp.users_collection: c.objects.unlink(gp)
        col.objects.link(gp)

        gp_data = gp.data
        if hasattr(gp_data, "layers"):
            try:
                if len(gp_data.layers) == 0:
                    layer_fill = gp_data.layers.new(name="Fill")
                    if hasattr(layer_fill, "frames"): layer_fill.frames.new(curr_frame)
                    layer_line = gp_data.layers.new(name="Line")
                    if hasattr(layer_line, "frames"): layer_line.frames.new(curr_frame)
                    gp_data.layers.active = layer_line
            except: pass
        mat_name = "GP_Mat_01"
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(name=mat_name)
            try: bpy.data.materials.create_gpencil_data(mat)
            except: pass
        if hasattr(mat, "grease_pencil") and mat.grease_pencil:
            mat.grease_pencil.color = props.col_01_stroke
            mat.grease_pencil.fill_color = props.col_01_fill
        mat.diffuse_color = props.col_01_stroke
        gp.data.materials.append(mat)
        gp.active_material_index = 0
        try: bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
        except: 
            try: bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
            except: pass
        try: bpy.ops.grease_pencil.insert_blank_frame()
        except: pass
        try: bpy.ops.gpencil.blank_frame_add()
        except: pass
        self.report({'INFO'}, f"'{gp.name}' を作成しました。")
        return {'FINISHED'}

class ZIONAD_OT_SetBrushTool(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_brush_tool"
    bl_label = "ツール変更"
    tool_type: StringProperty()
    def execute(self, context):
        if context.mode not in {'PAINT_GPENCIL', 'PAINT_GREASE_PENCIL'}:
            try: bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
            except: pass
        if self.tool_type == 'ERASER':
            try: bpy.ops.wm.tool_set_by_id(name="builtin_brush.Erase")
            except: pass
        else:
            try: bpy.ops.wm.tool_set_by_id(name="builtin_brush.Draw")
            except: pass
            ts = context.scene.tool_settings
            paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
            if paint:
                t_name = {"PEN":"Pen", "PENCIL":"Pencil", "MARKER":"Marker"}.get(self.tool_type, "Pen")
                for b in bpy.data.brushes:
                    if t_name.lower() in b.name.lower():
                        try: paint.brush = b
                        except:
                            try: bpy.ops.brush.asset_activate(asset_id=b.name)
                            except: pass
                        break
        return {'FINISHED'}

class ZIONAD_OT_ToggleCanvas(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.toggle_canvas"
    bl_label = "グリッド表示"
    def execute(self, context):
        for area in context.screen.areas:
            if area.type == 'VIEW_3D':
                for space in area.spaces:
                    if space.type == 'VIEW_3D':
                        state = space.overlay.show_floor
                        space.overlay.show_floor = not state
                        space.overlay.show_ortho_grid = not state
        return {'FINISHED'}

class ZIONAD_OT_SetBrushSize(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_brush_size"
    bl_label = "太さ"
    size: IntProperty(default=10)
    def execute(self, context):
        ts = context.scene.tool_settings
        paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
        if paint and hasattr(paint, "brush") and paint.brush:
            try: paint.brush.size = self.size
            except: pass
        try: ts.unified_paint_settings.size = self.size
        except: pass
        return {'FINISHED'}

class ZIONAD_OT_ApplyColor(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.apply_color"
    bl_label = "色"
    color_index: IntProperty(default=1)
    def execute(self, context):
        obj = context.active_object
        if not is_gp_object(obj): return {'CANCELLED'}
        props = context.scene.zionad_props
        stroke_col = getattr(props, f"col_{self.color_index:02d}_stroke")
        fill_col = getattr(props, f"col_{self.color_index:02d}_fill")
        mat_name = f"GP_Mat_{self.color_index:02d}"
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(name=mat_name)
            try: bpy.data.materials.create_gpencil_data(mat)
            except: pass
        if hasattr(mat, "grease_pencil") and mat.grease_pencil:
            mat.grease_pencil.color = stroke_col
            mat.grease_pencil.fill_color = fill_col
        mat.diffuse_color = stroke_col
        found = False
        for i, slot_mat in enumerate(obj.data.materials):
            if slot_mat == mat:
                obj.active_material_index = i; found = True; break
        if not found:
            obj.data.materials.append(mat)
            obj.active_material_index = len(obj.data.materials) - 1
        for area in context.screen.areas:
            if area.type == 'VIEW_3D': area.tag_redraw()
        return {'FINISHED'}

class ZIONAD_OT_AddModifier(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.add_modifier"
    bl_label = "エフェクト追加"
    mod_type: StringProperty()
    def execute(self, context):
        obj = context.active_object
        if not is_gp_object(obj): return {'CANCELLED'}
        mods = getattr(obj, "modifiers", [])
        if self.mod_type == 'NOISE':
            m_name = "Boiling_Noise"
            if m_name not in mods:
                mod = mods.new(name=m_name, type='NODES')
                mod.node_group = create_noise_node_group()
        elif self.mod_type == 'THICKNESS':
            m_name = "Outline_Thickness"
            if m_name not in mods:
                mod = mods.new(name=m_name, type='NODES')
                mod.node_group = create_thickness_node_group()
        return {'FINISHED'}

# ★ 【新規追加】設定を初期値に戻すボタン処理
class ZIONAD_OT_ResetModifiers(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.reset_modifiers"
    bl_label = "設定を初期値に戻す"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        obj = context.active_object
        if not is_gp_object(obj): return {'CANCELLED'}
        mods = getattr(obj, "modifiers", [])
        for m in mods:
            if m.type == 'NODES' and m.node_group:
                try:
                    if m.name == "Boiling_Noise":
                        id_name = [i.identifier for i in m.node_group.interface.items_tree if i.name == "Factor"][0]
                        m[id_name] = 0.05
                    elif m.name == "Outline_Thickness":
                        id_name = [i.identifier for i in m.node_group.interface.items_tree if i.name == "Add_Thickness"][0]
                        m[id_name] = 0.02
                except: pass
        self.report({'INFO'}, "エフェクトの数値を初期化しました")
        return {'FINISHED'}

class ZIONAD_OT_AddLayer(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.add_layer"
    bl_label = "新規レイヤー"
    def execute(self, context):
        obj = context.active_object
        if is_gp_object(obj) and hasattr(obj.data, "layers"):
            try:
                layer = obj.data.layers.new(name="Layer")
                obj.data.layers.active = layer
                if hasattr(layer, "frames"): layer.frames.new(context.scene.frame_current)
            except: pass
        return {'FINISHED'}

class ZIONAD_OT_SetLayerActive(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_layer_active"
    bl_label = "レイヤー選択"
    layer_name: StringProperty()
    def execute(self, context):
        obj = context.active_object
        if is_gp_object(obj) and hasattr(obj.data, "layers"):
            layer = obj.data.layers.get(self.layer_name)
            if layer: obj.data.layers.active = layer
        return {'FINISHED'}

class ZIONAD_OT_FinishDrawing(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.finish_drawing"
    bl_label = "⑥ ✓ オブジェクト完成"
    def execute(self, context):
        if not is_gp_object(context.active_object): return {'CANCELLED'}
        try: bpy.ops.object.mode_set(mode='OBJECT')
        except: pass
        try: getattr(bpy.ops, PREFIX_VAL.lower()).create_gp()
        except: pass
        return {'FINISHED'}

class ZIONAD_OT_EditPalette(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.edit_palette"
    bl_label = "🎨 パレットの色を編集"
    def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self, width=400)
    def draw(self, context):
        layout = self.layout
        props = context.scene.zionad_props
        layout.label(text="名前 / Stroke(線) / Fill(塗り)", icon='COLOR')
        for i in range(1, 13):
            row = layout.row(align=True)
            row.prop(props, f"col_{i:02d}_name", text="")
            row.prop(props, f"col_{i:02d}_stroke", text="")
            row.prop(props, f"col_{i:02d}_fill", text="")
    def execute(self, context): return {'FINISHED'}

class ZIONAD_OT_RemoveAllPanels(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.remove_all_panels"
    bl_label = "Unregister Addon"
    def execute(self, context): unregister(); return {'FINISHED'}

# =========================================================
# 4. パネル UI
# =========================================================
class ZIONAD_PT_Workflow(Panel):
    bl_label = "1. 描画ワークフロー"
    bl_idname = "ZIONAD_PT_workflow"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 0

    def draw(self, context):
        layout = self.layout
        props = context.scene.zionad_props
        
        # ① 新規作成
        col = layout.column(align=True)
        col.scale_y = 1.5
        col.operator(ZIONAD_OT_CreateGP.bl_idname, icon='ADD')
        layout.separator()
        
        # ② ツールとグリッド
        box_tool = layout.box()
        box_tool.label(text="② ツール & グリッド:", icon='TOOL_SETTINGS')
        row_tool = box_tool.row(align=True)
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="ペン", icon='GREASEPENCIL').tool_type = 'PEN'
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="鉛筆", icon='EDITMODE_HLT').tool_type = 'PENCIL'
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="マーカー", icon='BRUSH_DATA').tool_type = 'MARKER'
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="消しゴム", icon='TRASH').tool_type = 'ERASER'
        box_tool.operator(ZIONAD_OT_ToggleCanvas.bl_idname, text="マス目ガイド ON/OFF", icon='GRID')
        
        # ③ 太さ
        box_size = layout.box()
        box_size.label(text="③ ペンの太さ:", icon='BRUSH_DATA')
        r1 = box_size.row(align=True)
        r1.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="極細 (3)").size = 3
        r1.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="細 (10)").size = 10
        r1.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="中 (20)").size = 20
        r2 = box_size.row(align=True)
        r2.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="太 (30)").size = 30
        r2.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="極太 (50)").size = 50
        r2.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="特大 (100)").size = 100
        
        # ④ 色選択
        box_col = layout.box()
        r_col = box_col.row()
        r_col.label(text="④ 色を選ぶ:", icon='COLOR')
        r_col.operator(ZIONAD_OT_EditPalette.bl_idname, text="", icon='PREFERENCES')
        grid = box_col.grid_flow(columns=3, align=True)
        for i in range(1, 13):
            name = getattr(props, f"col_{i:02d}_name")
            c = grid.column(align=True)
            r = c.row(align=True)
            r.scale_x = 0.85
            r.prop(props, f"col_{i:02d}_stroke", text="")
            r.prop(props, f"col_{i:02d}_fill", text="")
            op = r.operator(ZIONAD_OT_ApplyColor.bl_idname, text=name)
            op.color_index = i

        # ⑤ レイヤー & エフェクト
        obj = context.active_object
        if is_gp_object(obj):
            box_layer = layout.box()
            row_head = box_layer.row()
            row_head.label(text="⑤ レイヤー:", icon='GROUP')
            row_head.operator(ZIONAD_OT_AddLayer.bl_idname, text="追加", icon='ADD')
            
            if hasattr(obj.data, "layers") and obj.data.layers:
                layers_list = []
                try: layers_list = list(obj.data.layers)
                except: pass
                for layer in reversed(layers_list):
                    row_layer = box_layer.row(align=True)
                    is_active = False
                    try: is_active = (obj.data.layers.active == layer)
                    except: pass
                    icon = 'RADIOBUT_ON' if is_active else 'RADIOBUT_OFF'
                    op_act = row_layer.operator(ZIONAD_OT_SetLayerActive.bl_idname, text=layer.name, icon=icon)
                    op_act.layer_name = layer.name
                    if hasattr(layer, "hide"): row_layer.prop(layer, "hide", text="", icon='HIDE_ON' if layer.hide else 'HIDE_OFF')
                    elif hasattr(layer, "hide_viewport"): row_layer.prop(layer, "hide_viewport", text="", icon='HIDE_ON' if layer.hide_viewport else 'HIDE_OFF')
                    if hasattr(layer, "lock"): row_layer.prop(layer, "lock", text="", icon='LOCKED' if layer.lock else 'UNLOCKED')
                    if hasattr(layer, "opacity"): row_layer.prop(layer, "opacity", text="", slider=True)
            else:
                box_layer.label(text="レイヤーなし")

            # エフェクト (ジオメトリノード)
            box_fx = layout.box()
            box_fx.label(text="★ エフェクト追加 (再生で動きます):", icon='MODIFIER')
            row_fx = box_fx.row(align=True)
            row_fx.operator(ZIONAD_OT_AddModifier.bl_idname, text="ボイリング(震え)").mod_type = 'NOISE'
            row_fx.operator(ZIONAD_OT_AddModifier.bl_idname, text="線を太くする").mod_type = 'THICKNESS'
            
            mods = getattr(obj, "modifiers", [])
            has_effect = False
            for m in mods:
                if m.type == 'NODES' and m.node_group:
                    if m.name in ["Boiling_Noise", "Outline_Thickness"]: has_effect = True
                    try:
                        if m.name == "Boiling_Noise":
                            id_name = [i.identifier for i in m.node_group.interface.items_tree if i.name == "Factor"][0]
                            if id_name not in m: m[id_name] = 0.05
                            box_fx.prop(m, f'["{id_name}"]', text="震えの強さ (0〜10)", slider=True)
                        elif m.name == "Outline_Thickness":
                            id_name = [i.identifier for i in m.node_group.interface.items_tree if i.name == "Add_Thickness"][0]
                            if id_name not in m: m[id_name] = 0.02
                            box_fx.prop(m, f'["{id_name}"]', text="追加する太さ (0〜10)", slider=True)
                    except: pass
            
            # 初期化ボタンの表示
            if has_effect:
                box_fx.operator(ZIONAD_OT_ResetModifiers.bl_idname, text="設定を初期値に戻す", icon='FILE_REFRESH')

        layout.separator()
        col = layout.column(align=True)
        col.scale_y = 1.5
        col.operator(ZIONAD_OT_FinishDrawing.bl_idname, icon='CHECKMARK')

class ZIONAD_PT_Manager(Panel):
    bl_label = "2. オブジェクト管理"
    bl_idname = "ZIONAD_PT_manager"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 1
    bl_options = {'DEFAULT_CLOSED'}

    def draw(self, context):
        layout = self.layout
        box = layout.box()
        props = context.scene.zionad_props
        col = bpy.data.collections.get(props.col_name)
        if col:
            for obj in col.objects:
                if is_gp_object(obj):
                    row = box.row(align=True)
                    icon = 'RADIOBUT_ON' if context.active_object == obj else 'RADIOBUT_OFF'
                    row.prop(obj, "name", text="", icon=icon)
                    row.prop(obj, "hide_viewport", text="", icon='HIDE_ON' if obj.hide_viewport else 'HIDE_OFF')
                    row.prop(obj, "hide_select", text="", icon='RESTRICT_SELECT_ON' if obj.hide_select else 'UNLOCKED')
        else: box.label(text="まだ描画がありません")

class ZIONAD_PT_RemovePanel(Panel):
    bl_label = "3. アドオン削除"
    bl_idname = "ZIONAD_PT_remove"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 2
    bl_options = {'DEFAULT_CLOSED'}
    def draw(self, context):
        layout = self.layout
        layout.operator(ZIONAD_OT_RemoveAllPanels.bl_idname, icon='CANCEL')

# =========================================================
# 5. 登録・解除
# =========================================================
classes = (
    ZIONAD_Properties,
    ZIONAD_OT_CreateGP, ZIONAD_OT_SetBrushTool, ZIONAD_OT_ToggleCanvas,
    ZIONAD_OT_SetBrushSize, ZIONAD_OT_ApplyColor, ZIONAD_OT_AddModifier,
    ZIONAD_OT_ResetModifiers, ZIONAD_OT_AddLayer, ZIONAD_OT_SetLayerActive,
    ZIONAD_OT_FinishDrawing, ZIONAD_OT_EditPalette, ZIONAD_OT_RemoveAllPanels,
    ZIONAD_PT_Workflow, ZIONAD_PT_Manager, ZIONAD_PT_RemovePanel,
)

def register():
    for cls in classes: bpy.utils.register_class(cls)
    bpy.types.Scene.zionad_props = PointerProperty(type=ZIONAD_Properties)

def unregister():
    if hasattr(bpy.types.Scene, 'zionad_props'): del bpy.types.Scene.zionad_props
    for cls in reversed(classes):
        try: bpy.utils.unregister_class(cls)
        except: pass

if __name__ == "__main__": register()

import bpy
from bpy.types import Operator, Panel, PropertyGroup
from bpy.props import StringProperty, PointerProperty, FloatVectorProperty, IntProperty

# =========================================================
# アドオン宣言
# =========================================================
bl_info = {
    "name": "Grease Pencil Object Manager",
    "author": "AI",
    "version": (4, 2, 1),
    "blender": (5, 0, 0),
    "location": "View3D > Sidebar > GP Manager",
    "description": "GPオブジェクト作成 → ツール/太さ/色 → レイヤー → エフェクト → 完成",
    "category": "Grease Pencil",
}

PREFIX_VAL = "ZIONAD"
ADDON_TITLE = "GP Manager"
DEFAULT_COL_NAME = "GreaseObjects"
DEFAULT_BASE_NAME = "GP_"

# =========================================================
# 1. プロパティグループ (12色のカラー設定)
# =========================================================
class ZIONAD_Properties(PropertyGroup):
    col_name: StringProperty(name="主コレクション", default=DEFAULT_COL_NAME)
    obj_base_name: StringProperty(name="ベース名", default=DEFAULT_BASE_NAME)

    col_01_name: StringProperty(default="黒"); col_01_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.05, 0.05, 0.05, 1.0), min=0.0, max=1.0); col_01_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.3, 0.3, 0.3, 0.5), min=0.0, max=1.0)
    col_02_name: StringProperty(default="白"); col_02_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(1.0, 1.0, 1.0, 1.0), min=0.0, max=1.0); col_02_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.9, 0.9, 0.5), min=0.0, max=1.0)
    col_03_name: StringProperty(default="灰"); col_03_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.4, 0.4, 1.0), min=0.0, max=1.0); col_03_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.6, 0.6, 0.6, 0.5), min=0.0, max=1.0)
    col_04_name: StringProperty(default="赤"); col_04_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.8, 0.1, 0.1, 1.0), min=0.0, max=1.0); col_04_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.4, 0.4, 0.5), min=0.0, max=1.0)
    col_05_name: StringProperty(default="青"); col_05_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.1, 0.2, 0.8, 1.0), min=0.0, max=1.0); col_05_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.5, 0.9, 0.5), min=0.0, max=1.0)
    col_06_name: StringProperty(default="緑"); col_06_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.1, 0.7, 0.2, 1.0), min=0.0, max=1.0); col_06_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.9, 0.5, 0.5), min=0.0, max=1.0)
    col_07_name: StringProperty(default="黄"); col_07_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.8, 0.1, 1.0), min=0.0, max=1.0); col_07_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.9, 0.5, 0.5), min=0.0, max=1.0)
    col_08_name: StringProperty(default="水"); col_08_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.1, 0.8, 0.9, 1.0), min=0.0, max=1.0); col_08_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.5, 0.9, 0.9, 0.5), min=0.0, max=1.0)
    col_09_name: StringProperty(default="紫"); col_09_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.6, 0.1, 0.8, 1.0), min=0.0, max=1.0); col_09_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.8, 0.5, 0.9, 0.5), min=0.0, max=1.0)
    col_10_name: StringProperty(default="橙"); col_10_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.4, 0.1, 1.0), min=0.0, max=1.0); col_10_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.6, 0.4, 0.5), min=0.0, max=1.0)
    col_11_name: StringProperty(default="茶"); col_11_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.4, 0.2, 0.1, 1.0), min=0.0, max=1.0); col_11_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.6, 0.4, 0.3, 0.5), min=0.0, max=1.0)
    col_12_name: StringProperty(default="桃"); col_12_stroke: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.3, 0.6, 1.0), min=0.0, max=1.0); col_12_fill: FloatVectorProperty(subtype='COLOR', size=4, default=(0.9, 0.6, 0.8, 0.5), min=0.0, max=1.0)

# =========================================================
# 2. ユーティリティ
# =========================================================
def is_gp_object(obj):
    return obj and obj.type in {'GPENCIL', 'GREASEPENCIL'}

def get_next_name(base_name):
    max_num = 0
    for obj in bpy.data.objects:
        if is_gp_object(obj) and obj.name.startswith(base_name):
            try: max_num = max(max_num, int(obj.name.replace(base_name, "")))
            except: pass
    return f"{base_name}{max_num + 1:03d}"

# =========================================================
# 3. オペレーター群
# =========================================================
class ZIONAD_OT_CreateGP(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.create_gp"
    bl_label = "① + 新規 GPオブジェクトを作成"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        props = context.scene.zionad_props
        curr_frame = context.scene.frame_current
        
        if context.mode != 'OBJECT':
            try: bpy.ops.object.mode_set(mode='OBJECT')
            except: pass
        
        col = bpy.data.collections.get(props.col_name)
        if not col:
            col = bpy.data.collections.new(props.col_name)
            context.scene.collection.children.link(col)
            
        try: bpy.ops.object.grease_pencil_add(type='EMPTY')
        except: bpy.ops.object.gpencil_add(type='EMPTY')
        
        gp = context.active_object
        gp.name = get_next_name(props.obj_base_name)
        for c in gp.users_collection: c.objects.unlink(gp)
        col.objects.link(gp)

        gp_data = gp.data
        if hasattr(gp_data, "layers"):
            try:
                if len(gp_data.layers) == 0:
                    layer_fill = gp_data.layers.new(name="Fill")
                    if hasattr(layer_fill, "frames"): layer_fill.frames.new(curr_frame)
                    layer_line = gp_data.layers.new(name="Line")
                    if hasattr(layer_line, "frames"): layer_line.frames.new(curr_frame)
                    gp_data.layers.active = layer_line
            except: pass
        
        mat_name = "GP_Mat_01"
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(name=mat_name)
            try: bpy.data.materials.create_gpencil_data(mat)
            except: pass
        
        if hasattr(mat, "grease_pencil") and mat.grease_pencil:
            mat.grease_pencil.color = props.col_01_stroke
            mat.grease_pencil.fill_color = props.col_01_fill
            mat.grease_pencil.show_stroke = True
            mat.grease_pencil.show_fill = True
            
        mat.diffuse_color = props.col_01_stroke
        gp.data.materials.append(mat)
        gp.active_material_index = 0
        
        try: bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
        except: 
            try: bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
            except: pass
            
        try: bpy.ops.grease_pencil.insert_blank_frame()
        except: pass
        try: bpy.ops.gpencil.blank_frame_add()
        except: pass
            
        ts = context.scene.tool_settings
        paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
        if paint and hasattr(paint, "brush") and paint.brush:
            try: paint.brush.use_material = True
            except: pass
            try: paint.brush.color_mode = 'MATERIAL'
            except: pass
        
        self.report({'INFO'}, f"'{gp.name}' を作成しました。")
        return {'FINISHED'}

class ZIONAD_OT_SetBrushTool(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_brush_tool"
    bl_label = "ツール変更"
    tool_type: StringProperty()
    
    def execute(self, context):
        if context.mode not in {'PAINT_GPENCIL', 'PAINT_GREASE_PENCIL'}:
            try: bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
            except: 
                try: bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
                except: pass
                
        if self.tool_type == 'ERASER':
            try: bpy.ops.wm.tool_set_by_id(name="builtin_brush.Erase")
            except: pass
        else:
            try: bpy.ops.wm.tool_set_by_id(name="builtin_brush.Draw")
            except: pass
            
            ts = context.scene.tool_settings
            paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
            if paint:
                t_name = {"PEN":"Pen", "PENCIL":"Pencil", "MARKER":"Marker"}.get(self.tool_type, "Pen")
                for b in bpy.data.brushes:
                    if t_name.lower() in b.name.lower():
                        # 【修正箇所】 Blender 4.3以降での Read-only エラーを回避
                        try:
                            paint.brush = b
                        except AttributeError:
                            # アセットブラシのアクティベートによるフォールバック
                            try: bpy.ops.brush.asset_activate(asset_id=b.name)
                            except: pass
                        except Exception:
                            pass
                        break
        return {'FINISHED'}

class ZIONAD_OT_ToggleCanvas(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.toggle_canvas"
    bl_label = "グリッド表示"
    
    def execute(self, context):
        for area in context.screen.areas:
            if area.type == 'VIEW_3D':
                for space in area.spaces:
                    if space.type == 'VIEW_3D':
                        if hasattr(space.overlay, "show_gpencil_canvas"):
                            space.overlay.show_gpencil_canvas = not space.overlay.show_gpencil_canvas
                        elif hasattr(space.overlay, "show_canvas"):
                            space.overlay.show_canvas = not space.overlay.show_canvas
        return {'FINISHED'}

class ZIONAD_OT_SetBrushSize(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_brush_size"
    bl_label = "太さ"
    size: IntProperty(default=10)
    
    def execute(self, context):
        ts = context.scene.tool_settings
        paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
        if paint and hasattr(paint, "brush") and paint.brush:
            try: paint.brush.size = self.size
            except: pass
            
        # Unified Paint 設定へのフォールバック
        try: ts.unified_paint_settings.size = self.size
        except: pass
        
        return {'FINISHED'}

class ZIONAD_OT_ApplyColor(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.apply_color"
    bl_label = "色"
    color_index: IntProperty(default=1)
    
    def execute(self, context):
        obj = context.active_object
        if not is_gp_object(obj): return {'CANCELLED'}
        props = context.scene.zionad_props
        stroke_col = getattr(props, f"col_{self.color_index:02d}_stroke")
        fill_col = getattr(props, f"col_{self.color_index:02d}_fill")
        mat_name = f"GP_Mat_{self.color_index:02d}"
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(name=mat_name)
            try: bpy.data.materials.create_gpencil_data(mat)
            except: pass
        if hasattr(mat, "grease_pencil") and mat.grease_pencil:
            mat.grease_pencil.color = stroke_col
            mat.grease_pencil.fill_color = fill_col
            mat.grease_pencil.show_stroke = True
            mat.grease_pencil.show_fill = True
        mat.diffuse_color = stroke_col
        found = False
        for i, slot_mat in enumerate(obj.data.materials):
            if slot_mat == mat:
                obj.active_material_index = i
                found = True
                break
        if not found:
            obj.data.materials.append(mat)
            obj.active_material_index = len(obj.data.materials) - 1
            
        ts = context.scene.tool_settings
        paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
        if paint and hasattr(paint, "brush") and paint.brush:
            try: paint.brush.use_material = True; paint.brush.color_mode = 'MATERIAL'
            except: pass
        for area in context.screen.areas:
            if area.type == 'VIEW_3D': area.tag_redraw()
        return {'FINISHED'}

class ZIONAD_OT_AddModifier(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.add_modifier"
    bl_label = "エフェクト追加"
    mod_type: StringProperty()
    
    def execute(self, context):
        obj = context.active_object
        if not is_gp_object(obj): return {'CANCELLED'}
        
        m_name = "Boiling_Noise" if self.mod_type == 'NOISE' else "Outline_Thickness"
        m_type_2 = 'GP_NOISE' if self.mod_type == 'NOISE' else 'GP_THICKNESS'
        m_type_3 = 'GREASE_PENCIL_NOISE' if self.mod_type == 'NOISE' else 'GREASE_PENCIL_THICKNESS'
        
        mod = None
        mods = getattr(obj, "grease_pencil_modifiers", getattr(obj, "modifiers", None))
        if mods:
            mod = mods.get(m_name)
            if not mod:
                try: mod = mods.new(name=m_name, type=m_type_3)
                except:
                    try: mod = mods.new(name=m_name, type=m_type_2)
                    except: pass
        return {'FINISHED'}

class ZIONAD_OT_AddLayer(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.add_layer"
    bl_label = "新規レイヤー"
    def execute(self, context):
        obj = context.active_object
        if is_gp_object(obj) and hasattr(obj.data, "layers"):
            try:
                layer = obj.data.layers.new(name="Layer")
                obj.data.layers.active = layer
                if hasattr(layer, "frames"): layer.frames.new(context.scene.frame_current)
            except: pass
        return {'FINISHED'}

class ZIONAD_OT_SetLayerActive(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_layer_active"
    bl_label = "レイヤー選択"
    layer_name: StringProperty()
    def execute(self, context):
        obj = context.active_object
        if is_gp_object(obj) and hasattr(obj.data, "layers"):
            layer = obj.data.layers.get(self.layer_name)
            if layer: obj.data.layers.active = layer
        return {'FINISHED'}

class ZIONAD_OT_FinishDrawing(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.finish_drawing"
    bl_label = "⑥ ✓ オブジェクト完成"
    def execute(self, context):
        if not is_gp_object(context.active_object): return {'CANCELLED'}
        try: bpy.ops.object.mode_set(mode='OBJECT')
        except: pass
        try: getattr(bpy.ops, PREFIX_VAL.lower()).create_gp()
        except: pass
        return {'FINISHED'}

class ZIONAD_OT_EditPalette(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.edit_palette"
    bl_label = "🎨 パレットの色を編集"
    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self, width=400)
    def draw(self, context):
        layout = self.layout
        props = context.scene.zionad_props
        layout.label(text="名前 / Stroke(線) / Fill(塗り)", icon='COLOR')
        for i in range(1, 13):
            row = layout.row(align=True)
            row.prop(props, f"col_{i:02d}_name", text="")
            row.prop(props, f"col_{i:02d}_stroke", text="")
            row.prop(props, f"col_{i:02d}_fill", text="")
    def execute(self, context): return {'FINISHED'}

class ZIONAD_OT_RemoveAllPanels(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.remove_all_panels"
    bl_label = "Unregister Addon"
    def execute(self, context): 
        unregister() 
        return {'FINISHED'}

# =========================================================
# 4. パネル UI
# =========================================================
class ZIONAD_PT_Workflow(Panel):
    bl_label = "1. 描画ワークフロー"
    bl_idname = "ZIONAD_PT_workflow"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 0

    def draw(self, context):
        layout = self.layout
        props = context.scene.zionad_props
        
        # ① 新規作成
        col = layout.column(align=True)
        col.scale_y = 1.5
        col.operator(ZIONAD_OT_CreateGP.bl_idname, icon='ADD')
        layout.separator()
        
        # ② ツールとグリッド
        box_tool = layout.box()
        box_tool.label(text="② ツール & グリッド:", icon='TOOL_SETTINGS')
        row_tool = box_tool.row(align=True)
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="ペン", icon='GREASEPENCIL').tool_type = 'PEN'
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="鉛筆", icon='EDITMODE_HLT').tool_type = 'PENCIL'
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="マーカー", icon='BRUSH_DATA').tool_type = 'MARKER'
        row_tool.operator(ZIONAD_OT_SetBrushTool.bl_idname, text="消しゴム", icon='TRASH').tool_type = 'ERASER'
        box_tool.operator(ZIONAD_OT_ToggleCanvas.bl_idname, text="グリッド ON/OFF", icon='GRID')
        
        # ③ 太さ (6種)
        box_size = layout.box()
        box_size.label(text="③ ペンの太さ:", icon='BRUSH_DATA')
        r1 = box_size.row(align=True)
        r1.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="極細 (3)").size = 3
        r1.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="細 (10)").size = 10
        r1.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="中 (20)").size = 20
        r2 = box_size.row(align=True)
        r2.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="太 (30)").size = 30
        r2.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="極太 (50)").size = 50
        r2.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="特大 (100)").size = 100
        
        # ④ 色選択 (3列レイアウトで小型化)
        box_col = layout.box()
        r_col = box_col.row()
        r_col.label(text="④ 色を選ぶ:", icon='COLOR')
        r_col.operator(ZIONAD_OT_EditPalette.bl_idname, text="", icon='PREFERENCES')
        
        grid = box_col.grid_flow(columns=3, align=True)
        for i in range(1, 13):
            name = getattr(props, f"col_{i:02d}_name")
            c = grid.column(align=True)
            r = c.row(align=True)
            r.scale_x = 0.85
            r.prop(props, f"col_{i:02d}_stroke", text="")
            r.prop(props, f"col_{i:02d}_fill", text="")
            op = r.operator(ZIONAD_OT_ApplyColor.bl_idname, text=name)
            op.color_index = i

        # ⑤ レイヤー & エフェクト
        obj = context.active_object
        if is_gp_object(obj):
            # レイヤー管理
            box_layer = layout.box()
            row_head = box_layer.row()
            row_head.label(text="⑤ レイヤー:", icon='GROUP')
            row_head.operator(ZIONAD_OT_AddLayer.bl_idname, text="追加", icon='ADD')
            
            if hasattr(obj.data, "layers") and obj.data.layers:
                layers_list = []
                try: layers_list = list(obj.data.layers)
                except: pass
                for layer in reversed(layers_list):
                    row_layer = box_layer.row(align=True)
                    is_active = False
                    try: is_active = (obj.data.layers.active == layer)
                    except: pass
                    icon = 'RADIOBUT_ON' if is_active else 'RADIOBUT_OFF'
                    op_act = row_layer.operator(ZIONAD_OT_SetLayerActive.bl_idname, text=layer.name, icon=icon)
                    op_act.layer_name = layer.name
                    if hasattr(layer, "hide"): row_layer.prop(layer, "hide", text="", icon='HIDE_ON' if layer.hide else 'HIDE_OFF')
                    elif hasattr(layer, "hide_viewport"): row_layer.prop(layer, "hide_viewport", text="", icon='HIDE_ON' if layer.hide_viewport else 'HIDE_OFF')
                    if hasattr(layer, "lock"): row_layer.prop(layer, "lock", text="", icon='LOCKED' if layer.lock else 'UNLOCKED')
                    if hasattr(layer, "opacity"): row_layer.prop(layer, "opacity", text="", slider=True)
            else:
                box_layer.label(text="レイヤーなし")

            # エフェクト
            box_fx = layout.box()
            box_fx.label(text="★ エフェクト追加:", icon='MODIFIER')
            row_fx = box_fx.row(align=True)
            row_fx.operator(ZIONAD_OT_AddModifier.bl_idname, text="ボイリング(震え)").mod_type = 'NOISE'
            row_fx.operator(ZIONAD_OT_AddModifier.bl_idname, text="アウトライン(フチ)").mod_type = 'THICKNESS'
            
            mods = getattr(obj, "grease_pencil_modifiers", getattr(obj, "modifiers", []))
            for m in mods:
                if m.name == "Boiling_Noise":
                    box_fx.prop(m, "factor", text="ボイリング強度", slider=True)
                if m.name == "Outline_Thickness":
                    box_fx.prop(m, "thickness", text="フチの太さ", slider=True)

        layout.separator()
        col = layout.column(align=True)
        col.scale_y = 1.5
        col.operator(ZIONAD_OT_FinishDrawing.bl_idname, icon='CHECKMARK')

class ZIONAD_PT_Manager(Panel):
    bl_label = "2. オブジェクト管理"
    bl_idname = "ZIONAD_PT_manager"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 1
    bl_options = {'DEFAULT_CLOSED'}

    def draw(self, context):
        layout = self.layout
        box = layout.box()
        props = context.scene.zionad_props
        col = bpy.data.collections.get(props.col_name)
        if col:
            for obj in col.objects:
                if is_gp_object(obj):
                    row = box.row(align=True)
                    icon = 'RADIOBUT_ON' if context.active_object == obj else 'RADIOBUT_OFF'
                    row.prop(obj, "name", text="", icon=icon)
                    row.prop(obj, "hide_viewport", text="", icon='HIDE_ON' if obj.hide_viewport else 'HIDE_OFF')
                    row.prop(obj, "hide_select", text="", icon='RESTRICT_SELECT_ON' if obj.hide_select else 'UNLOCKED')
        else: box.label(text="まだ描画がありません")

class ZIONAD_PT_RemovePanel(Panel):
    bl_label = "3. アドオン削除"
    bl_idname = "ZIONAD_PT_remove"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 2
    bl_options = {'DEFAULT_CLOSED'}
    def draw(self, context):
        layout = self.layout
        layout.operator(ZIONAD_OT_RemoveAllPanels.bl_idname, icon='CANCEL')

# =========================================================
# 5. 登録・解除
# =========================================================
classes = (
    ZIONAD_Properties,
    ZIONAD_OT_CreateGP, ZIONAD_OT_SetBrushTool, ZIONAD_OT_ToggleCanvas,
    ZIONAD_OT_SetBrushSize, ZIONAD_OT_ApplyColor, ZIONAD_OT_AddModifier,
    ZIONAD_OT_AddLayer, ZIONAD_OT_SetLayerActive, ZIONAD_OT_FinishDrawing,
    ZIONAD_OT_EditPalette, ZIONAD_OT_RemoveAllPanels,
    ZIONAD_PT_Workflow, ZIONAD_PT_Manager, ZIONAD_PT_RemovePanel,
)

def register():
    for cls in classes: bpy.utils.register_class(cls)
    bpy.types.Scene.zionad_props = PointerProperty(type=ZIONAD_Properties)

def unregister():
    if hasattr(bpy.types.Scene, 'zionad_props'): del bpy.types.Scene.zionad_props
    for cls in reversed(classes):
        try: bpy.utils.unregister_class(cls)
        except: pass

if __name__ == "__main__": register()

import bpy
import math
from bpy.types import Operator, Panel, PropertyGroup
from bpy.props import StringProperty, PointerProperty, FloatVectorProperty, IntProperty

# =========================================================
# アドオン宣言
# =========================================================
bl_info = {
    "name": "Grease Pencil Object Manager",
    "author": "AI",
    "version": (4, 1, 3),
    "blender": (5, 0, 0), # Blender 5.x / GPv3 対応
    "location": "View3D > Sidebar > GP Manager",
    "description": "GPオブジェクト作成 → 太さ・色選択 → レイヤー追加 → 完成",
    "category": "Grease Pencil",
}

PREFIX_VAL = "ZIONAD"
ADDON_TITLE = "GP Manager"
DEFAULT_COL_NAME = "GreaseObjects"
DEFAULT_BASE_NAME = "GP_"

# =========================================================
# 1. プロパティグループ (12色のカラー設定)
# =========================================================
class ZIONAD_Properties(PropertyGroup):
    col_name: StringProperty(name="主コレクション", default=DEFAULT_COL_NAME)
    obj_base_name: StringProperty(name="ベース名", default=DEFAULT_BASE_NAME)

    # 12色パレット
    col_01_name: StringProperty(default="黒")
    col_01_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.05, 0.05, 0.05, 1.0), min=0.0, max=1.0)
    col_01_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.3, 0.3, 0.3, 0.5), min=0.0, max=1.0)

    col_02_name: StringProperty(default="白")
    col_02_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(1.0, 1.0, 1.0, 1.0), min=0.0, max=1.0)
    col_02_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.9, 0.9, 0.9, 0.5), min=0.0, max=1.0)

    col_03_name: StringProperty(default="灰")
    col_03_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.4, 0.4, 0.4, 1.0), min=0.0, max=1.0)
    col_03_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.6, 0.6, 0.6, 0.5), min=0.0, max=1.0)

    col_04_name: StringProperty(default="赤")
    col_04_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.8, 0.1, 0.1, 1.0), min=0.0, max=1.0)
    col_04_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.9, 0.4, 0.4, 0.5), min=0.0, max=1.0)

    col_05_name: StringProperty(default="青")
    col_05_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.1, 0.2, 0.8, 1.0), min=0.0, max=1.0)
    col_05_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.4, 0.5, 0.9, 0.5), min=0.0, max=1.0)

    col_06_name: StringProperty(default="緑")
    col_06_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.1, 0.7, 0.2, 1.0), min=0.0, max=1.0)
    col_06_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.4, 0.9, 0.5, 0.5), min=0.0, max=1.0)

    col_07_name: StringProperty(default="黄")
    col_07_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.9, 0.8, 0.1, 1.0), min=0.0, max=1.0)
    col_07_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.9, 0.9, 0.5, 0.5), min=0.0, max=1.0)

    col_08_name: StringProperty(default="水")
    col_08_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.1, 0.8, 0.9, 1.0), min=0.0, max=1.0)
    col_08_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.5, 0.9, 0.9, 0.5), min=0.0, max=1.0)

    col_09_name: StringProperty(default="紫")
    col_09_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.6, 0.1, 0.8, 1.0), min=0.0, max=1.0)
    col_09_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.8, 0.5, 0.9, 0.5), min=0.0, max=1.0)

    col_10_name: StringProperty(default="橙")
    col_10_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.9, 0.4, 0.1, 1.0), min=0.0, max=1.0)
    col_10_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.9, 0.6, 0.4, 0.5), min=0.0, max=1.0)

    col_11_name: StringProperty(default="茶")
    col_11_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.4, 0.2, 0.1, 1.0), min=0.0, max=1.0)
    col_11_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.6, 0.4, 0.3, 0.5), min=0.0, max=1.0)

    col_12_name: StringProperty(default="桃")
    col_12_stroke: FloatVectorProperty(name="線", subtype='COLOR', size=4, default=(0.9, 0.3, 0.6, 1.0), min=0.0, max=1.0)
    col_12_fill: FloatVectorProperty(name="塗", subtype='COLOR', size=4, default=(0.9, 0.6, 0.8, 0.5), min=0.0, max=1.0)

# =========================================================
# 2. ユーティリティ
# =========================================================
def is_gp_object(obj):
    return obj and obj.type in {'GPENCIL', 'GREASEPENCIL'}

def get_next_name(base_name):
    max_num = 0
    for obj in bpy.data.objects:
        if is_gp_object(obj) and obj.name.startswith(base_name):
            try: max_num = max(max_num, int(obj.name.replace(base_name, "")))
            except: pass
    return f"{base_name}{max_num + 1:03d}"

# =========================================================
# 3. オペレーター (ワークフロー)
# =========================================================
class ZIONAD_OT_CreateGP(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.create_gp"
    bl_label = "① + 新規 GPオブジェクトを作成"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        props = context.scene.zionad_props
        curr_frame = context.scene.frame_current
        
        if context.mode != 'OBJECT':
            try: bpy.ops.object.mode_set(mode='OBJECT')
            except: pass
        
        # コレクション作成
        col = bpy.data.collections.get(props.col_name)
        if not col:
            col = bpy.data.collections.new(props.col_name)
            context.scene.collection.children.link(col)
            
        # GP作成
        try: bpy.ops.object.grease_pencil_add(type='EMPTY')
        except: bpy.ops.object.gpencil_add(type='EMPTY')
        
        gp = context.active_object
        gp.name = get_next_name(props.obj_base_name)
        for c in gp.users_collection: c.objects.unlink(gp)
        col.objects.link(gp)

        # 【レイヤー&フレーム自動生成】※フレームが無いと描画できないエラーを防止
        gp_data = gp.data
        if hasattr(gp_data, "layers"):
            try:
                if len(gp_data.layers) == 0:
                    layer_fill = gp_data.layers.new(name="Fill")
                    if hasattr(layer_fill, "frames"):
                        layer_fill.frames.new(curr_frame)
                        
                    layer_line = gp_data.layers.new(name="Line")
                    if hasattr(layer_line, "frames"):
                        layer_line.frames.new(curr_frame)
                        
                    gp_data.layers.active = layer_line
            except: pass
        
        # 【マテリアル設定】
        mat_name = "GP_Mat_01"
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(name=mat_name)
            try: bpy.data.materials.create_gpencil_data(mat)
            except: pass
        
        if hasattr(mat, "grease_pencil") and mat.grease_pencil:
            mat.grease_pencil.color = props.col_01_stroke
            mat.grease_pencil.fill_color = props.col_01_fill
            mat.grease_pencil.show_stroke = True
            mat.grease_pencil.show_fill = True
            
        mat.diffuse_color = props.col_01_stroke
        
        if not mat.use_nodes:
            mat.use_nodes = True
        if mat.node_tree:
            for node in mat.node_tree.nodes:
                if node.type == 'BSDF_PRINCIPLED':
                    node.inputs[0].default_value = props.col_01_stroke
        
        gp.data.materials.append(mat)
        gp.active_material_index = 0
        
        # ドローモードへ
        try: bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
        except: 
            try: bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
            except: pass
            
        # 【エラー対策】GPv3などの仕様変更用フォールバック(強制的に空白フレームを挿入)
        try: bpy.ops.grease_pencil.insert_blank_frame()
        except: pass
        try: bpy.ops.gpencil.blank_frame_add()
        except: pass
            
        # ブラシをマテリアルモードに強制
        ts = context.scene.tool_settings
        paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
        if paint and hasattr(paint, "brush") and paint.brush:
            try: paint.brush.use_material = True
            except: pass
            try: paint.brush.color_mode = 'MATERIAL'
            except: pass
        
        self.report({'INFO'}, f"キャンバス '{gp.name}' を作成しました。")
        return {'FINISHED'}

class ZIONAD_OT_SetBrushSize(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_brush_size"
    bl_label = "太さを変更"
    bl_options = {'REGISTER', 'UNDO'}
    
    size: IntProperty(default=10)
    
    def execute(self, context):
        ts = context.scene.tool_settings
        paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
        if paint and hasattr(paint, "brush") and paint.brush:
            paint.brush.size = self.size
        return {'FINISHED'}

class ZIONAD_OT_ApplyColor(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.apply_color"
    bl_label = "色を適用"
    bl_options = {'REGISTER', 'UNDO'}
    color_index: IntProperty(default=1)
    
    def execute(self, context):
        obj = context.active_object
        if not is_gp_object(obj):
            return {'CANCELLED'}
            
        props = context.scene.zionad_props
        stroke_col = getattr(props, f"col_{self.color_index:02d}_stroke")
        fill_col = getattr(props, f"col_{self.color_index:02d}_fill")
        
        mat_name = f"GP_Mat_{self.color_index:02d}"
        mat = bpy.data.materials.get(mat_name)
        
        if not mat:
            mat = bpy.data.materials.new(name=mat_name)
            try: bpy.data.materials.create_gpencil_data(mat)
            except: pass
        
        if hasattr(mat, "grease_pencil") and mat.grease_pencil:
            mat.grease_pencil.color = stroke_col
            mat.grease_pencil.fill_color = fill_col
            mat.grease_pencil.show_stroke = True
            mat.grease_pencil.show_fill = True
            
        mat.diffuse_color = stroke_col
        if not mat.use_nodes:
            mat.use_nodes = True
        if mat.node_tree:
            for node in mat.node_tree.nodes:
                if node.type == 'BSDF_PRINCIPLED':
                    node.inputs[0].default_value = stroke_col
        
        found = False
        for i, slot_mat in enumerate(obj.data.materials):
            if slot_mat == mat:
                obj.active_material_index = i
                found = True
                break
        
        if not found:
            obj.data.materials.append(mat)
            obj.active_material_index = len(obj.data.materials) - 1
            
        ts = context.scene.tool_settings
        paint = getattr(ts, "grease_pencil_paint", getattr(ts, "gpencil_paint", None))
        if paint and hasattr(paint, "brush") and paint.brush:
            try: paint.brush.use_material = True
            except: pass
            try: paint.brush.color_mode = 'MATERIAL'
            except: pass
            
        for area in context.screen.areas:
            if area.type == 'VIEW_3D': area.tag_redraw()
                
        if context.mode not in {'PAINT_GPENCIL', 'PAINT_GREASE_PENCIL'}:
            try: bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
            except: 
                try: bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
                except: pass
                
        return {'FINISHED'}

class ZIONAD_OT_AddLayer(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.add_layer"
    bl_label = "新規レイヤー追加"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        obj = context.active_object
        curr_frame = context.scene.frame_current
        
        if is_gp_object(obj) and hasattr(obj.data, "layers"):
            try:
                layer = obj.data.layers.new(name="Layer")
                obj.data.layers.active = layer
                # レイヤー作成と同時にフレーム(キャンバス)を追加
                if hasattr(layer, "frames"):
                    layer.frames.new(curr_frame)
            except: pass
            
            # ドローモード中の場合は念のためオペレーターでフレーム追加処理を呼び出す
            if context.mode in {'PAINT_GPENCIL', 'PAINT_GREASE_PENCIL'}:
                try: bpy.ops.grease_pencil.insert_blank_frame()
                except: pass
                try: bpy.ops.gpencil.blank_frame_add()
                except: pass
                
        return {'FINISHED'}

class ZIONAD_OT_SetLayerActive(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.set_layer_active"
    bl_label = "レイヤーを選択"
    layer_name: StringProperty()
    
    def execute(self, context):
        obj = context.active_object
        if is_gp_object(obj) and hasattr(obj.data, "layers"):
            layer = obj.data.layers.get(self.layer_name)
            if layer: obj.data.layers.active = layer
        return {'FINISHED'}

class ZIONAD_OT_FinishDrawing(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.finish_drawing"
    bl_label = "⑤ ✓ オブジェクト完成 (確定して次を作る)"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        if not is_gp_object(context.active_object): return {'CANCELLED'}
        try: bpy.ops.object.mode_set(mode='OBJECT')
        except: pass
        try:
            op = getattr(bpy.ops, PREFIX_VAL.lower())
            op.create_gp()
        except: pass
        return {'FINISHED'}

class ZIONAD_OT_EditPalette(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.edit_palette"
    bl_label = "🎨 パレットの12色をカスタマイズ"
    
    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self, width=400)
        
    def draw(self, context):
        layout = self.layout
        props = context.scene.zionad_props
        layout.label(text="名前 / Stroke(線の色) / Fill(塗りの色)", icon='COLOR')
        layout.separator()
        for i in range(1, 13):
            row = layout.row(align=True)
            row.prop(props, f"col_{i:02d}_name", text="")
            row.prop(props, f"col_{i:02d}_stroke", text="")
            row.prop(props, f"col_{i:02d}_fill", text="")
            
    def execute(self, context): return {'FINISHED'}

class ZIONAD_OT_RemoveAllPanels(Operator):
    bl_idname = f"{PREFIX_VAL.lower()}.remove_all_panels"
    bl_label = "Unregister Addon"
    def execute(self, context): 
        unregister() 
        return {'FINISHED'}

# =========================================================
# 4. パネル UI
# =========================================================
class ZIONAD_PT_Workflow(Panel):
    bl_label = "1. 描画ワークフロー"
    bl_idname = "ZIONAD_PT_workflow"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 0

    def draw(self, context):
        layout = self.layout
        props = context.scene.zionad_props
        
        # ① 新規作成
        col = layout.column(align=True)
        col.scale_y = 1.5
        col.operator(ZIONAD_OT_CreateGP.bl_idname, icon='ADD')
        layout.separator()
        
        # ② ペンの太さ
        box_size = layout.box()
        box_size.label(text="② ペンの太さ:", icon='BRUSH_DATA')
        row_size = box_size.row(align=True)
        op_s = row_size.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="細 (5)")
        op_s.size = 5
        op_m = row_size.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="中 (20)")
        op_m.size = 20
        op_l = row_size.operator(ZIONAD_OT_SetBrushSize.bl_idname, text="太 (50)")
        op_l.size = 50
        
        layout.separator()
        
        # ③ 色選択
        box = layout.box()
        row = box.row()
        row.label(text="③ 色を選ぶ (選択以降に描く線に適用):", icon='COLOR')
        row.operator(ZIONAD_OT_EditPalette.bl_idname, text="", icon='PREFERENCES')
        
        grid = box.grid_flow(columns=2, align=True)
        for i in range(1, 13):
            name = getattr(props, f"col_{i:02d}_name")
            c = grid.column(align=True)
            r = c.row(align=True)
            r.prop(props, f"col_{i:02d}_stroke", text="")
            r.prop(props, f"col_{i:02d}_fill", text="")
            op = r.operator(ZIONAD_OT_ApplyColor.bl_idname, text=name)
            op.color_index = i

        layout.separator()

        # ④ レイヤー管理
        obj = context.active_object
        if is_gp_object(obj):
            box_layer = layout.box()
            row_head = box_layer.row()
            row_head.label(text="④ レイヤー (同一オブジェクト内):", icon='GROUP')
            row_head.operator(ZIONAD_OT_AddLayer.bl_idname, text="追加", icon='ADD')
            
            if hasattr(obj.data, "layers") and obj.data.layers:
                # 【エラー対策】GPv3でリスト化が失敗した場合にパネルが消えないように保護
                layers_list = []
                try: layers_list = list(obj.data.layers)
                except: pass
                
                for layer in reversed(layers_list):
                    row_layer = box_layer.row(align=True)
                    
                    is_active = False
                    try: is_active = (obj.data.layers.active == layer)
                    except: pass
                    
                    icon = 'RADIOBUT_ON' if is_active else 'RADIOBUT_OFF'
                    op_act = row_layer.operator(ZIONAD_OT_SetLayerActive.bl_idname, text=layer.name, icon=icon)
                    op_act.layer_name = layer.name
                    
                    if hasattr(layer, "hide"):
                        row_layer.prop(layer, "hide", text="", icon='HIDE_ON' if layer.hide else 'HIDE_OFF')
                    elif hasattr(layer, "hide_viewport"):
                        row_layer.prop(layer, "hide_viewport", text="", icon='HIDE_ON' if layer.hide_viewport else 'HIDE_OFF')
                        
                    if hasattr(layer, "lock"):
                        row_layer.prop(layer, "lock", text="", icon='LOCKED' if layer.lock else 'UNLOCKED')
                        
                    if hasattr(layer, "opacity"):
                        row_layer.prop(layer, "opacity", text="", slider=True)
            else:
                box_layer.label(text="レイヤーがありません")
                
        layout.separator()

        # ⑤ 完成ボタン
        col = layout.column(align=True)
        col.scale_y = 1.5
        col.operator(ZIONAD_OT_FinishDrawing.bl_idname, icon='CHECKMARK')

class ZIONAD_PT_Manager(Panel):
    bl_label = "2. オブジェクト管理"
    bl_idname = "ZIONAD_PT_manager"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 1
    bl_options = {'DEFAULT_CLOSED'}

    def draw(self, context):
        layout = self.layout
        box = layout.box()
        
        props = context.scene.zionad_props
        col = bpy.data.collections.get(props.col_name)
        
        if col:
            for obj in col.objects:
                if is_gp_object(obj):
                    row = box.row(align=True)
                    icon = 'RADIOBUT_ON' if context.active_object == obj else 'RADIOBUT_OFF'
                    row.prop(obj, "name", text="", icon=icon)
                    row.prop(obj, "hide_viewport", text="", icon='HIDE_ON' if obj.hide_viewport else 'HIDE_OFF')
                    row.prop(obj, "hide_select", text="", icon='RESTRICT_SELECT_ON' if obj.hide_select else 'UNLOCKED')
        else:
            box.label(text="まだ描画がありません")

class ZIONAD_PT_RemovePanel(Panel):
    bl_label = "3. アドオン削除"
    bl_idname = "ZIONAD_PT_remove"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = ADDON_TITLE
    bl_order = 2
    bl_options = {'DEFAULT_CLOSED'}

    def draw(self, context):
        layout = self.layout
        box = layout.box()
        box.label(text="アドオンの登録を即座に解除します", icon='ERROR')
        box.operator(ZIONAD_OT_RemoveAllPanels.bl_idname, icon='CANCEL')

# =========================================================
# 5. 登録・解除
# =========================================================
classes = (
    ZIONAD_Properties,
    ZIONAD_OT_CreateGP,
    ZIONAD_OT_SetBrushSize,
    ZIONAD_OT_ApplyColor,
    ZIONAD_OT_AddLayer,
    ZIONAD_OT_SetLayerActive,
    ZIONAD_OT_FinishDrawing,
    ZIONAD_OT_EditPalette,
    ZIONAD_OT_RemoveAllPanels,
    ZIONAD_PT_Workflow,
    ZIONAD_PT_Manager,
    ZIONAD_PT_RemovePanel,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.zionad_props = PointerProperty(type=ZIONAD_Properties)

def unregister():
    if hasattr(bpy.types.Scene, 'zionad_props'):
        del bpy.types.Scene.zionad_props
    for cls in reversed(classes):
        try:
            bpy.utils.unregister_class(cls)
        except:
            pass

if __name__ == "__main__":
    register()