import ps10; reload(ps10)
import wx, Queue
from wx.lib.newevent import NewEvent
from ps10 import *
from threading import Thread

WordSubmission, EVT_WORD_SUBMISSION = NewEvent()

class ModeFrame(wx.Frame):
    """
    This class is the main game mode selection menu.  It is the first window
    displayed, and is also re-displayed after each game.
    """
    def __init__(self):
        """
        Build up the GUI.
        """
        wx.Frame.__init__(self, parent = None, title = '6.00 Word Game',
                          size = (400, 80),
                          style = wx.DEFAULT_FRAME_STYLE &
                                  ~(wx.RESIZE_BORDER |
                                    wx.MAXIMIZE_BOX |
                                    wx.MINIMIZE_BOX))
        # Create the three buttons side by side; each one starts a different
        # game mode.
        mainPanel = wx.Panel(self, style = wx.TAB_TRAVERSAL)
        options = [('&Solo Game',    self.OnSolo),
                   ('Vs. &Computer', self.OnVsComp),
                   ('Vs. &Human',    self.OnVsHuman)]
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        for label, handler in options:
            button = wx.Button(mainPanel, label = label)
            button.Bind(wx.EVT_BUTTON, handler)
            hbox.Add(button, 1, wx.EXPAND | wx.ALL, border = 10)
        mainPanel.SetSizer(hbox)
        self.Center()
    def OnSolo(self, event):
        """
        Start a solo game.
        """
        self.Close()
        PlayFrame(HUMAN_SOLO).Show()
    def OnVsComp(self, event):
        """
        Start a vs. computer game.
        """
        self.Close()
        PlayFrame(HUMAN_VS_COMP).Show()
    def OnVsHuman(self, event):
        """
        Start a vs. human game.
        """
        self.Close()
        PlayFrame(HUMAN_VS_HUMAN).Show()

