# A class for communicating in Go Text Protocol
#
# An already-connected socket should be passed in, then this class
# can be used to handle some aspects of GTP simply

# Commands that any user of this class *must* support...
# quit
# boardsize
# clear_board
# komi
# play
# genmove


class GTPSocket:
    # By default the list only has commands we can handle internally in this class.
    # Anything that uses this class should append any commands it can handle to this
    # list. A common pattern would be:
    #
    # dispatcher = {'command1': function_1, 'command_2': function_2, 'command_3': function_3}
    # gtp_socket = GTPSocket(socket)
    # GTPSocket.known_cmds = GTPSocket.known_cmds & set(dispatcher.keys())
    # gtp = gtp_socket.get()
    # if gtp.type == 'command':
    #     dispatcher[gtp.command](gtp)
    known_cmds = set(['protocol_version', 'name', 'version', 'known_command', 'list_commands'])

    def __init__(self, socket):
        self.socket = None
        self.last_id = None

    def get(self):
        msg = None

        while msg is None:
            try:
                select.select([self.socket], [], [])
                msg = self.socket.recv(4096)
                if msg[0] == '?':
                    print "Error: GTP response: " + msg
                    return self._msg_to_response(msg)
                elif msg[0] == '=':
                    return self._msg_to_response(msg)
                elif not _validate_gtp(msg):
                    print 'Error: Incoming data was not a valid GTP message'
                    msg = None
            except:
                pass

        gtp = self._msg_to_gtp(msg)

        # Some gtp commands should be handled internally
        if gtp.command == 'protocol_version':
            self.send_response('2', gtp.id)
            return None

        elif gtp.command == 'name':
            self.send_response('pygo', gtp.id)
            return None

        elif gtp.command == 'version':
            self.send_response('', gtp.id)
            return None

        elif gtp.command == 'known_command':
            if gtp.arguments[0] in GTPSocket.known_cmds:
                resp = 'true'
            else:
                resp = 'false'
            self.send_response(resp, gtp.id)
            return None

        elif gtp.command == 'list_commands':
            self.send_response(''.join(GTPSocket.known_cmds, '\n'), gtp.id)
            return None

        else:
            return gtp



    def send(self, msg):
        try:
            msg = str(self.last_id + 1) + ' ' + msg + "\n"
            if _validate_gtp(msg):
                self.last_id += 1
                self.socket.send(msg)
                return True
            else:
                print 'Error: Outgoing data was not a valid GTP message'
                return False
        except:
            return False


    def send_response(self, msg, resp_id=None, is_error=False):
        try:
            to_send = ''
            if is_error:
                to_send += '?'
            else:
                to_send += '='

            if resp_id is not None:
                to_send += str(resp_id)

            to_send += msg + "\n\n"
            self.socket.send(to_send)
            return True

        except:
            return False
        

    def _msg_to_gtp(self, msg):
        gtp = Object()
        data = msg.split(' ')
        gtp.type = 'message'
        gtp.id = int(data[0])
        gtp.command = data[1]

        if len(data) > 2:
            gtp.arguments = data[2:]

        return gtp


    def _msg_to_response(self, msg):
        resp = Object()
        resp.type = 'response'
        
        

# For now, we *require* our messages to have an id.
# This violates the protocol, but we'll deal with the
# more complicated case later.
def _validate_gtp(msg):
    return re.match(r'\d+?\s+\w+?', msg)