<class name="MvKController">
    <relationships>
        <association name="to_mvs" class="HTTPClient" min="1" max="1"/>
        <association name="to_mvi" class="Server" min="1" max="1"/>
        <association name="tasks" class="UserStatechart"/>
        <association name="executor" class="Executor" min="1" max="1"/>
    </relationships>
    <constructor>
        <parameter name="params"/>
        <body>
            <![CDATA[
            self.tasks = []
            self.task_statecharts = {"__hierarchy": None}
            self.input_queue = []
            self.task_queue = {}
            self.source = None
            self.root = None

            self.params = params
            ]]>
        </body>
    </constructor>
    <scxml initial="init_server">
        <state id="init_server">
            <onentry>
                <raise scope="cd" event="create_instance">
                    <parameter expr="'to_mvi'"/>
                    <parameter expr="'Server'"/>
                    <parameter expr="''"/>
                    <parameter expr="8001"/>
                </raise>
            </onentry>
            <transition event="instance_created" target="../init_executor">
                <parameter name="instancename"/>
                <raise scope="cd" event="start_instance">
                    <parameter expr="instancename"/>
                </raise>
            </transition>
        </state>

        <state id="init_executor">
            <onentry>
                <raise scope="cd" event="create_instance">
                    <parameter expr="'executor'"/>
                    <parameter expr="'Executor'"/>
                </raise>
            </onentry>
            <transition event="instance_created" target="../get_mvk">
                <parameter name="instancename"/>
                <raise scope="cd" event="start_instance">
                    <parameter expr="instancename"/>
                </raise>
            </transition>
        </state>

        <state id="get_mvk">
            <onentry>
                <raise event="get_mvk" scope="narrow" target="'executor'">
                    <parameter expr="'parent'"/>
                </raise>
            </onentry>

            <transition event="get_mvk_reply" target="../read_root">
                <parameter name="mvk"/>
                <script>
                    self.mvk = mvk
                </script>
            </transition>
        </state>

        <state id="read_root">
            <onentry>
                <raise event="raw_exec" scope="narrow" target="'executor'">
                    <parameter expr="[['RR', []]]"/>
                    <parameter expr="'parent'"/>
                </raise>
            </onentry>

            <transition event="raw_exec_reply" target="../running">
                <parameter name="data"/>
                <script>
                    self.root = data[0][0]
                </script>
            </transition>
        </state>

        <parallel id="running">
            <state id="wait_for_requests">
                <state id="wait">
                    <transition event="from_mvi" target=".">
                        <parameter name="source"/>
                        <parameter name="data"/>
                        <script>                                
                            # No JSON encoding necessary, as it is not complex
                            print("Got from MVI")
                            try:
                                args = None
                                if data["op"] == "set_input":
                                    if "value" in data:
                                        value = [json.loads(data["value"])]
                                    else:
                                        value = json.loads(data["data"])
                                    for v in value[:-1]:
                                        self.input_queue.append((None, data["op"], [v], data["taskname"]))
                                    self.input_queue.append((source, data["op"], [value[-1]], data["taskname"]))
                                    print("Added to input queue")
                                else:
                                    print("Appending " + data["op"])
                                    self.input_queue.append((source, data["op"], args, data["taskname"]))
                            except ValueError:
                                print("Error when deserializing request: " + str(data))
                                raise
                        </script>
                    </transition>
                </state>
            </state>

            <state id="push" initial="wait">
                <state id="wait">
                    <transition cond="self.input_queue and self.input_queue[0][3] in self.task_statecharts" target="../process"/>
                    <!-- User is not yet detected, so stall this message -->
                    <transition cond="self.input_queue and self.input_queue[0][3] not in self.task_statecharts" target=".">
                        <!-- Move it to the back of the list, so we can process other tasks first -->
                        <script>
                            print("Queue task")
                            self.task_queue.setdefault(self.input_queue[0][3], []).append(self.input_queue.pop(0))
                        </script>
                        <!-- Force a refresh too -->
                        <raise event="force_task_refresh"/>
                    </transition>

                </state>

                <state id="process">
                    <transition cond="self.input_queue[0][1] == 'set_input'" target="../wait">
                        <script>
                            source, op, args, taskname = self.input_queue.pop(0)
                        </script>
                        <raise event="set_input" scope="narrow" target="self.task_statecharts[taskname]">
                            <parameter expr="source"/>
                            <parameter expr="args"/>
                        </raise>
                    </transition>

                    <transition cond="self.input_queue[0][1] == 'get_output'" target="../wait">
                        <script>
                            source, op, args, taskname = self.input_queue.pop(0)
                        </script>
                        <raise event="get_output" scope="narrow" target="self.task_statecharts[taskname]">
                            <parameter expr="source"/>
                            <parameter expr="args"/>
                        </raise>
                    </transition>
                    
                    <transition cond="self.input_queue[0][1] == 'pause'" target="../wait">
                        <script>
                            print("Sending pause")
                            source, op, args, taskname = self.input_queue.pop(0)
                        </script>
                        <raise event="pause" scope="narrow" target="self.task_statecharts[taskname]">
                            <parameter expr="source"/>
                            <parameter expr="args"/>
                        </raise>
                    </transition>
                    
                    <transition cond="self.input_queue[0][1] == 'resume'" target="../wait">
                        <script>
                            source, op, args, taskname = self.input_queue.pop(0)
                        </script>
                        <raise event="resume" scope="narrow" target="self.task_statecharts[taskname]">
                            <parameter expr="source"/>
                            <parameter expr="args"/>
                        </raise>
                    </transition>
                </state>
            </state>

            <state id="find_tasks" initial="get_all_links">
                <state id="get_all_links">
                    <onentry>
                        <raise event="raw_exec" scope="narrow" target="'executor'">
                            <parameter expr="[['RDK', [self.root]]]"/>
                            <parameter expr="'parent'"/>
                        </raise>
                    </onentry>

                    <transition event="raw_exec_reply" target="../retrieve_tasks">
                        <parameter name="data"/>
                        <script>
                            self.tasks = data[0][0]
                        </script>
                    </transition>
                </state>

                <state id="retrieve_tasks">
                    <transition cond="self.tasks" target="../got_tasknames">
                        <raise event="raw_exec" scope="narrow" target="'executor'">
                            <parameter expr="[['RV', [task]] for task in self.tasks]"/>
                            <parameter expr="'parent'"/>
                        </raise>
                    </transition>
                    <transition cond="not self.tasks" target="../wait"/>
                </state>

                <state id="got_tasknames">
                    <transition event="raw_exec_reply" target="../process_tasks">
                        <parameter name="data"/>
                        <script>
                            data = set([v[0] for v in data])
                            self.add_tasks = [v for v in data if v not in self.task_statecharts]
                            self.delete_tasks = [v for v in self.task_statecharts if v not in data]
                        </script>
                    </transition>
                </state>

                <state id="process_tasks">
                    <transition cond="not self.add_tasks and not self.delete_tasks" target="../wait"/>
                    <transition cond="self.add_tasks and not self.delete_tasks" target="../creating_task">
                        <raise event="create_instance" scope="cd">
                            <parameter expr="'tasks'"/>
                            <parameter expr="'UserStatechart'"/>
                            <parameter expr="self.add_tasks[0]"/>
                            <parameter expr="self.mvk"/>
                        </raise>
                    </transition>
                    <transition cond="self.delete_tasks" target=".">
                        <raise event="delete_instance" scope="cd">
                            <parameter expr="self.task_statecharts.pop(self.delete_tasks.pop(0))"/>
                        </raise>
                    </transition>
                </state>

                <state id="creating_task">
                    <transition event="instance_created" target="../retrieve_tasks">
                        <parameter name="instancename"/>
                        <raise scope="cd" event="start_instance">
                            <parameter expr="instancename"/>
                        </raise>
                        <raise scope="narrow" target="instancename" event="set_returnpath">
                            <parameter expr="instancename"/>
                        </raise>
                        <script>
                            self.task_statecharts[self.add_tasks[0]] = instancename
                            # Repush all this task's messages in the input queue
                            self.input_queue.extend(self.task_queue.pop(self.add_tasks[0], []))
                            self.add_tasks.pop(0)
                        </script>
                    </transition>
                </state>

                <state id="wait">
                    <transition after="1.0" target="../get_all_links"/>
                    <transition event="force_task_refresh" target="../get_all_links"/>
                </state>
            </state>
        </parallel>
    </scxml>
</class>
