# -*- coding: utf-8 -*-
import os
import bpy

from stalker import Project, Task, Version, Sequence, Shot
from anima import logger

bl_info = {
    "name": "Stalker Reviewer",
    "description": "Review Stalker task version outputs in Blender.",
    "author": "Erkan Ozgur Yilmaz",
    "version": (0, 1),
    "blender": (2, 71, 0),
    "location": "View3D > Add > Mesh",
    "warning": "",  # used for warning icon and text in addons panel
    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"
    "Scripts/My_Script",
    "category": "Object",
}


idname_template = "stalker.%s_%s_menu"
registered_menus = []


output_types = {
    "image": [".jpg", ".jpeg", ".png", ".tiff", ".tga"],
    "movie": [".mov", ".avi", ".webm", ".mpeg", ".mpg"],
}


class StripGenerator(object):
    """Generates strips out of tasks given to it."""

    def __init__(self):
        pass

    def add_from_task(self, task):
        """adds the latest output of the given task"""
        if not task:
            # inform the user
            return

        # get the repo
        repo = task.project.repository

        # do it in dirty way
        versions = (
            Version.query.filter(Version.task == task)
            .order_by(Version.version_number.desc())
            .all()
        )

        found_output = False
        for v in versions:
            if len(v.outputs):
                # get all outputs of this version
                for output in v.outputs:
                    output_absolute_full_path = os.path.expandvars(output.full_path)
                    self.add_output(output_absolute_full_path)
                    found_output = True
                    break

            if found_output:
                break

    def add_output(self, full_path, name=None, channel=1):
        """adds the media in the given path to the time line

        :param str full_path: The path of the file
        """
        logger.debug("adding output from: %s" % full_path)
        extension = os.path.splitext(full_path)[-1].lower()

        logger.debug("extension: %s" % extension)

        output_type = None
        for key in output_types.keys():
            if extension in output_types[key]:
                output_type = key
                break

        if output_type == "image":
            logger.debug("output is image")
            bpy.ops.sequencer.image_strip_add(
                directory=os.path.dirname(full_path),
                files=[
                    {
                        "name": os.path.basename(full_path),
                        # "name": os.path.basename(full_path)
                    }
                ],
                relative_path=True,
                frame_start=1,
                frame_end=26,
                channel=channel,
            )
        elif output_type == "movie":
            logger.debug("output is movie")
            logger.debug("full_path: %s" % full_path)
            bpy.ops.sequencer.movie_strip_add(
                filepath=full_path,
                files=[
                    {
                        "name": os.path.basename(full_path),
                        # "name": os.path.basename(full_path)
                    }
                ],
                relative_path=True,
                frame_start=1,
                channel=channel,
            )
        else:
            logger.debug("output_type is unknown: %s" % output_type)

    def storyboard(self, task):
        """When a Scene task is given it will create a strip from the output of
        the Storyboard task under it.

        :param task: A :class:`stalker.models.task.Task` class instance.
        """
        # get the storyboard task
        storyboard = (
            Task.query.filter(Task.parent == task)
            .filter(Task.name == "Storyboard")
            .first()
        )

        self.add_from_task(storyboard)

    def previs(self, task):
        """when a Shot task is given it will create a strip from the output of
        the Previs task under it.

        :param task: A :class:`stalker.models.task.Task` class instance.
        """
        # get the previs task
        previs = (
            Task.query.filter(Task.parent == task).filter(Task.name == "Previs").first()
        )

        self.add_from_task(previs)

    def animation(self, task):
        """when a Shot task is given it will create a strip from the output of
        the Animation task under it.

        :param task: A :class:`stalker.models.task.Task` class instance.
        """
        # get the storyboard task
        animation = (
            Task.query.filter(Task.parent == task)
            .filter(Task.name == "Animation")
            .first()
        )

        self.add_from_task(animation)

    def lighting(self, task):
        """when a Shot task is given it will create a strip from the output of
        the Lighting task under it.

        :param task: A :class:`stalker.models.task.Task` class instance.
        """
        # get the storyboard task
        animation = (
            Task.query.filter(Task.parent == task)
            .filter(Task.name == "Animation")
            .first()
        )

        self.add_from_task(animation)

    def comp(self, task):
        """when a Shot task is given it will create a strip from the output of
        the Compositing task under it.

        :param task: A :class:`stalker.models.task.Task` class instance.
        """
        pass


