from UM.Backend.Backend import Backend
from UM.Application import Application
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Preferences import Preferences

from . import Cura_pb2
from . import ProcessSlicedObjectListJob

class CuraEngineBackend(Backend):
    def __init__(self):
        super().__init__()

        self._scene = Application.getInstance().getController().getScene()
        self._scene.sceneChanged.connect(self._onSceneChanged)

        self._message_handlers[Cura_pb2.SlicedObjectList] = self._onSlicedObjectListMessage
        self._message_handlers[Cura_pb2.Progress] = self._onProgressMessage
        self._message_handlers[Cura_pb2.GCode] = self._onGCodeMessage
        self._message_handlers[Cura_pb2.ObjectPrintTime] = self._onObjectPrintTimeMessage

    def getEngineCommand(self):
        return [Preferences.getPreference("BackendLocation"), '--connect', "127.0.0.1:{0}".format(self._port)]

    def _onSceneChanged(self, source):
        if (type(source) is not SceneNode) or (source is self._scene.getRoot()):
            return

        objects = []
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                objects.append(node)

        msg = Cura_pb2.ObjectList()
        for object in objects:
            meshData = object.getMeshData()

            obj = msg.objects.add()
            obj.id = id(object)

            verts = meshData.getVertices()
            verts[:,[1,2]] = verts[:,[2,1]]
            obj.vertices = verts.tostring()

            #if meshData.hasNormals():
                #obj.normals = meshData.getNormalsAsByteArray()

            #if meshData.hasIndices():
                #obj.indices = meshData.getIndicesAsByteArray()

        self._socket.sendMessage(msg)

    def _onSlicedObjectListMessage(self, message):
        job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
        job.start()

    def _onProgressMessage(self, message):
        self.processingProgress.emit(message.amount)

    def _onGCodeMessage(self, message):
        pass

    def _onObjectPrintTimeMessage(self, message):
        pass

    def _createSocket(self):
        super()._createSocket()
        
        self._socket.registerMessageType(1, Cura_pb2.ObjectList)
        self._socket.registerMessageType(2, Cura_pb2.SlicedObjectList)
        self._socket.registerMessageType(3, Cura_pb2.Progress)
        self._socket.registerMessageType(4, Cura_pb2.GCode)
        self._socket.registerMessageType(5, Cura_pb2.ObjectPrintTime)

@@ -283,16 +283,64 @@
 tListJob
+%0Afrom . import ProcessGCodeJob%0A%0Aimport threading
 %0A%0Aclass 
@@ -556,16 +556,54 @@
 Changed)
+%0A        self._sceneChangeTimer = None
 %0A%0A      
@@ -1221,742 +1221,176 @@
     
-objects = %5B%5D%0A        for node in DepthFirstIterator(self._scene.getRoot()):%0A            if type(node) is SceneNode and node.getMeshData():%0A                objects.append(node)%0A%0A        msg = Cura_pb2.ObjectList()%0A        for object in objects:%0A            meshData = object.getMeshData()%0A%0A            obj = msg.objects.add()%0A            obj.id = id(object)%0A%0A            verts = meshData.getVertices()%0A            verts%5B:,%5B1,2%5D%5D = verts%5B:,%5B2,1%5D%5D%0A            obj.vertices = verts.tostring()%0A%0A            #if meshData.hasNormals():%0A                #obj.normals = meshData.getNormalsAsByteArray()%0A%0A            #if meshData.hasIndices():%0A                #obj.indices = meshData.getIndicesAsByteArray()%0A%0A        self._socket.sendMessage(msg
+if self._sceneChangeTimer:%0A            return%0A%0A        self._sceneChangeTimer = threading.Timer(1, self._sceneChangeTimerFinished)%0A        self._sceneChangeTimer.start(
 )%0A%0A 
@@ -1674,36 +1674,98 @@
 ssage):%0A        
-pass
+job = ProcessGCodeJob.ProcessGCodeJob(message)%0A        job.start()
 %0A%0A    def _onObj
@@ -2213,8 +2213,833 @@
 ntTime)%0A
+%0A    def _sceneChangeTimerFinished(self):%0A        objects = %5B%5D%0A        for node in DepthFirstIterator(self._scene.getRoot()):%0A            if type(node) is SceneNode and node.getMeshData():%0A                objects.append(node)%0A%0A        msg = Cura_pb2.ObjectList()%0A        for object in objects:%0A            meshData = object.getMeshData()%0A%0A            obj = msg.objects.add()%0A            obj.id = id(object)%0A%0A            verts = meshData.getVertices()%0A            verts%5B:,%5B1,2%5D%5D = verts%5B:,%5B2,1%5D%5D%0A            obj.vertices = verts.tostring()%0A%0A            #if meshData.hasNormals():%0A                #obj.normals = meshData.getNormalsAsByteArray()%0A%0A            #if meshData.hasIndices():%0A                #obj.indices = meshData.getIndicesAsByteArray()%0A%0A        self._socket.sendMessage(msg)%0A%0A        self._sceneChangeTimer = None%0A

Handle GCode messages and delay sending object updates until a timer is expired