from socket import *
import select,time
theTotalMemory=0
class ConnectedClient:
    def __init__(self, client, listOfClients):
        self.client = client
        self.name = "NO_NAME"
        self.organisms=[]
        self.listOfClients =listOfClients
        self.listOfClientsCounter = 0
        self.receivedFromCounter=0
        self.sendToCounter=0
        self.status = "B"
        self.fase = 0
        self.lengthNextBot=0
        self.sendCounter=0
        self.sendData=""
        self.nextRecvFile = True
        self.fileInMemory=""
        self.address = ""
        self.header_size = 0
        self.sendFileHeader = 0
    def _readHeader(self):
        # 5 reserved for filesize
        # 32 reserved for name
        # print self.fase
        headerInMemory=""        
        if self.header_size ==0:
            self.header_size = 38
            headerInMemory=""

        # HEADER_SIZE = 38
        # headerInMemory=""
        if (self.header_size) > 0:
            rec = self.client.recv(self.header_size)
            headerInMemory+=rec
            self.header_size -= len(rec)
        # print headerInMemory
        if self.header_size > 0:
            raise Exception("Corrupt header")
            return
        length = int(headerInMemory[:5])
        self.name = headerInMemory[5:36]
        self.status = headerInMemory[37]
        self.fileInMemory=""
        self.lengthNextBot = length
        global theTotalMemory
        theTotalMemory += length
        if self.status in ['B','S']:
            self.fase =1
        elif self.status == 'R':
            self.fase = 2
            self.nextRecvFile= False
        
    def _recvFile(self):
        if (self.lengthNextBot) > 0:
            rec = self.client.recv(min(1024, self.lengthNextBot))
            self.fileInMemory+=rec
            self.lengthNextBot -= len(rec)
        if (self.lengthNextBot) > 0:
            self.nextRecvFile = False
            return            
        self.organisms.append(self.fileInMemory)
        self.receivedFromCounter+=1
        if self.status == 'B':
            self.fase = 2
            self.nextRecvFile = False
        elif self.status == 'S':
            self.fase = 0
    
    def encode_length(self, l):
        return str(l).zfill(5)

    def handleClient(self):
        self.nextRecvFile = True
        # print 'fase', self.fase
        if self.fase ==0:
            # print '_readHeader'                        
            self._readHeader()
        elif self.fase ==1:
            # print 'self._recvFile()'            
            self._recvFile()
        elif self.fase ==2:
            # print 'sendFileHeader'
            self.sendFileHeader_init()
        elif self.fase ==3:
            self._sendFile()
        return self.nextRecvFile

    def incClientCounter(self):
        self.listOfClientsCounter+=1
        if self.listOfClientsCounter >= len(self.listOfClients):
            self.listOfClientsCounter=0

    def sendFileHeader_init(self):
        startCounter=self.listOfClientsCounter
        self.incClientCounter()
        while len(self.listOfClients[self.listOfClientsCounter].organisms)==0 \
        or self.listOfClients[self.listOfClientsCounter].name ==self.name :        
            # no bots availeble, reset to fase 0
            if startCounter == self.listOfClientsCounter:
                self.client.sendall(self.encode_length(0))
                self.fase =0
                return
            self.incClientCounter()
        data = self.listOfClients[self.listOfClientsCounter].organisms.pop()
        self.sendFileHeader = self.encode_length(len(data))
        lengthSend = self.client.send(self.sendFileHeader)
        #print 'lengthSend , self.sendFileHeader ',lengthSend , self.sendFileHeader 
        if lengthSend !=5:
            print "oops, guess client will hang"
        self.sendCounter=0
        self.sendData=data
        self.fase =3
        self.nextRecvFile= False

    def _sendFile(self):
        # print self.sendCounter,len(self.sendData)        
        self.sendCounter += self.client.send(self.sendData[self.sendCounter:])
        if self.sendCounter != len(self.sendData):
            self.nextRecvFile= False            
            return
        self.sendToCounter+=1
        self.fase = 0

i=0

