<class name="ChatServer">
    <relationships>
        <association name="server" class="NetworkServer"/>
    </relationships>

    <constructor>
        <parameter name="port"/>
        <body>
            <![CDATA[
            self.room2clients = {}
            self.client2room = {}
            self.serverinstance = None
            self.queue = []
            self.port = port

            self.tmp_sendlist = []
            self.tmp_data = None
            ]]>
        </body>
    </constructor>

    <scxml>
        <parallel id="server">
            <state id="processor" initial="init">
                <state id="init">
                    <onentry>
                        <raise scope="cd" event="create_instance">
                            <parameter expr="'server'"/>
                            <parameter expr="'NetworkServer'"/>
                            <parameter expr="self.port"/>
                        </raise>
                    </onentry>
                    <transition event="instance_created" target="../main">
                        <parameter name="instancename"/>
                        <script>
                            self.serverinstance = instancename
                        </script>
                        <raise scope="narrow" event="set_association_name" target="instancename">
                            <parameter expr="instancename"/>
                        </raise>
                        <raise scope="cd" event="start_instance">
                            <parameter expr="instancename"/>
                        </raise>
                    </transition>
                </state>
                <state id="main">
                    <transition cond="len(self.queue) > 0 and self.queue[0][1].startswith('ACK ')" target="."/>
                    <transition cond="len(self.queue) > 0 and self.queue[0][1].startswith('MSG ')" target="../forwarding">
                        <script>
                            socket, data = self.queue.pop(0)
                            roomnumber = self.client2room[socket]
                            self.tmp_sendlist = set(self.room2clients[roomnumber])
                            # Don't send to the original sender
                            # This also prevents infinite propagation to the other server
                            self.tmp_sendlist.remove(socket)
                            self.tmp_sendlist = list(self.tmp_sendlist)
                            self.tmp_data = data
                        </script>
                        <raise scope="broad" event="server_input">
                            <parameter expr="socket"/>
                            <parameter expr="'ACK MSG'"/>
                        </raise>
                    </transition>
                    <transition cond="len(self.queue) > 0 and self.queue[0][1].startswith('POLL')" target=".">
                        <script>
                            socket, data = self.queue.pop(0)
                        </script>
                        <raise scope="broad" event="server_input">
                            <parameter expr="socket"/>
                            <parameter expr="'ALIVE'"/>
                        </raise>
                    </transition>
                    <transition cond="len(self.queue) > 0 and self.queue[0][1].startswith('JOIN ')" target=".">
                        <script>
                            socket, data = self.queue.pop(0)
                            roomnumber = int(data[5:])
                            self.room2clients.setdefault(roomnumber, set()).add(socket)
                            self.client2room[socket] = roomnumber
                        </script>
                        <raise scope="broad" event="server_input">
                            <parameter expr="socket"/>
                            <parameter expr="'ACK JOIN %i' % roomnumber"/>
                        </raise>
                    </transition>
                    <transition cond="len(self.queue) > 0 and self.queue[0][1].startswith('LEAVE')" target=".">
                        <script>
                            socket, data = self.queue.pop(0)
                            roomnumber = self.client2room[socket]
                            self.room2clients[roomnumber].remove(socket)
                            del self.client2room[socket]
                        </script>
                        <raise scope="broad" event="server_input">
                            <parameter expr="socket"/>
                            <parameter expr="'ACK LEAVE'"/>
                        </raise>
                    </transition>
                </state>
                <state id="forwarding">
                    <transition cond="len(self.tmp_sendlist) > 0" target=".">
                        <raise scope="broad" event="server_input">
                            <parameter expr="self.tmp_sendlist.pop()"/>
                            <parameter expr="self.tmp_data"/>
                        </raise>
                    </transition>
                    <transition cond="len(self.tmp_sendlist) == 0" target="../main"/>
                </state>
            </state>

            <state id="receiver">
                <state id="queue">
                    <transition event="server_output" target=".">
                        <parameter name="socket"/>
                        <parameter name="data"/>
                        <script>
                            self.queue.append((socket, data))
                        </script>
                    </transition>
                </state>
            </state>
        </parallel>
    </scxml>
</class>
