'''
    Wrappers and helper classes to work with Softimage.
'''
from win32com.client import constants as const
import win32com
STR_CODEC = 'utf-8'
xsi = win32com.client.Dispatch('XSI.Application')
xsiui = win32com.client.Dispatch('XSI.UIToolkit')
xsimath = win32com.client.Dispatch('XSI.Math')


class SoftimageError(Exception):
    def __init__(self, value):
        self.value = value

    def __repr__(self):
        return 'SofimageError: {0}'.format(self.value)


class Softimage(object):
    @staticmethod
    def get_objects(name):
        xsifact = win32com.client.Dispatch('XSI.Factory')
        collection = xsifact.CreateObject('XSI.Collection')
        collection.items = name
        if collection.Count > 0:
            return list(collection)
        return ()

    @staticmethod
    def get_object(name):
        try:
            return xsi.Dictionary.GetObject(name)
        except win32com.client.pythoncom.com_error:
            print 'COM ERROR'
            return None

    @staticmethod
    def get_plugin_origin(plugin_name):
        xsi = win32com.client.Dispatch('XSI.Application')
        plugins = xsi.Plugins
        for plugin in plugins:
            if plugin.Name == plugin_name:
                return plugin.OriginPath[:-20]

    @staticmethod
    def msg(message, msg_type=const.siMsgOkOnly, caption='Information'):
        return xsiui.MsgBox(message, msg_type, caption)

    @classmethod
    def get_all_children(cls, root):
        '''Recursively adds children to a list.'''
        children = []
        children.append(root)
        if root.Children(0):
            chldrn = list(root.Children)
            for child in chldrn:
                children.extend(cls.get_all_children(child))
        return children

    @staticmethod
    def get_playcontrol():
        return xsi.Dictionary.GetObject('PlayControl')


class SIScene(object):
    def get_playcontrol(self):
        return xsi.Dictionary.GetObject('PlayControl')

    def get_name(self):
        return xsi.ActiveProject.ActiveScene.Name

    def wirecol(self, r, g, b):
        wr = int(round(r * 7)) << 1
        wg = int(round(g * 7)) << 4
        wb = int(round(b * 7)) << 7
        return wr + wg + wb


class SIModel(object):
    def __init__(self, si_model):
        self.si_model = si_model
        self.geo = si_model.ActivePrimitive.GetGeometry2(0)

    def get_geo(self, model):
        if model:
            return model.ActivePrimitive.GetGeometry2(0)
        else:
            return self.si_model.ActivePrimitive.GetGeometry2(0)

    def get_uv_props(self):
        cls = self.si_model.ActivePrimitive.GetGeometry2(0).Clusters
        sample = cls.Filter('sample')
        for cls in sample:
            props = cls.Properties.Filter('uvspace')
            if props.Count >= 1:
                return props

    def get_normal_props(self):
        clusters = self.geo.Clusters.Filter('sample')
        for cluster in clusters:
            normal_clusters = cluster.Properties.Filter('normal')
            if normal_clusters.Count >= 1:
                return normal_clusters

    def set_normals(self, index, normals):
        prop = self.get_normal_props()[index]
        prop.Elements.Array = normals

    def get_envelope_props(self):
        pnt_clusters = self.geo.Clusters.Filter('pnt')
        for cluster in pnt_clusters:
            properties = cluster.Properties
            weight_props = properties.Filter('envweights')
            if weight_props.Count >= 1:
                return weight_props

    def get_vertex_color_props(self):
        cls = self.si_model.ActivePrimitive.GetGeometry2(0).Clusters
        sample = cls.Filter('sample')
        for cls in sample:
            props = cls.Properties.Filter('vertexcolor')
            if props.Count >= 1:
                return props

    def set_weights(self, index, weights):
        prop = self.get_envelope_props()[index]
        prop.Elements.Array = weights
        return True

    def set_uvs(self, index, uvs):
        prop = self.get_uv_props()[index]
        prop.Elements.Array = uvs
        return True

    def set_vertex_colors(self, index, colors):
        prop = self.get_vertex_color_props()[index]
        prop.Elements.Array = colors
        return True

    def get_vis(self, model):
        vis = model.Properties.Filter('visibility')
        prop = vis[0]
        val = prop.viewvis.Value
        if val:
            return True
        return False

    def get_transform_quat(self, model):
        transform = model.Kinematics.Local.Transform
        quat = xsimath.CreateQuaternion()
        transform.GetRotationQuaternion(quat)
        sca = transform.SclX, transform.SclY, transform.SclZ
        rot = quat.X, quat.Y, quat.Z, quat.W
        tra = transform.PosX, transform.PosY, transform.PosZ
        return tra, rot, sca

    def is_uved(self):
        sample_clusters = self.geo.Clusters.Filter('sample')
        for cls in sample_clusters:
            props = cls.Properties
            uv_props = props.Filter('uvspace')
            if uv_props.Count > 0:
                return True

    def is_colored(self):
        sample_clusters = self.geo.Clusters.Filter('sample')
        for cls in sample_clusters:
            props = cls.Properties
            vcol_props = props.Filter('vertexcolor')
            if vcol_props.Count > 0:
                return True

    def is_weighted(self):
        pnt_clusters = self.geo.Clusters.Filter('pnt')
        for cluster in pnt_clusters:
            properties = cluster.Properties
            weight_props = properties.Filter('envweights')
            if weight_props.Count > 0:
                return True


