#!python2
import os, sys
import socket
import subprocess

import Pyro4
from PyQt4 import QtCore, QtGui

import core
import screen
import screens

class FeedbackReader(QtCore.QThread):

    message_received = QtCore.pyqtSignal(unicode, tuple)

    def __init__(self, proxy):
        super(FeedbackReader, self).__init__()
        self.feedback = proxy

    def run(self):
        while True:
            feedback = self.feedback.get()
            core.log.debug("feedback: %r", feedback)
            if feedback:
                message, args = feedback
                self.message_received.emit(message, args)

class Panel(QtGui.QGroupBox):

    def __init__(self, controller, position, *args, **kwargs):
        super(Panel, self).__init__(position.title(), *args, **kwargs)
        self.instructions = controller
        self.position = position.lower()

        layout = QtGui.QVBoxLayout()
        self.selector = QtGui.QComboBox()
        self.selector.currentIndexChanged.connect(self.on_selector)

        layout.addWidget(self.selector)
        self.stack = QtGui.QStackedWidget()
        layout.addWidget(self.stack)
        self.setLayout(layout)

        for cls in screen.ScreenWidget.__subclasses__():
            self.selector.addItem(cls.name)
            self.stack.addWidget(cls(controller, position))

    def on_selector(self, index):
        core.log.debug("on_selector: %d, item %r", index, self.selector.itemText(index))
        self.stack.setCurrentIndex(index)
        screen_name = unicode(self.selector.itemText(index))
        self.instructions.send_command("SWITCH", self.position, screen_name)

