import subprocess
import warnings

from freemoovr.proxy.base_zmq import MultiServerBaseZMQ


class _OSGFileParsingMixin(object):

    @staticmethod
    def get_animations(path):
        return _OSGFileParsingMixin._parse_osg_file(path)[1]

    @staticmethod
    def get_nodes(path):
        return _OSGFileParsingMixin._parse_osg_file(path)[0]

    @staticmethod
    def _parse_osg_file(name):
        """returns a list of named nodes and a list of animations present in the osg file

        NOTE: `parseosg` executable needs to be in path
        """
        stdout = subprocess.check_output("parseosg %s" % name, shell=True)
        nodes = []
        animations = []
        for line in stdout.split('\n'):
            if line and ('=' in line):
                thing, name = line.split('=')
                if thing == 'MatrixTransformNode':
                    nodes.append(name)
                elif thing == 'Animation':
                    animations.append(name)
        return nodes, animations


class StimulusOSGController(MultiServerBaseZMQ, _OSGFileParsingMixin):
    """compatible with the fishvr StimulusOSGController"""

    def __init__(self, *args, **kwargs):
        super(StimulusOSGController, self).__init__(*args, **kwargs)
        self.wait_for_servers(error_after=10.0)
        self.set_stimulus_plugin('StimulusOSG')

    def load_osg(self, f):
        self.send({'osg_filename': {'data': f}})

    def move_world(self, x, y, z, orientation_x=0, orientation_y=0, orientation_z=0):
        self.send({'osg_model_pose': {'position': {'x': x, 'y': y, 'z': z},
                                      'orientation': {'w': 1,
                                                      'x': orientation_x, 'y': orientation_y, 'z': orientation_z
                                     }}})

    def scale_world(self, scale_x, scale_y, scale_z):
        self.send({'osg_model_scale': {'x': scale_x, 'y': scale_y, 'z': scale_z}})

    def move_node(self, name, x, y, z, scale=1, hidden=False, orientation_x=0, orientation_y=0, orientation_z=0):
        self.send({'osg_submodel_pose': {'header': {'frame_id': name},
                                         'pose': {'position': {'x': x, 'y': y, 'z': z},
                                                  'orientation': {
                                                      'w': 0 if hidden else scale,
                                                      'x': orientation_x, 'y': orientation_y, 'z': orientation_z
                                                  }}}})

    def animation_start(self, name):
        warnings.warn('animation in StimulusOSGController is deprecated. please use StimulusOSG2Controller')
        self.send({'osg_animation_start': {'data': name}})

    def animation_stop(self, name):
        warnings.warn('animation in StimulusOSGController is deprecated. please use StimulusOSG2Controller')
        self.send({'osg_animation_stop': {'data': name}})


class _OSG2FileHandle(object):
    """Handle class returned by the StimulusOSG2Controller.load_osg(path)

    do not instantiate directly.
    """

    def __init__(self, proxy, filename):
        self._p = proxy
        self._f = filename
        self._hidden = False
        self._scale = 1
        self._x0 = self._y0 = self._z0 = self._ox0 = self._oy0 = self._oz0 = 0.0
        self._x = self._y = self._z = self._ox = self._oy = self._oz = 0.0

    def _transform(self, x0, y0, z0, ox0, oy0, oz0):
        self._x0 = x0
        self._y0 = y0
        self._z0 = z0
        self._ox0 = ox0
        self._oy0 = oy0
        self._oz0 = oz0
        self.move(x=self._x, y=self._y, z=self._z,
                  hidden=self._hidden, scale=self._scale,
                  orientation_x=self._ox0, orientation_y=self._oy0, orientation_z=self._oz0)

    def move(self, x=0, y=0, z=0, scale=1, hidden=False, orientation_x=0, orientation_y=0, orientation_z=0):
        self._p.send({'osg2_submodel_pose': {'header': {'frame_id': self._f},
                                             'pose': {'position': {'x': x + self._x0, 'y': y + self._y0, 'z': z + self._z0},
                                                      'orientation': {
                                                          'w': 0 if hidden else scale,
                                                          'x': orientation_x + self._ox0, 'y': orientation_y + self._oy0, 'z': orientation_z + self._oz0
                                                      }}}})
        self._x = x
        self._y = y
        self._z = z
        self._ox = orientation_x
        self._oy = orientation_y
        self._oz = orientation_z
        self._scale = scale
        self._hidden = hidden

    def hide(self):
        self.move(hidden=True)

    def fade_in(self):
        self._p.send({'osg2_submodel_fade_in': {'data': self._f}})

    def fade_out(self):
        self._p.send({'osg2_submodel_fade_out': {'data': self._f}})

    def animation_start(self, name):
        self._p.send({'osg2_animation_start': {'data': "%s|%s" % (self._f, name)}})

    def animation_stop(self, name):
        self._p.send({'osg2_animation_stop': {'data': "%s|%s" % (self._f, name)}})

    def unload(self):
        self._p.load_osg(self._f + '.unload')

    def clone(self, new_name, how='auto'):
        new_name = "%s+%s+%s" % (self._f, new_name, {'shallow': 's', 'deep': 'd', 'auto': 'a'}.get(how, 'a'))
        self._p.send({'osg2_filename_duplicate': {'data': new_name}})
        return _OSG2FileHandle(self._p, new_name)