class ServerControl:
    def __init__(self, *args, **kwargs):
        server = socket(AF_INET, SOCK_STREAM)
        #server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)         
        server.setblocking(0)
        server.settimeout(300)
        server.bind(("", 1001))
        server.listen(100) # max 100 clients waiting
        self.clients = []
        readList =[server]
        writeList =[]
        self.clientSocket={}
        socketsInLine=0
        #bannedIPs=[]
        while True:
                print "shameless whatsup"
                #print 'readList: %s writeList: %s' %(len(readList), len(writeList))
                inputready,outputready,exceptready = select.select(readList,writeList,[])
                # if server in inputready:
                #     socketsInLine+=1
                # else:
                #     socketsInLine=0                    
                # print inputready, outputready
                for sock in inputready:
                    # new connection
                    if  sock == server:
                        #print 'new connection'
                        connClient, address = server.accept()
                        #if address[0] not in bannedIPs:
                        connClient.setblocking(0)
                        readList.append(connClient)
                        client = ConnectedClient(connClient,self.clients)
                        client.address = address[0]
                        self.clients.append(client)
                        self.clientSocket[connClient] = client
                #        print "adding socket from %s|%s" % (client.name,client.address)
                        if (len(readList)+ len(writeList))> 500:
                            # avoid overflow, remove oldest inactive socket                                                                                  
                            killSock = readList.pop(1)
                            killSock.close()
                            self.clients.remove(client)
                #            print 'avoid overflow: removing socket from %s|%s' % (self.clientSocket[killSock].name  , self.clientSocket[killSock].address)

                    else:                         
                        try:
                            if not self.clientSocket[sock].handleClient():
                                readList.remove(sock)
                                writeList.append(sock)
                        except Exception,e:
                            print str(e)
                            sock.close()
                            readList.remove(sock)
                for sock in outputready:
                    try:
                        if self.clientSocket[sock].handleClient():
                            writeList.remove(sock)
                            readList.append(sock)
                    except Exception,e:
                        print str(e)
                        sock.close()
                        writeList.remove(sock)

                # cleanup
                for i in reversed(range(len(self.clients))):
                    if self.clients[i] not in self.clientSocket.values():
                        for client2 in self.clients:
                            if self.clients[i].name  == client2.name and self.clients[i] != client2:
                                client2.listOfClientsCounter += self.clients[i].listOfClientsCounter
                                client2.receivedFromCounter += self.clients[i].receivedFromCounter
                                client2.organisms.extend(self.clients[i].organisms)
                                self.clients.remove(self.clients[i])
                # if socketsInLine >90:
                #     # defaultdict ought to be better, but I want to avoid extra library installs
                #     d = dict()
                #     for oneClient in self.clients:
                #         if oneClient.address not in d:
                #             d[oneClient.address]=1
                #         else:
                #             d[oneClient.address] = d[oneClient.address]+1
                #     bannedIP = max(d.iterkeys(), key=(lambda key: d[key]))
                #     print "%s has been banned, too many connections" % bannedIP
                #     bannedIPs.append(bannedIP)
                #print 'totalMemory',theTotalMemory   
                global theTotalMemory

                if theTotalMemory >20000000 :
                    totalMemory=0
                    totalMemory2=0                
                    for client in self.clients:
                        for organism in client.organisms:                              
                            totalMemory+= len(organism)
                #    print 'totalMemory',totalMemory
                    # cap max bots
                    cap = 4096
                    if totalMemory > 20000000:
                        while totalMemory > 10000000:
                            cap /=2
                            #totalCache=0
                            for i in range(len(self.clients)):
                                if len(self.clients[i].organisms)>cap*2:
                                    self.clients[i].organisms = self.clients[i].organisms[-cap:]
                                    #totalCache += len(self.clients[i].organisms)
                            totalMemory=0
                            for client in self.clients:
                                for organism in client.organisms:                              
                                    totalMemory+= len(organism)
                    theTotalMemory = totalMemory
                    
                    
while(True):
    try:
        ServerControl()
    except Exception,e:
        print str(e)
        print "Server crashed, restarting in 100 seconds"
        time.sleep(100)