#a program developed by Christopher David King, free for others to use
import socket, cPickle

class NotConnectedError(Exception):
    '''Raised when sock isn't connected.'''
    pass #A ClosedError

class Client(object): #client object
    '''A client.'''
    def __init__(self, host = 'localhost', port = 1024, timeout = None):
        '''Create a sock and connect it.'''
        self.host = host #set attributes
        self.port = port
        self.__sock =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__sock.connect((self.host, self.port))
        self.timeout = timeout
        self.closed = False
        self.new()

    def new(self): #make new  sock
        '''Create a new sock and send.'''
        try: self.__sock.close() #close old socket
        except AttributeError: pass #If there isn't a socket, don't worry about it
        self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #make a new one
        self.__sock.connect((self.host, self.port))
        self.closed = False
        self.__sock.settimeout(self.timeout)
        
    def send(self, data): #send
        '''Send data.'''
        if self.closed: raise NotConnectedError, 'The socket isn\'t connected.'#if closed, raise error
        self.new()
        self.__sock.send(data) #now send it back
        
    def recv(self): #receive data
        '''Recieve data.'''
        if self.closed: raise NotConnectedError, 'The socket isn\'t connected.' #if closed, raise error
        return self.__sock.recv(1024) #recieve how much they send (+5 for good luck)
    
    def close(self): #close sock
        '''Close the sock.'''
        self.__sock.close()
        self.closed = True
        
    def connect(self, host=None, port=None): #connect or reconnect
        '''Connect or reconnect the sock.'''
        if host: self.host = host #change host
        if port: self.port = port #change port
        self.__sock.connect((self.host, self.port)) #connect it
        self.closed = False #set closed to false
        
    def __get_timeout(self):
        try: return self.__sock.gettimeout()
        except AttributeError: return self.__timeout #sock may not exist, so do predefined

    def __set_timeout(self, timeout):
        self.__timeout = timeout
        try: self.__sock.settimeout(self.__timeout)
        except AttributeError: pass #sock may not exist
        
    timeout = property(__get_timeout, __set_timeout, doc = 'A property for the timeout.')

class Big_Client(Client):
    def __init__(self, host = 'localhost', port = 1024, big_port = 1024): pass
    def send(self, data):
        give_len = Client(self.host, self.port, self.timeout) #a client to get the lenght
        give_len.send(str(len(data)))
        give_len.recv()
        Client.send(self, data)
    def recv(self):
        get_len = Client(self.host, self.big_port, self.timeout)
        get_len.send('length please') #ask them for it
        Client.recv(self)
        
class Pickle_Client(Big_Client): #a client to send objects, not just strings
    '''A client that can send a recieve objects beside strings.'''
    def send(self, data):
        '''Send data.'''
        Client.send(self, cPickle.dumps(data)) #pickle the object first
    def recv(self):
        '''Recieve data.'''
        return cPickle.loads(Client.recv(self)) #unpickles object first
    
##if __name__ == '__main__':
##    client = Client(raw_input('What host? '), int(raw_input('What port? ')))
##    while True:
##        client.send(raw_input('What do you want to send? '))
##        print client.recv()
##        client.new()
