def __init__(self, s):
     self.state = S_OFFLINE
     self.peer = ''
     self.me = ''
     self.out_msg = ''
     self.s = s
     # set my own private key
     self.public_base = 0
     self.public_clock = 0
     self.private_key = random.randint(1, 1000)
     self.public_key = 0
     self.peer_public_key = 0
     self.group_public_key = 0
     self.group_private_key = 0
     self.shared_key = 0
     self.offset = 0
     # store results of dice rolls for me and peer
     self.result = ''
     self.roll_first = ''
     self.peer_result = ''
     # store info for TicTacToe game
     self.go_first = ''
     self.board = TicTacToe.Board()
     self.xo = ''
     self.my_position = ''
     self.peer_position = ''
    def proc(self, my_msg, peer_msg):
        self.out_msg = ''
        #==============================================================================
        # Once logged in, do a few things: get peer listing, connect, search
        # And, of course, if you are so bored, just go
        # This is event handling instate "S_LOGGEDIN"
        #==============================================================================

        if self.state == S_LOGGEDIN:
            # todo: can't deal with multiple lines yet

            if len(my_msg) > 0:

                if my_msg == 'q':
                    self.out_msg += 'See you next time!\n'
                    self.state = S_OFFLINE

                elif my_msg == 'time':
                    mysend(self.s, json.dumps({"action": "time"}))
                    time_in = json.loads(myrecv(self.s))["results"]
                    self.out_msg += "Time is: " + time_in

                elif my_msg == 'who':
                    mysend(self.s, json.dumps({"action": "list"}))
                    logged_in = json.loads(myrecv(self.s))["results"]
                    self.out_msg += 'Here are all the users in the system:\n'
                    self.out_msg += logged_in

                elif my_msg[0] == 'c':
                    peer = my_msg[1:]
                    peer = peer.strip()
                    if self.connect_to(peer) == True:
                        self.out_msg += 'Connect to ' + peer + '. Chat away!\n\n'
                        self.out_msg += '-----------------------------------\n'
                        # when we are connected, we immediately request the base, clock and my group public/private key.
                        mysend(self.s, json.dumps({"action": "exchange_key"}))
                        received = json.loads(myrecv(self.s))["keys"]
                        self.group_public_key = received[0]
                        self.group_private_key = received[1]
                        self.public_base = received[2]
                        self.public_clock = received[3]
                        self.public_key = (self.public_base**self.private_key
                                           ) % self.public_clock
                        self.state = S_CHATTING
                    else:
                        self.out_msg += 'Connection unsuccessful\n'

                elif my_msg[0] == '?':
                    term = my_msg[1:].strip()
                    mysend(self.s,
                           json.dumps({
                               "action": "search",
                               "target": term
                           }))
                    search_rslt = json.loads(myrecv(self.s))["results"].strip()
                    if (len(search_rslt)) > 0:
                        self.out_msg += search_rslt + '\n\n'
                    else:
                        self.out_msg += '\'' + term + '\'' + ' not found\n\n'

                elif my_msg[0] == 'p' and my_msg[1:].isdigit():
                    poem_idx = my_msg[1:].strip()
                    mysend(self.s,
                           json.dumps({
                               "action": "poem",
                               "target": poem_idx
                           }))
                    poem = json.loads(myrecv(self.s))["results"]
                    # print(poem)
                    if (len(poem) > 0):
                        self.out_msg += poem + '\n\n'
                    else:
                        self.out_msg += 'Sonnet ' + poem_idx + ' not found\n\n'

                # I invite peer to a game
                elif my_msg[0] == "g":
                    os.system("clear")
                    peer = my_msg[1:]
                    peer = peer.strip()
                    if self.game_to(peer) == True:
                        self.state = S_GAMING_DICE
                        self.out_msg += 'Connect to ' + peer + '. Game away!\n\n'
                        self.out_msg += '-----------------------------------\n'
                        self.out_msg += '''\nWelcome to Tic-Tac-Toe!\n
                        1 | 2 | 3 
                        ----------
                        4 | 5 | 6 
                        ----------
                        7 | 8 | 9 
                        \nLet's Start!\n\n '''
                        self.out_msg += "Please enter D to roll a dice.\n"
                        self.out_msg += "Whoever gets a larger number goes first!\n"
                    else:
                        self.out_msg += 'Connection unsuccessful\n'

                else:
                    self.out_msg += menu

            if len(peer_msg) > 0:
                peer_msg = json.loads(peer_msg)
                if peer_msg["action"] == "connect":
                    self.peer = peer_msg["from"]
                    self.out_msg += 'Request from ' + self.peer + '\n'
                    self.out_msg += 'You are connected with ' + self.peer
                    self.out_msg += '. Chat away!\n\n'
                    self.out_msg += '------------------------------------\n'
                    # when we are connected, we immediately request the group public key.
                    mysend(self.s, json.dumps({"action": "exchange_key"}))
                    received = json.loads(myrecv(self.s))["keys"]
                    self.group_public_key = received[0]
                    self.group_private_key = received[1]
                    self.public_base = received[2]
                    self.public_clock = received[3]
                    self.public_key = (self.public_base**
                                       self.private_key) % self.public_clock
                    self.state = S_CHATTING

                # peer invite me to a game
                if peer_msg["action"] == "game":
                    os.system("clear")
                    self.peer = peer_msg["from"]
                    self.out_msg += 'Request from ' + self.peer + '\n'
                    self.out_msg += 'You are connected with ' + self.peer
                    self.out_msg += '. Game away!\n\n'
                    self.out_msg += '------------------------------------\n'
                    self.out_msg += '''\nWelcome to Tic-Tac-Toe!\n
                        1 | 2 | 3 
                        ----------
                        4 | 5 | 6 
                        ----------
                        7 | 8 | 9 
                        \nLet's Start!\n\n '''
                    self.state = S_GAMING_DICE
                    self.out_msg += "Please enter D to roll a dice.\n"
                    self.out_msg += "Whoever gets a larger number goes first!\n"