class StalkerMenu(bpy.types.Menu):
    """The Stalker menu for Blender"""

    bl_label = "Stalker..."
    bl_idname = "stalker.main_menu"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = "INVOKE_REGION_WIN"
        layout.menu(StalkerAddFromProjectMenu.bl_idname, text="Add From Project")


def draw_stalker_entity_menu_item(self, context):
    """draws one menu item"""
    logger.debug("entity_id   : %s" % self.stalker_entity_id)
    logger.debug("entity_name : %s" % self.stalker_entity_name)


def draw_stalker_project_menu_item(self, context):
    """draws one menu item"""
    logger.debug("entity_id   : %s" % self.stalker_entity_id)
    logger.debug("entity_name : %s" % self.stalker_entity_name)

    layout = self.layout

    # get the project
    project = Project.query.get(self.stalker_entity_id)
    all_seqs = (
        Sequence.query.filter(Sequence.project == project).order_by(Sequence.name).all()
    )
    for seq in all_seqs:
        idname = idname_template % (seq.entity_type, seq.id)
        layout.menu(idname, text=seq.name)


def draw_stalker_sequence_menu_item(self, context):
    """draws one menu item"""
    logger.debug("entity_id   : %s" % self.stalker_entity_id)
    logger.debug("entity_name : %s" % self.stalker_entity_name)

    layout = self.layout

    # get the sequence
    seq = Sequence.query.get(self.stalker_entity_id)

    # add all the child tasks under it
    for scene in sorted(seq.children, key=lambda x: x.name):
        # op = layout.operator(StalkerSceneMenu.bl_idname, text=scene.name)
        # op.stalker_entity_id = scene.id
        # op.stalker_entity_name = scene.name
        idname = idname_template % (scene.entity_type, scene.id)
        layout.menu(idname, text=scene.name)


def draw_stalker_scene_menu_item(self, context):
    """draws one scene menu item"""
    logger.debug("entity_id   : %s" % self.stalker_entity_id)
    logger.debug("entity_name : %s" % self.stalker_entity_name)

    layout = self.layout

    scene = Task.query.get(self.stalker_entity_id)

    # Add Everything
    op = layout.operator(
        StalkerSceneAddEverythingOperator.bl_idname, text="Add Everything"
    )
    op.stalker_entity_id = scene.id
    op.stalker_entity_name = scene.name

    layout.separator()

    # Add Storyboard Only
    op = layout.operator(StalkerSceneAddStoryboardOperator.bl_idname, text="Storyboard")
    op.stalker_entity_id = scene.id
    op.stalker_entity_name = scene.name

    # Add Previs Only
    op = layout.operator(StalkerSceneAddPrevisOperator.bl_idname, text="Previs")
    op.stalker_entity_id = scene.id
    op.stalker_entity_name = scene.name

    layout.separator()

    # Add From Shots Menu
    idname = "%s%s" % (
        idname_template % (scene.entity_type, scene.id),
        "_add_from_shots_menu",
    )
    layout.menu(idname)


def draw_stalker_scene_add_from_shots_menu_item(self, context):
    """draws one scene/add from shots scene menu item"""

    logger.debug("entity_id   : %s" % self.stalker_entity_id)
    logger.debug("entity_name : %s" % self.stalker_entity_name)

    layout = self.layout

    scene = Task.query.get(self.stalker_entity_id)

    # Add All Shot Task Outputs
    op = layout.operator(
        StalkerSceneAddAllShotOutputsOperator.bl_idname, text="Add All Shot Outputs"
    )
    op.stalker_entity_id = scene.id
    op.stalker_entity_name = scene.name

    # separator
    layout.separator()

    # Previs
    op = layout.operator(
        StalkerSceneAddAllShotPrevisOutputsOperator.bl_idname, text="Previs"
    )
    op.stalker_entity_id = scene.id
    op.stalker_entity_name = scene.name

    # Animation
    op = layout.operator(
        StalkerSceneAddAllShotAnimationOutputsOperator.bl_idname, text="Animation"
    )
    op.stalker_entity_id = scene.id
    op.stalker_entity_name = scene.name

    # Lighting
    op = layout.operator(
        StalkerSceneAddAllShotLightingOutputsOperator.bl_idname, text="Lighting"
    )
    op.stalker_entity_id = scene.id
    op.stalker_entity_name = scene.name

    # Comp
    op = layout.operator(
        StalkerSceneAddAllShotCompOutputsOperator.bl_idname, text="Comp"
    )
    op.stalker_entity_id = scene.id
    op.stalker_entity_name = scene.name

    # separator
    layout.separator()

    # then add menu for each shot under its "Shots" tasks
    shots_task = (
        Task.query.filter(Task.parent == scene).filter(Task.name == "Shots").first()
    )

    if shots_task:
        for shot in shots_task.children:
            # add a shot menu
            idname = idname_template % (shot.entity_type, shot.id)
            layout.menu(idname)