class QuizController(QtGui.QWidget):

    COMMAND_MAILSLOT_NAME = "quiz"
    RESPONSE_MAILSLOT_NAME = "sub"

    def __init__(self, *args, **kwargs):
        super(QuizController, self).__init__(*args, **kwargs)
        self.setWindowTitle("Quiz Controller")

        self.instructions = Pyro4.Proxy("PYRO:quiz.instructions@localhost:1234")
        self.responder = FeedbackReader(Pyro4.Proxy("PYRO:quiz.feedback@localhost:1234"))
        self.responder.message_received.connect(self.handle_response)
        self.responder.start()

        overall_layout = QtGui.QVBoxLayout()
        self.add_teams(overall_layout)

        self.panel_layout = QtGui.QHBoxLayout()
        self.panels = {}
        for position in "left", "right":
            panel = self.panels[position.lower()] = Panel(self, position)
            self.panel_layout.addWidget(panel)
        overall_layout.addLayout(self.panel_layout)
        self.add_controller(overall_layout)
        self.setLayout(overall_layout)

        #
        # The first response is always lost. Not sure why.
        #
        self.send_command("COLOURS?")
        self.send_command("COLOURS?")
        self.send_command("SCORES?")
        self.send_command("TEAMS?")

    def add_teams(self, overall_layout):
        self.teams = []
        for i in range(4):
            team = (
                team_name,
                team_score,
                team_plus,
                team_minus
            ) = (
                QtGui.QLineEdit(),
                QtGui.QLineEdit(""),
                QtGui.QPushButton("+"),
                QtGui.QPushButton("-")
            )
            self.teams.append(team)
            layout = QtGui.QHBoxLayout()
            for widget in team:
                layout.addWidget(widget)
            overall_layout.addLayout(layout)

            def set_team_name(new_name, n_team=i, team_name=team_name, team_score=team_score):
                self.send_command("name", n_team, unicode(team_name.text()))
                if not team_name.styleSheet():
                    self.send_command("COLOURS?")
            def set_team_score(new_score, n_team=i):
                self.send_command("SCORE", str(n_team), str(new_score))
            def set_team_plus(n_team=i, team_score=team_score):
                score = 1 + int(team_score.text() or 0)
                team_score.setText(str(score))
            def set_team_minus(n_team=i, team_score=team_score):
                score = int(team_score.text() or 0) - 1
                team_score.setText(str(score))

            team_name.textEdited.connect(set_team_name)
            team_score.textChanged.connect(set_team_score)
            team_plus.pressed.connect(set_team_plus)
            team_minus.pressed.connect(set_team_minus)

    def add_controller(self, overall_layout):
        command_label = QtGui.QLabel("Command")
        self.command = QtGui.QLineEdit()
        self.send = QtGui.QPushButton("&Send")
        controller_layout = QtGui.QHBoxLayout()
        controller_layout.addWidget(command_label)
        controller_layout.addWidget(self.command)
        controller_layout.addWidget(self.send)
        overall_layout.addLayout(controller_layout)
        self.send.clicked.connect(self.send_command)

        self.responses = QtGui.QLabel()
        responses_layout = QtGui.QHBoxLayout()
        responses_layout.addWidget(self.responses)
        overall_layout.addLayout(responses_layout)

    def send_command(self, message=None, *args):
        if not message:
            commands = unicode(self.command.text()).encode("iso-8859-1").split()
            if not commands:
                core.log.warn("No command")
                return
            else:
                message, args = commands[0], commands[1:]

        args = [(unicode(arg) if isinstance(arg, QtCore.QString) else arg) for arg in args]
        command = "%s %s" % (message, " ".join(str(arg) for arg in args))
        core.log.debug("send_command: %s", command)
        if hasattr(self, "command"):
            self.command.setText(command)
        self.instructions.put(message, *args)

    def position_widget(self, position):
        return self.groups.get(position.lower())

    def handle_default(self, *args, **kwargs):
        core.log.debug("handle_default: %s, %s", str(args), str(kwargs))

    #~ def add_positions(self):
        #~ for position in "left", "right":
            #~ panel = self.panels[position.lower()] = Panel(self, position)
            #~ self.panel_layout.addWidget(panel)

    #~ def handle_position(self, position, screen_name):
        #~ """Handle the POSITION event by selecting the corresponding
        #~ screen from the stacked widget.
        #~ """
        #~ panel = self.panels[position.lower()]
        #~ if panel.selector.currentText() != screen_name:
            #~ panel.selector.setCurrentIndex(panel.selector.findText(screen_name))
            #~ #
            #~ # Changing the selector will cause a STATE? query to fire
            #~ #

    #~ def _handle_position(self, position, cls_name, state):
        #~ core.log.debug("handle_position: %s, %s", position, rest)

        #~ group = self.groups[position]
        #~ group.selector.setCurrentIndex(group.selector.findText(cls_name))
        #~ screen_widget = group.stack.currentWidget()

        #~ styles_combo = screen_widget.styles
        #~ if "styles" in state:
            #~ styles_combo.clear()
            #~ styles_combo.addItems([item.strip() for item in state.pop("styles")])
        #~ if "style" in state:
            #~ screen_widget.styles.setCurrentIndex(screen_widget.styles.findText(state.pop("style")))
        #~ for k, v in state.items():
            #~ subwidget = getattr(screen_widget, k.lower(), None)
            #~ if subwidget:
                #~ subwidget.setText(v)

    #~ def handle_left(self, *args, **kwargs):
        #~ self._handle_position("left", *args, **kwargs)

    #~ def handle_right(self, *args, **kwargs):
        #~ self._handle_position("right", *args, **kwargs)

    def handle_teams(self, teams):
        for n_team, new_name in enumerate(teams):
            name, _, _, _ = self.teams[n_team]
            name.setText(new_name)

    def handle_colours(self, colours):
        for n_team, new_colour in enumerate(colours):
            name, _, _, _ = self.teams[n_team]
            name.setStyleSheet("* { background-color : %s; }" % new_colour)

    def handle_scores(self, scores):
        for n_team, new_score in enumerate(scores):
            _, score, _, _ = self.teams[n_team]
            score.setText(unicode(new_score))

    def handle_quit(self):
        self.close()

    def handle_response(self, message, args):
        core.log.debug("Response received: %s, %s", message, args)
        message = unicode(message)
        response = "%s %s" % (message, " ".join("%r" % arg for arg in args))
        self.responses.setText(response)
        handler = getattr(self, "handle_" + message.lower(), self.handle_default)
        return handler(*args)

def main():
    app = QtGui.QApplication([])
    quiz_controller = QuizController()
    quiz_controller.show()
    return app.exec_()

if __name__ == '__main__':
    try:
        socket.socket().connect(("localhost", 1234))
    except socket.error:
        subprocess.Popen([sys.executable, "quiz.py"])
    sys.exit(main(*sys.argv[1:]))