#==============================================================================
# Start chatting, 'bye' for quit
# This is event handling instate "S_CHATTING"
#==============================================================================

        elif self.state == S_CHATTING:

            if len(my_msg) > 0:  # my stuff going out
                # use the Diffie-Hellman key exchange formula
                self.shared_key = (self.group_public_key**
                                   self.private_key) % self.public_clock
                # calculate the offset to encrypt my message
                self.offset = int(self.shared_key) % 26
                my_encrypted_msg = encrypt.generate_encrypted_msg(
                    self.offset, my_msg)
                # when I am sending out my message, I will send out my public key as well, so group members can decrypt my message
                mysend(
                    self.s,
                    json.dumps({
                        "action": "exchange",
                        "from": "[" + self.me + "]",
                        "message": my_encrypted_msg,
                        "from_name_public_key": self.public_key
                    }))
                if my_msg == 'bye':
                    self.disconnect()
                    self.state = S_LOGGEDIN
                    self.peer = ''
            if len(peer_msg) > 0:
                peer_msg = json.loads(peer_msg)

                if peer_msg["action"] == "connect":
                    self.out_msg += "(" + peer_msg["from"] + " joined)\n"
                elif peer_msg["action"] == "disconnect":
                    self.state = S_LOGGEDIN
                else:
                    # first we retrieve the peer's public key
                    self.peer_public_key = peer_msg["from_name_public_key"]
                    #  get the peer's encrypted message based on my group own private key
                    self.shared_key = (
                        self.peer_public_key**
                        self.group_private_key) % self.public_clock
                    # calculate the offset to encrypt my message
                    self.offset = int(self.shared_key) % 26
                    encrypted_msg = peer_msg["message"]
                    # then calculate the offset according to the group public key

                    decrypted_msg = encrypt.decrypt_msg(
                        self.offset, encrypted_msg)
                    self.out_msg += peer_msg["from"] + decrypted_msg

            # Display the menu again
            if self.state == S_LOGGEDIN:
                self.out_msg += menu