def draw_stalker_shot_menu_item(self, context):
    """draws one menu item"""
    logger.debug("entity_id   : %s" % self.stalker_entity_id)
    logger.debug("entity_name : %s" % self.stalker_entity_name)

    layout = self.layout

    # get the sequence

    shot = Shot.query.get(self.stalker_entity_id)

    # add all the child tasks under it
    # for scene in sorted(seq.children, key=lambda x: x.name):
    #     # op = layout.operator(StalkerSceneMenu.bl_idname, text=scene.name)
    #     # op.stalker_entity_id = scene.id
    #     # op.stalker_entity_name = scene.name
    #     idname = idname_template % (scene.entity_type, scene.id)
    #     layout.menu(idname, text=scene.name)

    # add a menu operator for
    # Add All Task Outputs
    op = layout.operator(
        StalkerShotAddAllTaskOutputsOperator.bl_idname, text="Add All Task Outputs"
    )
    op.stalker_entity_id = self.stalker_entity_id
    op.stalker_entity_name = self.stalker_entity_name

    # ------
    # Add a separator
    layout.separator()

    # Previs
    op = layout.operator(StalkerShotAddPrevisOutputOperator.bl_idname, text="Previs")
    op.stalker_entity_id = self.stalker_entity_id
    op.stalker_entity_name = self.stalker_entity_name

    # Animation
    op = layout.operator(
        StalkerShotAddAnimationOutputOperator.bl_idname, text="Animation"
    )
    op.stalker_entity_id = self.stalker_entity_id
    op.stalker_entity_name = self.stalker_entity_name

    # Lighting
    op = layout.operator(
        StalkerShotAddLightingOutputOperator.bl_idname, text="Lighting"
    )
    op.stalker_entity_id = self.stalker_entity_id
    op.stalker_entity_name = self.stalker_entity_name

    # Comp
    op = layout.operator(StalkerShotAddCompOutputOperator.bl_idname, text="Comp")
    op.stalker_entity_id = self.stalker_entity_id
    op.stalker_entity_name = self.stalker_entity_name


class StalkerAddFromProjectMenu(bpy.types.Menu):
    """The Add From Project menu for Blender"""

    bl_label = "From Project..."
    bl_idname = "stalker.add_from_project_menu"

    def draw(self, context):
        layout = self.layout
        for project in Project.query.order_by(Project.name).all():
            idname = "stalker.%s_%s_menu" % (project.entity_type, project.id)
            layout.menu(idname)


class StalkerSceneAddEverythingOperator(bpy.types.Operator):
    """Adds all of the outputs of everything under this scene"""

    bl_label = "Add Everything"
    bl_idname = "stalker.scene_add_everything_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        scene = Task.query.get(self.stalker_entity_id)

        logger.debug("scene: %s" % scene)

        # generate storyboard
        strip_gen = StripGenerator()
        strip_gen.storyboard(scene)
        strip_gen.previs(scene)
        strip_gen.animation(scene)
        strip_gen.lighting(scene)
        strip_gen.comp(scene)

        # go to each Shot and add latest outputs of everything under it
        for task in scene.walk_hierarchy():
            if isinstance(task, Shot):
                # find the animation task under it
                animation_task = (
                    Task.query.filter(Task.parent_id == task.id)
                    .filter(Task.name == "Animation")
                    .first()
                )

        return set(["FINISHED"])


class StalkerSceneAddStoryboardOperator(bpy.types.Operator):
    """Adds the storyboard output of this scene"""

    bl_label = "Add Storyboard Only"
    bl_idname = "stalker.scene_add_storyboard_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        scene = Task.query.get(self.stalker_entity_id)

        logger.debug("scene: %s" % scene)

        # generate storyboard
        strip_gen = StripGenerator()
        strip_gen.storyboard(scene)

        return set(["FINISHED"])


