from memory import Memory

m = Memory()

r = m.alloc("xyz", 14)
print r
print m

r = m.alloc("abc", 12)
print r
print m




r = m.read("xyz", 4, 5)
print r
print m

r = m.deleteFile("xyz")
print r
print m

r = m.deleteFile("abc")
print r
print m
class Server:
    backlog = 5
    # this set will serve as a filesystem index
    files = set()

    def __init__(self, port):
        # creates server object
        global blocksize, n_blocks
        path = ".storage"
        try:
            os.makedirs(path)
        except OSError as exception:
            shutil.rmtree(path)
            os.makedirs(path)
        host = ""
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.s.bind((host, port))
        self.port = port
        self.memory = Memory(n_blocks, blocksize)
        print "Block size is", blocksize
        print "Number of blocks is", n_blocks

    def process_line(self, client):
        # recv's until \n
        recieved = ""
        while True:
            cur = client.recv(1)
            if cur == "\n":
                break
            elif not cur:
                return cur
            else:
                recieved += cur
        return recieved

    def listen(self):
        # Handles initial client connections and thread creation
        self.s.listen(self.backlog)
        print "Listening on port", self.port
        while True:
            client, address = self.s.accept()
            print "Received incoming connection from", address[0]
            cmd = self.process_line(client)
            thread = threading.Thread(target=self.handler, args=(cmd, client))
            thread.start()

    # the following functions implement the server functionality requested
    def store(self, client, args):
        thread = str(threading.current_thread().ident)
        if len(args) != 3:
            client.send("ERROR: INVALID COMMAND.\n")
            print "[thread", thread + "] Sent: ERROR: INVALID COMMAND."
            return
        args[2] = int(args[2])
        if args[1] in self.files:
            client.send("ERROR: FILE EXISTS.\n")
            print "[thread", thread + "] Sent: ERROR: FILE EXISTS."
            data = client.recv(args[2])  # can we assume data will be sent regardless of error?
            return
        response = self.memory.alloc(args[1], args[2])
        if not response[0]:
            client.send("ERROR: INSUFFICIENT DISK SPACE.\n")
            print "[thread", thread + "] Sent: ERROR: INSUFFICIENT DISK SPACE."
            data = client.recv(args[2])  # can we assume data will be sent regardless of error?
            return
        # actually do the storing stuff
        data = client.recv(args[2])
        f = open(".storage/" + args[1], "w")
        f.write(data)
        self.files.add(args[1])
        print "[thread", thread + "] Stored file '%s' (%s bytes; %s blocks; %s" % response, (
            "cluster" if response[3] == 1 else "cluster"
        ) + ")"
        print "[thread", thread + "] Simulated Clustered Disk Space Allocation:"
        print self.memory
        client.send("ACK\n")
        print "[thread", thread + "] Sent: ACK"

    def read(self, client, args):
        thread = str(threading.current_thread().ident)
        if len(args) != 4:
            client.send("ERROR: INVALID COMMAND.\n")
            print "[thread", thread + "] Sent: ERROR: INVALID COMMAND."
            return
        if args[1] not in self.files:
            client.send("ERROR: NO SUCH FILE.\n")
            print "[thread", thread + "] Sent: ERROR: NO SUCH FILE."
            return
        args[2] = int(args[2])
        args[3] = int(args[3])
        data = open(".storage/" + args[1], "r").read()
        if args[2] + args[3] > len(data) or args[2] < 0 or args[3] < 0:
            client.send("ERROR: INVALID BYTE RANGE.\n")
            print "[thread", thread + "] Sent: ERROR: INVALID BYTE RANGE."
            return
        # TODO: (1) Memory dump / print output
        result = "ACK " + str(args[3]) + "\n" + data[args[2] : args[2] + args[3]]
        print "[thread", thread + "] Sent: ACK", args[3]
        client.send(result)
        response = self.memory.read(args[1], args[2], args[3])
        print "[thread", thread + "] Sent %s bytes (from %s '%s' blocks) from offset %s." % response

    def delete(self, client, args):
        thread = str(threading.current_thread().ident)
        if len(args) != 2:
            client.send("ERROR: INVALID COMMAND.\n")
            print "[thread", thread + "] Sent: INVALID COMMAND."
            return
        if args[1] not in self.files:
            client.send("ERROR: NO SUCH FILE.\n")
            print "[thread", thread + "] Sent: ERROR: NO SUCH FILE."
            return
        os.remove(".storage/" + args[1])
        self.files.remove(args[1])
        response = self.memory.deleteFile(args[1])
        client.send("ACK\n")
        print "[thread", thread + "] Deleted", args[1], "file '%s' (deallocated %s blocks)" % response
        print "[thread", thread + "] Simulated Clustered Disk Space Allocation:"
        print self.memory
        print "[thread", thread + "] Sent: ACK"

    def dir(self, client):
        thread = str(threading.current_thread().ident)
        files_sorted = sorted(self.files, key=str.lower)
        result = str(len(self.files)) + "\n"
        for f in files_sorted:
            result += f + "\n"
        print "[thread", thread + "] Sent list of %d" % len(self.files), (
            "file" if len(self.files) == 1 else "files"
        ) + "."
        client.send(result)

    def handler(self, cmd, client):
        # Handles specific client connections until they close
        thread = str(threading.current_thread().ident)
        while cmd:
            print "[thread", thread + "] Rcvd:", cmd
            args = cmd.split(" ")
            args[0] = args[0].strip()
            if args[0] == "STORE":
                self.store(client, args)
            elif args[0] == "READ":
                self.read(client, args)
            elif args[0] == "DELETE":
                self.delete(client, args)
            elif args[0] == "DIR":
                self.dir(client)
            else:
                client.send("ERROR: INVALID COMMAND.\n")
                print "[thread", thread + "] Sent: ERROR: INVALID COMMAND."
            cmd = self.process_line(client).strip()
        print "[thread", thread + "] Client closed its socket....terminating."
        client.close()