#==============================================================================
# Start GAMING, 'q' for quit
# This is event handling instate "S_GAMING_DICE"
# Peer and I will roll a dice. Whoever gets a bigger number starts first.
# The one that starts first is "X". The one that starts later is "O"
#==============================================================================
        elif self.state == S_GAMING_DICE:

            # roll a dice to determine who goes first
            if len(my_msg) > 0:
                if my_msg == "q":
                    self.disconnect()
                    self.state = S_LOGGEDIN
                    self.peer = ''

                # enter "d" or "D" to roll a dice
                elif my_msg == "d" or my_msg == "D":
                    self.result = str(random.randint(1, 6))
                    self.out_msg += "You got " + self.result + ".\n"
                    mysend(
                        self.s,
                        json.dumps({
                            "action": "dice",
                            "from": self.me,
                            "result": self.result
                        }))
                    # when peer hasn't rolled yet
                    if self.peer_result == '':
                        self.roll_first = True
                        self.out_msg += "Waiting for " + self.peer + " to roll...\n"
                    # when peer already rolled
                    else:
                        self.roll_first = False
                        # if peer rolled larger number than me, peer goes first
                        if self.peer_result > self.result:
                            self.out_msg += self.peer + " got " + self.peer_result + ".\n"
                            self.out_msg += self.peer + " goes first!\n"
                            self.state = S_GAMING_WAITING
                            self.go_first = False
                            self.xo = "O"
                            os.system("clear")
                            self.out_msg += "You are O. " + self.peer + " is X.\n"
                            self.out_msg += '''  
                            1 | 2 | 3 
                            ----------
                            4 | 5 | 6 
                            ----------
                            7 | 8 | 9 
                            \n'''
                            self.out_msg += "Waiting for " + self.peer + " to make a move...\n"
                            self.result = ''
                            self.peer_result = ''
                            self.roll_first = ''
                        # if we both roll the same number, roll again
                        elif self.result == self.peer_result:
                            self.out_msg += "opps, same results. Throw again!\n"
                            self.result = ''
                            self.peer_result = ''
                            self.roll_first = ''
                        # if I rolled larger number than peer, I goes first
                        else:
                            self.out_msg += self.peer + " got " + self.peer_result + ".\n"
                            self.out_msg += "You go first!\n"
                            self.state = S_GAMING_MOVING
                            self.go_first = True
                            self.xo = "X"
                            os.system("clear")
                            self.out_msg += "You are X. " + self.peer + " is O.\n"
                            self.out_msg += '''  
                            1 | 2 | 3 
                            ----------
                            4 | 5 | 6 
                            ----------
                            7 | 8 | 9 
                            \n'''
                            self.out_msg += "Please choose a number from 1 - 9. > \n"
                            self.result = ''
                            self.peer_result = ''
                            self.roll_first = ''

            if len(peer_msg) > 0:
                peer_msg = json.loads(peer_msg)

                if peer_msg["action"] == "game":
                    self.out_msg += "(" + peer_msg["from"] + " joined)\n"
                elif peer_msg["action"] == "disconnect":
                    self.state = S_LOGGEDIN
                    self.out_msg += "You are disconnected from " + self.peer + "\n"

                # receive peer's dice result
                elif peer_msg["action"] == "dice":
                    self.peer_result = peer_msg["result"]
                    self.out_msg += peer_msg[
                        "from"] + " got " + self.peer_result + "\n"
                    # if I haven't rolled yet
                    if self.result == "":
                        self.roll_first = False
                        self.out_msg += "Waiting for you to roll...\n"
                    # if I already rolled
                    else:
                        self.roll_first = True

                        # compare my dice result and peer's dice result and determine who goes first
                        if self.result < self.peer_result:
                            self.out_msg += "You got " + self.result + ".\n"
                            self.out_msg += peer_msg["from"] + " goes first!\n"
                            self.state = S_GAMING_WAITING
                            self.go_first = False
                            self.xo = "O"
                            os.system("clear")
                            self.out_msg += "You are O. " + self.peer + " is X.\n"
                            self.out_msg += '''  
                            1 | 2 | 3 
                            ----------
                            4 | 5 | 6 
                            ----------
                            7 | 8 | 9 
                            \n'''
                            self.out_msg += "Waiting for " + self.peer + " to make a move...\n"
                            self.result = ''
                            self.peer_result = ''
                            self.roll_first = ''
                        elif self.result == self.peer_result:
                            self.out_msg += "opps, same results. Throw again!\n"
                            self.result = ''
                            self.peer_result = ''
                            self.roll_first = ''
                        else:
                            self.out_msg += "You got " + self.result + ".\n"
                            self.out_msg += "You go first!\n"
                            self.state = S_GAMING_MOVING
                            self.go_first = True
                            self.xo = "X"
                            os.system("clear")
                            self.out_msg += "You are X. " + self.peer + " is O.\n"
                            self.out_msg += '''  
                            1 | 2 | 3 
                            ----------
                            4 | 5 | 6 
                            ----------
                            7 | 8 | 9 
                            \n'''
                            self.out_msg += "Please choose a number from 1 - 9. > \n"
                            self.result = ''
                            self.peer_result = ''
                            self.roll_first = ''

            if self.state == S_LOGGEDIN:
                self.out_msg += menu