class StalkerSceneAddPrevisOperator(bpy.types.Operator):
    """Adds the previs output of this scene"""

    bl_label = "Add Previs Only"
    bl_idname = "stalker.scene_add_previs_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        scene = Task.query.get(self.stalker_entity_id)

        logger.debug("scene: %s" % scene)

        # generate storyboard
        strip_gen = StripGenerator()
        strip_gen.previs(scene)

        return set(["FINISHED"])


class StalkerSceneAddAllShotOutputsOperator(bpy.types.Operator):
    """Adds all of the shot task outputs under this scene"""

    bl_label = "Add All Shot Outputs"
    bl_idname = "stalker.scene_add_all_shot_outputs_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        scene = Task.query.get(self.stalker_entity_id)

        logger.debug("scene: %s" % scene)

        # find the "Shots" Task and get all the shots under it
        # for each shot add the animation, lighting and comp

        # for shot in scene.children
        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.previs(scene)
        # strip_gen.animation(scene)
        # strip_gen.lighting(scene)
        # strip_gen.comp(scene)

        return set(["FINISHED"])


class StalkerSceneAddAllShotPrevisOutputsOperator(bpy.types.Operator):
    """Adds all the Previs task outputs from all of the shots"""

    bl_label = "Add All Shot Previs Outputs"
    bl_idname = "stalker.scene_add_all_shot_previs_outputs_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        scene = Task.query.get(self.stalker_entity_id)

        logger.debug("scene: %s" % scene)

        # find the "Shots" Task and get all the shots under it
        # for each shot add the animation, lighting and comp

        # for shot in scene.children
        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.previs(scene)
        # strip_gen.animation(scene)
        # strip_gen.lighting(scene)
        # strip_gen.comp(scene)

        return set(["FINISHED"])


class StalkerSceneAddAllShotAnimationOutputsOperator(bpy.types.Operator):
    """Adds all the Animation task outputs from all of the shots"""

    bl_label = "Add All Shot Animation Outputs"
    bl_idname = "stalker.scene_add_all_shot_animation_outputs_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        scene = Task.query.get(self.stalker_entity_id)

        logger.debug("scene: %s" % scene)

        # find the "Shots" Task and get all the shots under it
        # for each shot add the animation, lighting and comp

        # for shot in scene.children
        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.previs(scene)
        # strip_gen.animation(scene)
        # strip_gen.lighting(scene)
        # strip_gen.comp(scene)

        return set(["FINISHED"])


class StalkerSceneAddAllShotLightingOutputsOperator(bpy.types.Operator):
    """Adds all the Lighting task outputs from all of the shots"""

    bl_label = "Add All Shot Lighting Outputs"
    bl_idname = "stalker.scene_add_all_shot_lighting_outputs_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        scene = Task.query.get(self.stalker_entity_id)

        logger.debug("scene: %s" % scene)

        # find the "Shots" Task and get all the shots under it
        # for each shot add the animation, lighting and comp

        # for shot in scene.children
        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.previs(scene)
        # strip_gen.animation(scene)
        # strip_gen.lighting(scene)
        # strip_gen.comp(scene)

        return set(["FINISHED"])


class StalkerSceneAddAllShotCompOutputsOperator(bpy.types.Operator):
    """Adds all the Comp task outputs from all of the shots"""

    bl_label = "Add All Shot Comp Outputs"
    bl_idname = "stalker.scene_add_all_shot_comp_outputs_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        scene = Task.query.get(self.stalker_entity_id)

        logger.debug("scene: %s" % scene)

        # find the "Shots" Task and get all the shots under it
        # for each shot add the animation, lighting and comp

        # for shot in scene.children
        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.previs(scene)
        # strip_gen.animation(scene)
        # strip_gen.lighting(scene)
        # strip_gen.comp(scene)

        return set(["FINISHED"])


class StalkerShotAddAllTaskOutputsOperator(bpy.types.Operator):
    """Adds all task outputs of this shot"""

    bl_label = "Add All Task Outputs"
    bl_idname = "stalker.shot_add_all_task_outputs_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        shot = Shot.query.get(self.stalker_entity_id)

        logger.debug("shot: %s" % shot)

        # find Previs, Animation, Lighting and Comp tasks

        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.previs(scene)
        # strip_gen.animation(scene)
        # strip_gen.lighting(scene)
        # strip_gen.comp(scene)

        return set(["FINISHED"])