class StimulusOSG2Controller(MultiServerBaseZMQ, _OSGFileParsingMixin):
    """compatible with the fishvr StimulusOSG2Controller"""

    def __init__(self, *args, **kwargs):
        super(StimulusOSG2Controller, self).__init__(*args, **kwargs)
        self.wait_for_servers(error_after=10.0)
        self.set_stimulus_plugin('StimulusOSG2')

    def move_world(self, x=0, y=0, z=0, scale=1, orientation_x=0, orientation_y=0, orientation_z=0):
        raise NotImplementedError

    def set_animation_duration(self, v):
        self.send({'osg2_animation_duration': {'data': float(v)}})

    def unload_all(self):
        self.send({'osg2_filename': {'data': '/dev/null'}})

    def load_osg(self, path):
        self.send({'osg2_filename': {'data': path}})
        return _OSG2FileHandle(self, path)


if __name__ == "__main__":
    import sys

    # noinspection PyProtectedMember
    n, a = _OSGFileParsingMixin._parse_osg_file(sys.argv[1])
    print "nodes:", ",".join(n)
    print "animations:", ",".join(a)

import subprocess
import warnings

from freemoovr.proxy.base_zmq import MultiServerBaseZMQ


class _OSGFileParsingMixin(object):

    @staticmethod
    def get_animations(path):
        return _OSGFileParsingMixin._parse_osg_file(path)[1]

    @staticmethod
    def get_nodes(path):
        return _OSGFileParsingMixin._parse_osg_file(path)[0]

    @staticmethod
    def _parse_osg_file(name):
        """returns a list of named nodes and a list of animations present in the osg file

        NOTE: `parseosg` executable needs to be in path
        """
        stdout = subprocess.check_output("parseosg %s" % name, shell=True)
        nodes = []
        animations = []
        for line in stdout.split('\n'):
            if line and ('=' in line):
                thing, name = line.split('=')
                if thing == 'MatrixTransformNode':
                    nodes.append(name)
                elif thing == 'Animation':
                    animations.append(name)
        return nodes, animations


class StimulusOSGController(MultiServerBaseZMQ, _OSGFileParsingMixin):
    """compatible with the fishvr StimulusOSGController"""

    def __init__(self, *args, **kwargs):
        super(StimulusOSGController, self).__init__(*args, **kwargs)
        self.wait_for_servers(error_after=10.0)
        self.set_stimulus_plugin('StimulusOSG')

    def load_osg(self, f):
        self.send({'osg_filename': {'data': f}})

    def move_world(self, x, y, z, orientation_x=0, orientation_y=0, orientation_z=0):
        self.send({'osg_model_pose': {'position': {'x': x, 'y': y, 'z': z},
                                      'orientation': {'w': 1,
                                                      'x': orientation_x, 'y': orientation_y, 'z': orientation_z
                                     }}})

    def scale_world(self, scale_x, scale_y, scale_z):
        self.send({'osg_model_scale': {'x': scale_x, 'y': scale_y, 'z': scale_z}})

    def move_node(self, name, x, y, z, scale=1, hidden=False, orientation_x=0, orientation_y=0, orientation_z=0):
        self.send({'osg_submodel_pose': {'header': {'frame_id': name},
                                         'pose': {'position': {'x': x, 'y': y, 'z': z},
                                                  'orientation': {
                                                      'w': 0 if hidden else scale,
                                                      'x': orientation_x, 'y': orientation_y, 'z': orientation_z
                                                  }}}})

    def animation_start(self, name):
        warnings.warn('animation in StimulusOSGController is deprecated. please use StimulusOSG2Controller')
        self.send({'osg_animation_start': {'data': name}})

    def animation_stop(self, name):
        warnings.warn('animation in StimulusOSGController is deprecated. please use StimulusOSG2Controller')
        self.send({'osg_animation_stop': {'data': name}})


