# gpl author: Antonis Karvelas

# -*- coding: utf-8 -*-

bl_info = {
    "name": "Circle Array",
    "author": "Antonis Karvelas",
    "version": (1, 0, 1),
    "blender": (2, 67, 0),
    "location": "View3D > Object > Circle_Array",
    "description": "Uses an existing array and creates an empty, "
                   "rotates it properly and makes a Circle Array",
    "warning": "",
    "wiki_url": "",
    "category": "Mesh"
    }


import bpy
from bpy.types import Operator
from math import radians


class Circle_Array(Operator):
    bl_label = "Circle Array"
    bl_idname = "objects.circle_array_operator"
    bl_description = ("Creates an Array Modifier with offset empty object\n"
                      "Works with Mesh, Curve, Text and Surface\n"
                      "Use an object with an existing Array modifier\n"
                      "or rotate the newly created Empty with the name pattern\n"
                      "EMPTY_C_Array_ if the Array doesn't exist (angle: 360/Count)")

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def check_empty_name(self, context):
        new_name, def_name = "", "EMPTY_C_Array"
        suffix = 1
        try:
            # first slap a simple linear count + 1 for numeric suffix, if it fails
            # harvest for the rightmost numbers and append the max value
            list_obj = []
            obj_all = context.scene.objects
            list_obj = [obj.name for obj in obj_all if obj.name.startswith(def_name)]
            new_name = "{}_{}".format(def_name, len(list_obj) + 1)

            if new_name in list_obj:
                from re import findall
                test_num = [findall("\d+", words) for words in list_obj]
                suffix += max([int(l[-1]) for l in test_num])
                new_name = "{}_{}".format(def_name, suffix)
            return new_name
        except:
            return None

    def execute(self, context):
        is_allowed = True
        try:
            allowed_obj = ['MESH', 'CURVE', 'SURFACE', 'FONT']
            for obj in context.selected_objects:
                if obj.type not in allowed_obj:
                    is_allowed = False
                    break

            if not is_allowed:
                self.report(
                    {"WARNING"},
                    "The Active/Selected objects are not of "
                    "Mesh, Curve, Surface or Font type. Operation Cancelled"
                    )
                return {'CANCELLED'}

            default_name = self.check_empty_name(context) or "EMPTY_C_Array"
            bpy.ops.object.modifier_add(type='ARRAY')

            if len(context.selected_objects) == 2:
                selected = context.selected_objects
                lists = [obj for obj in selected if obj != context.active_object]
                active = lists[0]
                # check if the list object has a modifier
                check_mod = None
                for mod in active.modifiers[:]:
                    if mod.type == "ARRAY":
                        check_mod = mod
                        break

                if check_mod:
                    check_mod.use_object_offset = True
                    check_mod.use_relative_offset = False
                else:
                    # fallback
                    bpy.context.view_layer.objects.active = active
                    bpy.ops.object.modifier_add(type='ARRAY')
                    active.modifiers[0].use_object_offset = True
                    active.modifiers[0].use_relative_offset = False

                active.modifiers[0].use_object_offset = True
                active.modifiers[0].use_relative_offset = False
                active.select_set(False)
                bpy.context.view_layer.objects.active = context.active_object
                bpy.ops.view3d.snap_cursor_to_selected()

                if active.modifiers[0].offset_object is None:
                    bpy.ops.object.add(type='EMPTY')
                    empty_name = bpy.context.active_object
                    empty_name.name = default_name
                    active.modifiers[0].offset_object = empty_name
                else:
                    empty_name = active.modifiers[0].offset_object

                bpy.context.view_layer.objects.active = active
                num = active.modifiers["Array"].count
                rotate_num = 360 / num
                active.select_set(True)
                bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
                empty_name.rotation_euler = (0, 0, radians(rotate_num))
                empty_name.select_set(False)
                active.select_set(True)
                bpy.ops.object.origin_set(type="ORIGIN_CURSOR")

                return {'FINISHED'}
            else:
                active = context.active_object
                active.modifiers[0].use_object_offset = True
                active.modifiers[0].use_relative_offset = False
                bpy.ops.view3d.snap_cursor_to_selected()

                if active.modifiers[0].offset_object is None:
                    bpy.ops.object.add(type='EMPTY')
                    empty_name = bpy.context.active_object
                    empty_name.name = default_name
                    active.modifiers[0].offset_object = empty_name
                else:
                    empty_name = active.modifiers[0].offset_object

                bpy.context.view_layer.objects.active = active
                num = active.modifiers["Array"].count
                rotate_num = 360 / num
                active.select_set(True)
                bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
                empty_name.rotation_euler = (0, 0, radians(rotate_num))
                empty_name.select_set(False)
                active.select_set(True)

                return {'FINISHED'}

        except Exception as e:
            self.report({'WARNING'},
                        "Circle Array operator could not be executed (See the console for more info)")
            print("\n[objects.circle_array_operator]\nError: {}\n".format(e))

            return {'CANCELLED'}


# Register
def register():
    bpy.utils.register_class(Circle_Array)


def unregister():
    bpy.utils.unregister_class(Circle_Array)


if __name__ == "__main__":
    register()