class StalkerShotAddPrevisOutputOperator(bpy.types.Operator):
    """Adds just the Previs output of this shot"""

    bl_label = "Add Previs Output"
    bl_idname = "stalker.shot_add_previs_output_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        shot = Shot.query.get(self.stalker_entity_id)

        logger.debug("shot: %s" % shot)

        # find Previs, Animation, Lighting and Comp tasks

        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.previs(shot)

        return set(["FINISHED"])


class StalkerShotAddAnimationOutputOperator(bpy.types.Operator):
    """Adds just the Animation output of this shot"""

    bl_label = "Add Animation Output"
    bl_idname = "stalker.shot_add_animation_output_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        shot = Shot.query.get(self.stalker_entity_id)

        logger.debug("shot: %s" % shot)

        # find Previs, Animation, Lighting and Comp tasks

        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.animation(shot)

        return set(["FINISHED"])


class StalkerShotAddLightingOutputOperator(bpy.types.Operator):
    """Adds just the Lighting output of this shot"""

    bl_label = "Add Lighting Output"
    bl_idname = "stalker.shot_add_lighting_output_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        shot = Shot.query.get(self.stalker_entity_id)

        logger.debug("shot: %s" % shot)

        # find Previs, Animation, Lighting and Comp tasks

        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.lighting(shot)

        return set(["FINISHED"])


class StalkerShotAddCompOutputOperator(bpy.types.Operator):
    """Adds just the Comp output of this shot"""

    bl_label = "Add Comp Output"
    bl_idname = "stalker.shot_add_comp_output_op"

    stalker_entity_id = bpy.props.IntProperty(name="stalker_entity_id")
    stalker_entity_name = bpy.props.StringProperty(name="stalker_entity_name")

    def execute(self, context):
        logger.debug("inside %s.execute()" % self.__class__.__name__)

        # get the scene and all the shots under it
        shot = Shot.query.get(self.stalker_entity_id)

        logger.debug("shot: %s" % shot)

        # find Previs, Animation, Lighting and Comp tasks

        # # generate storyboard
        # strip_gen = StripGenerator()
        # strip_gen.comp(shot)

        return set(["FINISHED"])


# class Reviewer(bpy.types.Panel):
#     """Stalker version outputs Reviewer
#     """
#     bl_idname = "stalker.reviewer"       # unique identifier for buttons and
#                                          # menu items to reference.
#     bl_label = "Stalker Reviewer Panel"  # display name in the interface.
#     bl_space_type = 'SEQUENCE_EDITOR'
#     bl_region_type = 'UI'
#
#     def draw(self, context):
#         layout = self.layout
#
#         selected_sequences = context.selected_sequences
#         selected_sequence_names = ''
#         if selected_sequences:
#             seq_names = []
#             for seq in selected_sequences:
#                 seq_names.append(seq.name)
#             seq_names = ','.join(selected_sequence_names)
#
#         row = layout.row()
#         row.label(text="Hello world!", icon='WORLD_DATA')
#
#         row = layout.row()
#         row.label(text="Active sequence is: %s" % selected_sequence_names)
#
#         #row = layout.row()
#         #row.prop(selected_sequences, "name")
#
#         # row = layout.row()
#         # row.operator("mesh.primitive_cube_add")


def draw_stalker_menu(self, context):
    layout = self.layout
    layout.menu(StalkerMenu.bl_idname)


def generate_op_class(
    entity, draw=draw_stalker_entity_menu_item, idpostfix="", label=None
):
    """generates clunky opclass for given entity

    :param entity: The Stalker entity
    :param draw: The draw function, defaults to draw_stalker_entity_menu_item
    :param label: Label of this menu
    """
    idname = "%s%s" % (idname_template % (entity.entity_type, entity.id), idpostfix)
    return type(
        idname,
        (bpy.types.Menu,),
        {
            "bl_idname": idname,
            "bl_label": label if label else entity.name,
            "stalker_entity_id": entity.id,
            "stalker_entity_type": entity.entity_type,
            "stalker_entity_name": entity.name,
            "draw": draw,
        },
    )