#==============================================================================
# Start GAMING, 'q' for quit
# This is event handling instate "S_GAMING_MOVING" and "S_GAMING_WAITING"
# When playing TicTacToe, there are two states:
# whether you are making a move, or you are waiting for your peer to make a move
#==============================================================================
# If it's my turn to make a move
        elif self.state == S_GAMING_MOVING:

            if len(my_msg) > 0:
                # quit the game
                if my_msg == "q":
                    self.disconnect()
                    self.state = S_LOGGEDIN
                    self.peer = ""
                else:
                    # if enter alphabet
                    if my_msg.isalpha() == True:
                        self.out_msg += "Invalid input! Please enter again!\n"
                    # if I enter a valid move number, update my move on my board
                    elif self.board.update_board(int(my_msg), self.xo) == True:
                        os.system("clear")
                        self.out_msg += "You are " + self.xo + ".\n"
                        print(self.board.display())
                        # check whether I win
                        if self.board.is_winner(self.xo) == True:
                            self.out_msg += "You win!\n"
                            mysend(
                                self.s,
                                json.dumps({
                                    "action": "move",
                                    "from": self.xo,
                                    "position": my_msg,
                                    "status": "win"
                                }))
                            self.state = S_GAMING_AGAIN
                            self.out_msg += "Play again?(Y/N)\n"
                            self.board = TicTacToe.Board()
                        # check whether it is a tie
                        elif self.board.is_tie() == True:
                            self.out_msg += "It's a tie!\n"
                            mysend(
                                self.s,
                                json.dumps({
                                    "action": "move",
                                    "from": self.xo,
                                    "position": my_msg,
                                    "status": "tie"
                                }))
                            self.state = S_GAMING_AGAIN
                            self.out_msg += "Play again?(Y/N)\n"
                            self.board = TicTacToe.Board()
                        # send me move to peer, continue playing
                        else:
                            mysend(
                                self.s,
                                json.dumps({
                                    "action": "move",
                                    "from": self.xo,
                                    "position": my_msg,
                                    "status": ""
                                }))
                            self.state = S_GAMING_WAITING
                            self.out_msg += "Waiting for " + self.peer + " to move..."
                    # if I enter an invalid move number(either the cell has already been taken or the number is not within 1-9)
                    else:
                        self.out_msg += "Invalid input! Please enter again!\n"

            if len(peer_msg) > 0:
                peer_msg = json.loads(peer_msg)
                #print(peer_msg)
                if peer_msg["action"] == "disconnect":
                    self.state = S_LOGGEDIN
                    self.out_msg += "You are disconnected from " + self.peer + "\n"

            if self.state == S_LOGGEDIN:
                self.out_msg += menu

        # If it's my peer's turn to make a move, I will be waiting
        elif self.state == S_GAMING_WAITING:
            if len(my_msg) > 0:
                # quit the game
                if my_msg == "q":
                    self.disconnect()
                    self.state = S_LOGGEDIN
                    self.peer = ""

            if len(peer_msg) > 0:
                peer_msg = json.loads(peer_msg)
                if peer_msg["action"] == "disconnect":
                    self.state = S_LOGGEDIN
                    self.out_msg += "You are disconnected from " + self.peer + '\n'
                # receiving peer's move and update peer's move on my board
                elif peer_msg["action"] == "move":
                    position = peer_msg["position"]
                    xo = peer_msg["from"]
                    if self.board.update_board(int(position), xo) == True:
                        os.system("clear")
                        self.out_msg += "You are " + self.xo + ".\n"
                        print(self.board.display())
                        # check whether peer wins
                        if peer_msg["status"] == "win":
                            self.out_msg += "You lose!\n"
                            self.out_msg += "Play again?(Y/N)\n"
                            self.state = S_GAMING_AGAIN
                            self.board = TicTacToe.Board()
                        # check whether it is a tie
                        elif peer_msg["status"] == "tie":
                            self.out_msg += "It's a tie!\n"
                            self.out_msg += "Play again?(Y/N)\n"
                            self.state = S_GAMING_AGAIN
                            self.board = TicTacToe.Board()
                        # continue the game
                        else:
                            self.state = S_GAMING_MOVING
                            self.out_msg += "Please choose a number from 1 - 9. > \n"
                    else:
                        self.out_msg += "Waiting for " + self.peer + " to move..."

            if self.state == S_LOGGEDIN:
                self.out_msg += menu

