<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="users" class="UserStatechart"/>
        <association name="executor" class="Executor" min="1" max="1"/>
    </relationships>
    <constructor>
        <parameter name="params"/>
        <body>
            <![CDATA[
            self.mvk = None
            self.users = []
            self.user_statecharts = {"__hierarchy": None}
            self.input_queue = []
            self.user_queue = {}
            self.source = None

            self.params = params
            ]]>
        </body>
    </constructor>
    <scxml initial="init">
        <state id="init">
            <!-- XXX For HTTP sockets
            <onentry>
                <raise scope="cd" event="create_instance">
                    <parameter expr="'to_mvs'"/>
                    <parameter expr="'HTTPClient'"/>
                    <parameter expr="'127.0.0.1'"/>
                    <parameter expr="8000"/>
                </raise>
            </onentry>
            -->
            <onentry>
                <raise scope="cd" event="create_instance">
                    <parameter expr="'to_mvs'"/>
                    <parameter expr="'LocalMvS'"/>
                    <parameter expr="self.params"/>
                </raise>
            </onentry>
            <transition event="instance_created" target="../get_root">
                <parameter name="instancename"/>
                <raise scope="cd" event="start_instance">
                    <parameter expr="instancename"/>
                </raise>
            </transition>
        </state>

        <state id="get_root">
            <transition event="http_client_ready" target=".">
                <raise event="HTTP_input" scope="narrow" target="'to_mvs'">
                    <parameter expr="'requests=%s' % json.dumps([['RR', []]])"/>
                    <parameter expr="'parent'"/>
                </raise>
            </transition>

            <transition event="HTTP_output" target="../init_server">
                <parameter name="data"/>
                <script>
                    self.mvk = ModelverseKernel(json.loads(data["data"])[0][0])
                    self.mvk_next_op = "load_primitives"
                    self.mvk_params = []
                </script>
            </transition>
        </state>

        <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'"/>
                    <parameter expr="self.mvk"/>
                </raise>
            </onentry>
            <transition event="instance_created" target="../running">
                <parameter name="instancename"/>
                <raise scope="cd" event="start_instance">
                    <parameter expr="instancename"/>
                </raise>
            </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
                            try:
                                if data["op"] == "set_input":
                                    if data["element_type"] == "V":
                                        value = json.loads(data["value"])
                                    else:
                                        value = data["value"]
                                    args = (data["element_type"], value)
                                    self.input_queue.append((source, "set_input", args, data["username"]))
                                elif data["op"] == "get_output":
                                    args = []
                                    self.input_queue.append((source, "get_output", args, data["username"]))
                                else:
                                    print("DROPPING unknown operation: " + str(data["op"]))
                            except:
                                import traceback
                                traceback.print_exc()
                                print("DROPPING deserialization error")
                                print("Decoding value " + str(data["value"]))
                        </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.user_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.user_statecharts" target=".">
                        <!-- Move it to the back of the list, so we can process other users first -->
                        <script>
                            self.user_queue.setdefault(self.input_queue[0][3], []).append(self.input_queue.pop(0))
                        </script>
                        <!-- Force a refresh too -->
                        <raise event="force_user_refresh"/>
                    </transition>

                </state>

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

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

            <state id="find_users" initial="get_all_links">
                <state id="get_all_links">
                    <onentry>
                        <raise event="HTTP_input" scope="narrow" target="'to_mvs'">
                            <parameter expr="'requests=%s' % json.dumps([['RDK', [self.mvk.root]]])"/>
                            <parameter expr="'parent'"/>
                        </raise>
                    </onentry>

                    <transition event="HTTP_output" target="../retrieve_users">
                        <parameter name="data"/>
                        <script>
                            self.users = json.loads(data["data"])[0][0]
                        </script>
                    </transition>
                </state>

                <state id="retrieve_users">
                    <transition cond="self.users" target="../got_usernames">
                        <raise event="HTTP_input" scope="narrow" target="'to_mvs'">
                            <parameter expr="'requests=%s' % json.dumps([['RV', [user]] for user in self.users])"/>
                            <parameter expr="'parent'"/>
                        </raise>
                    </transition>
                    <transition cond="not self.users" target="../wait"/>
                </state>

                <state id="got_usernames">
                    <transition event="HTTP_output" target="../process_users">
                        <parameter name="data"/>
                        <script>
                            self.users = [v[0] for v in json.loads(data["data"]) if v[0] not in self.user_statecharts]
                        </script>
                    </transition>
                </state>

                <state id="process_users">
                    <transition cond="not self.users" target="../wait"/>
                    <transition cond="self.users" target="../creating_user">
                        <raise event="create_instance" scope="cd">
                            <parameter expr="'users'"/>
                            <parameter expr="'UserStatechart'"/>
                            <parameter expr="self.users[0]"/>
                        </raise>
                    </transition>
                </state>

                <state id="creating_user">
                    <transition event="instance_created" target="../retrieve_users">
                        <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.user_statecharts[self.users[0]] = instancename
                            # Repush all this user's messages in the input queue
                            self.input_queue.extend(self.user_queue.pop(self.users[0], []))
                            self.users.pop(0)
                        </script>
                    </transition>
                </state>

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