def register():
    """register the addon"""
    # do database setup
    from anima.utils import do_db_setup

    do_db_setup()

    #
    # generate one class for each menu item
    #
    # This is a limitation of Blender, as of 2.71 the ``layout.menu()`` method
    # is not returning any item to set the property to, so clever guys around
    # the blender community are suggesting to dynamically generating new
    # classes which is a very very very very bad practice, which essentially
    # does nothing better than bloating the python name space.
    #
    for project in Project.query.order_by(Project.name).all():
        opclass = generate_op_class(project, draw=draw_stalker_project_menu_item)

        bpy.utils.register_class(opclass)

        # append them to the lookup table so we will be able to unregister
        # them later on
        registered_menus.append(opclass)

        # for each project register a sequence menu
        all_seqs = (
            Sequence.query.filter(Sequence.project == project)
            .order_by(Sequence.name)
            .all()
        )
        for seq in all_seqs:
            opclass = generate_op_class(seq, draw=draw_stalker_sequence_menu_item)
            bpy.utils.register_class(opclass)
            registered_menus.append(opclass)

            # for each sequence register a scene menu
            for sce in seq.children:
                opclass = generate_op_class(sce, draw=draw_stalker_scene_menu_item)
                bpy.utils.register_class(opclass)
                registered_menus.append(opclass)

                # generate a "Add From Shots" menu for each of them
                opclass = generate_op_class(
                    sce,
                    draw=draw_stalker_scene_add_from_shots_menu_item,
                    idpostfix="_add_from_shots_menu",
                    label="Add From Shots",
                )
                bpy.utils.register_class(opclass)
                registered_menus.append(opclass)

        # for each Shot generate a menu item
        for shot in Shot.query.filter(Shot.project == project).all():
            opclass = generate_op_class(shot, draw=draw_stalker_shot_menu_item)
            bpy.utils.register_class(opclass)
            registered_menus.append(opclass)

    bpy.utils.register_class(StalkerMenu)
    bpy.utils.register_class(StalkerAddFromProjectMenu)

    bpy.utils.register_class(StalkerSceneAddEverythingOperator)
    bpy.utils.register_class(StalkerSceneAddStoryboardOperator)
    bpy.utils.register_class(StalkerSceneAddPrevisOperator)

    bpy.utils.register_class(StalkerSceneAddAllShotOutputsOperator)
    bpy.utils.register_class(StalkerSceneAddAllShotPrevisOutputsOperator)
    bpy.utils.register_class(StalkerSceneAddAllShotAnimationOutputsOperator)
    bpy.utils.register_class(StalkerSceneAddAllShotLightingOutputsOperator)
    bpy.utils.register_class(StalkerSceneAddAllShotCompOutputsOperator)

    bpy.utils.register_class(StalkerShotAddAllTaskOutputsOperator)
    bpy.utils.register_class(StalkerShotAddPrevisOutputOperator)
    bpy.utils.register_class(StalkerShotAddAnimationOutputOperator)
    bpy.utils.register_class(StalkerShotAddLightingOutputOperator)
    bpy.utils.register_class(StalkerShotAddCompOutputOperator)

    bpy.types.SEQUENCER_MT_add.append(draw_stalker_menu)


def unregister():
    """unregister the addon"""
    bpy.utils.unregister_class(StalkerMenu)
    bpy.utils.unregister_class(StalkerAddFromProjectMenu)

    bpy.utils.unregister_class(StalkerSceneAddEverythingOperator)
    bpy.utils.unregister_class(StalkerSceneAddStoryboardOperator)
    bpy.utils.unregister_class(StalkerSceneAddPrevisOperator)

    bpy.utils.unregister_class(StalkerSceneAddAllShotOutputsOperator)
    bpy.utils.unregister_class(StalkerSceneAddAllShotPrevisOutputsOperator)
    bpy.utils.unregister_class(StalkerSceneAddAllShotAnimationOutputsOperator)
    bpy.utils.unregister_class(StalkerSceneAddAllShotLightingOutputsOperator)
    bpy.utils.unregister_class(StalkerSceneAddAllShotCompOutputsOperator)

    bpy.utils.unregister_class(StalkerShotAddPrevisOutputOperator)
    bpy.utils.unregister_class(StalkerShotAddAnimationOutputOperator)
    bpy.utils.unregister_class(StalkerShotAddLightingOutputOperator)
    bpy.utils.unregister_class(StalkerShotAddCompOutputOperator)

    # unregister dynamically created menu items
    for opclass in registered_menus:
        bpy.utils.unregister_class(opclass)

    bpy.types.SEQUENCER_MT_add.remove(draw_stalker_menu)


if __name__ == "__main__":
    register()