#==============================================================================
# This is event handling instate "S_GAMING_AGAIN"
# Ask users whether want to play again
#==============================================================================
        elif self.state == S_GAMING_AGAIN:

            if len(my_msg) > 0:
                # if don't want to play again
                if my_msg == "n" or my_msg == "N":
                    mysend(
                        self.s,
                        json.dumps({
                            "action": "game_again",
                            "from": self.me,
                            "status": "no"
                        }))
                    self.out_msg += "You are disconnected from " + self.peer + '\n'
                    self.state = S_LOGGEDIN
                    self.peer = ''
                # if want to play again
                elif my_msg == "y" or my_msg == "Y":
                    mysend(
                        self.s,
                        json.dumps({
                            "action": "game_again",
                            "from": self.me,
                            "status": "yes"
                        }))
                    os.system("clear")
                    self.out_msg += '''\nWelcome to Tic-Tac-Toe!\n
                        1 | 2 | 3 
                        ----------
                        4 | 5 | 6 
                        ----------
                        7 | 8 | 9 
                        \nLet's Start!\n\n'''
                    self.state = S_GAMING_DICE
                    self.out_msg += "Please enter D to roll a dice.\n"
                    self.out_msg += "Whoever gets a larger number goes first!\n"

            if len(peer_msg) > 0:
                peer_msg = json.loads(peer_msg)
                if peer_msg["action"] == "game_again":
                    # if peer doesn't want to play again
                    if peer_msg["status"] == "no":
                        self.state = S_LOGGEDIN
                        self.out_msg += "You are disconnected from " + self.peer
                    # if peer wants to play again
                    elif peer_msg["status"] == "yes":
                        os.system("clear")
                        self.out_msg += '''\nWelcome to Tic-Tac-Toe!\n
                            1 | 2 | 3 
                            ----------
                            4 | 5 | 6 
                            ----------
                            7 | 8 | 9 
                            \nLet's Start!\n\n '''
                        self.state = S_GAMING_DICE
                        self.out_msg += "Please enter D to roll a dice.\n"
                        self.out_msg += "Whoever gets a larger number goes first!\n"

            if self.state == S_LOGGEDIN:
                self.out_msg += menu

#==============================================================================
# invalid state
#==============================================================================
        else:
            self.out_msg += 'How did you wind up here??\n'
            print_state(self.state)

        return self.out_msg