class PlayFrame(wx.Frame):
    """
    The main game-playing window.
    """
    def __init__(self, mode):
        """
        Build up the GUI.
        """
        wx.Frame.__init__(self, parent = None, title = '6.00 Word Game')
        self.mode = mode
        # There are four nested levels of panels.  The outerPanel contains the
        # status bar on the bottom and everything else on top of it (in
        # statsPanel).  statsPanel contains the stats display for player 1 on
        # the left, the mainPanel in the middle, and the stats display for
        # player 2 on the right.  mainPanel contains the history list box and
        # the entryPanel below it.  The entryPanel contains the inputBox and
        # the submitButton side by side.
        outerPanel = wx.Panel(self)
        statsPanel = wx.Panel(outerPanel)
        mainPanel = wx.Panel(statsPanel)
        entryPanel = wx.Panel(mainPanel)

        # entryPanel
        self.inputBox = wx.TextCtrl(entryPanel)
        self.submitButton = wx.Button(entryPanel, label = 'Enter!')
        self.submitButton.SetDefault()
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(self.inputBox, 1, wx.EXPAND)
        hbox.Add(self.submitButton, 0, wx.EXPAND)
        self.submitButton.Bind(wx.EVT_BUTTON, self.OnEnter)
        entryPanel.SetSizer(hbox)

        # mainPanel
        historyLabel = wx.StaticText(mainPanel,
                                     label = 'Previously entered words:')
        self.history = wx.ListBox(mainPanel)
        self.handLabel = wx.StaticText(mainPanel, label = 'Hand: ')
        inputBoxLabel = wx.StaticText(mainPanel, label =
                '&Enter word, or . to end round:')
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(historyLabel, 0, wx.EXPAND)
        vbox.Add(self.history, 1, wx.EXPAND)
        vbox.Add(self.handLabel, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 10)
        vbox.Add(inputBoxLabel, 0, wx.ALIGN_CENTER_VERTICAL)
        vbox.Add(entryPanel, 0, wx.EXPAND)
        mainPanel.SetSizer(vbox)

        # statsPanel
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        empty = lambda: wx.StaticText(statsPanel, label = '')
        self.stats = [
                wx.StaticText(statsPanel,
                              label = '',
                              style = wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE,
                              size = (150, -1))
                for player in [1,2] ]
        hbox.Add( self.stats[0], 0, wx.EXPAND )
        hbox.Add( mainPanel, 1, wx.EXPAND )
        hbox.Add( self.stats[1], 0, wx.EXPAND )
        statsPanel.SetSizer(hbox)

        # outerPanel
        self.statusBar = wx.StatusBar(outerPanel)
        self.statusBar.SetStatusText('This is the status bar.')
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add( statsPanel, 1, wx.EXPAND )
        vbox.Add( self.statusBar, 0, wx.EXPAND )
        outerPanel.SetSizer(vbox)

        # Set some miscellaneous options on the window.
        self.SetMinSize((700, 300))
        self.Center()

        # Bind some event handlers.
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(EVT_WORD_SUBMISSION, self.OnWordSubmission)

        # Start things up.
        self.queue = Queue.Queue()
        self.inputBox.SetFocus()
        self.StartGame()

    def OnClose(self, event):
        """
        Handle the event where the window is closed.
        """
        self.Destroy()
        frame = ModeFrame()
        frame.Show()
        frame.Raise()

    def OnEnter(self, event):
        """
        Handle the event where the submit button was pressed.
        """
        if self.submitButton.IsEnabled():
            self.TryWord(self.inputBox.GetValue())

    def OnWordSubmission(self, event):
        """
        Handle the event where the computer player has finished coming up with
        a word and tries to submit it.
        """
        self.queue.put(self.TryWord(event.word))

    def TryWord(self, word):
        """
        Common method called by OnEnter and OnWordSubmission.  This calls into
        the back-end to try this word, then the window displays are updated
        according to whether things succeeded or failed.

        returns: True if the game is still going, False if the game has ended.
        """
        try:
            try:
                # Call into the backend.
                points = self.game.tryWord(word)
                if points is not None:
                    # Succeeded, so add the word.
                    self.history.Insert(word, 0)
                    self.statusBar.SetStatusText('Got %d points.' % points)
                else:
                    # Failed, so just update the status bar.
                    self.statusBar.SetStatusText('Not a valid word.')
            finally:
                # In any case, clear the inputBox and refresh the stats
                # displays.
                self.inputBox.Clear()
                self.RefreshLabels()
            return True
        except EndHand:
            # We've reached the end of the hand, either voluntarily ('.') or by
            # using up all letters.

            # Summarize the hand.
            if word != '.' and word != '': self.history.Insert(word, 0)
            wx.MessageDialog(self,
                    message = "Player %d's hand ends." %
                        self.game.getCurrentPlayer().getIdNum(),
                    caption = 'End of hand',
                    style = wx.OK).ShowModal()
            if self.game.nextPlayer():
                # There is another player to switch to.
                self.StartHand()
            else:
                # Game has completely ended.
                if self.game.getNumPlayers() > 1:
                    if self.game.isTie():
                        wx.MessageDialog(self,
                                message = "Game ends in a tie.",
                                caption = "Game over",
                                style = wx.OK).ShowModal()
                    else:
                        wx.MessageDialog(self,
                                message = "Player %d wins!" % \
                                    self.game.getWinner().getIdNum(),
                                caption = "Game over",
                                style = wx.OK).ShowModal()
                self.Close()
            return False

    def RefreshLabels(self):
        """
        Refresh the displays in the stats labels and the hand label.
        """
        self.handLabel.SetLabel('Hand: %s' %
                self.game.getCurrentPlayer().getHand())
        for s, player in zip(self.stats, self.game.players):
            s.SetLabel(str(player))

    def StartGame(self):
        """
        Start a game by creating a wordlist and a game backend, then starting a
        hand.
        """
        wordlist = Wordlist()
        self.game = Game(self.mode, wordlist)
        self.StartHand()

    def StartHand(self):
        """
        Start a hand.
        """
        player = self.game.getCurrentPlayer()
        # Clear the history list box and refresh labels.
        self.history.Clear()
        self.RefreshLabels()
        # Prompt the user so that they can get ready.
        wx.MessageDialog(self,
                message = 'Press OK to begin!',
                caption = 'Starting new game',
                style = wx.OK).ShowModal()
        if type(player) == ComputerPlayer:
            # Disable controls.
            self.inputBox.Disable()
            self.submitButton.Disable()
            # The way for the worker thread to communicate to the reactor
            # thread is by posting events for the reactor to consume.  The
            # opposite direction is achieved using a synchronized queue.
            def submitWord(word):
                try:
                    wx.PostEvent(self, WordSubmission(word = word))
                    return self.queue.get()
                except:
                    return False
            # Start the computer player in a background thread.
            Thread(target = player.playHand,
                   args = (submitWord, self.game.wordlist)).start()
        else:
            # Enable controls.
            self.inputBox.Enable()
            self.submitButton.Enable()

if __name__ == '__main__':
    app = wx.App()
    frame = ModeFrame()
    frame.Show()
    app.MainLoop()

# vim:et:sw=4:ts=4