class _OSG2FileHandle(object):
    """Handle class returned by the StimulusOSG2Controller.load_osg(path)

    do not instantiate directly.
    """

    def __init__(self, proxy, filename):
        self._p = proxy
        self._f = filename
        self._hidden = False
        self._scale = 1
        self._x0 = self._y0 = self._z0 = self._ox0 = self._oy0 = self._oz0 = 0.0
        self._x = self._y = self._z = self._ox = self._oy = self._oz = 0.0

    def _transform(self, x0, y0, z0, ox0, oy0, oz0):
        self._x0 = x0
        self._y0 = y0
        self._z0 = z0
        self._ox0 = ox0
        self._oy0 = oy0
        self._oz0 = oz0
        self.move(x=self._x, y=self._y, z=self._z,
                  hidden=self._hidden, scale=self._scale,
                  orientation_x=self._ox0, orientation_y=self._oy0, orientation_z=self._oz0)

    def move(self, x=0, y=0, z=0, scale=1, hidden=False, orientation_x=0, orientation_y=0, orientation_z=0):
        self._p.send({'osg2_submodel_pose': {'header': {'frame_id': self._f},
                                             'pose': {'position': {'x': x + self._x0, 'y': y + self._y0, 'z': z + self._z0},
                                                      'orientation': {
                                                          'w': 0 if hidden else scale,
                                                          'x': orientation_x + self._ox0, 'y': orientation_y + self._oy0, 'z': orientation_z + self._oz0
                                                      }}}})
        self._x = x
        self._y = y
        self._z = z
        self._ox = orientation_x
        self._oy = orientation_y
        self._oz = orientation_z
        self._scale = scale
        self._hidden = hidden

    def hide(self):
        self.move(hidden=True)

    def fade_in(self):
        self._p.send({'osg2_submodel_fade_in': {'data': self._f}})

    def fade_out(self):
        self._p.send({'osg2_submodel_fade_out': {'data': self._f}})

    def animation_start(self, name):
        self._p.send({'osg2_animation_start': {'data': "%s|%s" % (self._f, name)}})

    def animation_stop(self, name):
        self._p.send({'osg2_animation_stop': {'data': "%s|%s" % (self._f, name)}})

    def unload(self):
        self._p.load_osg(self._f + '.unload')

    def clone(self, new_name, how='auto'):
        new_name = "%s+%s+%s" % (self._f, new_name, {'shallow': 's', 'deep': 'd', 'auto': 'a'}.get(how, 'a'))
        self._p.send({'osg2_filename_duplicate': {'data': new_name}})
        return _OSG2FileHandle(self._p, new_name)


class StimulusOSG2Controller(MultiServerBaseZMQ, _OSGFileParsingMixin):
    """compatible with the fishvr StimulusOSG2Controller"""

    def __init__(self, *args, **kwargs):
        super(StimulusOSG2Controller, self).__init__(*args, **kwargs)
        self.wait_for_servers(error_after=10.0)
        self.set_stimulus_plugin('StimulusOSG2')

    def move_world(self, x=0, y=0, z=0, scale=1, orientation_x=0, orientation_y=0, orientation_z=0):
        raise NotImplementedError

    def set_animation_duration(self, v):
        self.send({'osg2_animation_duration': {'data': float(v)}})

    def unload_all(self):
        self.send({'osg2_filename': {'data': '/dev/null'}})

    def load_osg(self, path):
        self.send({'osg2_filename': {'data': path}})
        return _OSG2FileHandle(self, path)


if __name__ == "__main__":
    import sys

    # noinspection PyProtectedMember
    n, a = _OSGFileParsingMixin._parse_osg_file(sys.argv[1])
    print "nodes:", ",".join(n)
    print "animations:", ",".join(a)

returns a list of named nodes and a list of animations present in the osg file          NOTE: `parseosg` executable needs to be in path compatible with the fishvr StimulusOSGController Handle class returned by the StimulusOSG2Controller.load_osg(path)      do not instantiate directly. compatible with the fishvr StimulusOSG2Controller # noinspection PyProtectedMember