class SIMaterial(object):
    def get_clip_source_name(self, clip):
        src = clip.Source
        filename = src.Parameters('FileName').Value
        filename_list = filename.split('\\')
        return filename_list[-1].encode(STR_CODEC)

    def get_shader_color(self, shader, color):
        col = shader.Parameters(color).Value
        return col.Red, col.Green, col.Blue, col.Alpha


class SIGeneral(object):
    @staticmethod
    def get_plugin_origin(plugin_name):
        xsi = win32com.client.Dispatch('XSI.Application')
        plugins = xsi.Plugins
        for plugin in plugins:
            if plugin.Name == plugin_name:
                return plugin.OriginPath[:-20]

    def get_objects_by_name(self, modelnames):
        models = []
        for name in modelnames:
            xsifact = win32com.client.Dispatch('XSI.Factory')
            collection = xsifact.CreateObject('XSI.Collection')
            collection.items = name
            models.append(collection(0))
        return models

    def msg(self, message, msg_type=const.siMsgOkOnly, plugin='XSIZETools'):
        return xsiui.MsgBox(message, msg_type, plugin)

    def get_obj_by_name(self, name):
        xsifact = win32com.client.Dispatch('XSI.Factory')
        collection = xsifact.CreateObject('XSI.Collection')
        collection.items = name
        if collection.count == 1:
            return collection(0)

    def check_existence(self, name):
        xsifact = win32com.client.Dispatch('XSI.Factory')
        collection = xsifact.CreateObject('XSI.Collection')
        collection.items = name
        return collection.Count

    def get_main_material_name(self, obj):
        if obj.Material:
            return obj.Material.Name
        return 'Scene_Material'

    def get_all_children(self, root):
        if root:
            return self._get_all_children(root)
        else:
            return None

    def _get_all_children(self, root):
        '''Recursively adds children to a list.'''
        children = []
        children.append(root)
        if root.Children(0):
            chldrn = list(root.Children)
            for child in chldrn:
                children.extend(self.get_all_children(child))
        return children

    def unique_materials(self, obj_list):
        materials = self.all_materials(obj_list)
        unique = []
        unique_names = []
        for mat in materials:
            if mat.Name not in unique_names:
                unique.append(mat)
                unique_names.append(mat.Name)
        return unique

    def all_materials(self, obj_list):
        materials = []
        for obj in obj_list:
            # Non-polymsh objects sometimes dont have a
            # material, so just ignore them.
            if obj.Type != 'polymsh':
                continue
            geo = obj.ActivePrimitive.GetGeometry2(0)
            if obj.Material:
                materials.append(obj.Material)
            else:
                materials.append('Scene_Material')
            for cls in geo.Clusters:
                if cls.Material:
                    materials.append(cls.Material)
        return materials

    def material_name_dict(self):
        dict_ = {}
        lib = self.xsi.ActiveProject.ActiveScene.MaterialLibraries[0]
        for mat in lib.Items:
            dict_[mat.Name] = mat
        return dict_


class SIProgressBar(object):
    def __init__(self):
        self.bar = xsiui.ProgressBar

    def show(self):
        self.bar.Visible = True

    def hide(self):
        self.bar.Visible = False

    def set(self, maximum, capt, val=0, step=1):
        self.bar.Value = val
        if maximum:
            self.bar.Maximum = maximum
        else:
            self.bar.Maximum = 1
        self.bar.Caption = capt
        self.bar.Step = step

    def setc(self, capt):
        self.bar.Caption = capt

    def inc(self):
        self.bar.Increment()
