 import Foundation
 import Socket
 
-open class SSDB {
+public class SSDB {
     public typealias Bytes = [UInt8]
+
+    private enum Stage {
+        case Size, Payload
+    }
 
     public enum E: Error {
         case SocketError(String)

    }

    public func get(key: String) throws -> Data? {
        return try self.send(command: .Get(key: key)).details.first
    }

    public func get(key: String, encoding: String.Encoding = .ascii) throws -> String? {

        description: String? = nil,
        isAuth: Bool = false
    ) throws -> Response {
        return try self.queue.sync {
            let description = description ?? String(data: command, encoding: .isoLatin1)!
            let socket = try connection ?? self.getConnection()
            do {
                guard try socket.write(from: command) > 0 else {
                    throw E.SendError("Zero bytes sent")
                }
            } catch let error {
                throw E.SendError("Could not send command \"\(description)\" (error \"\(error)\")")
            }
            do {
                let response = try self.read(from: socket)
                if !isAuth && response.status == "noauth" {
                    throw E.NoAuthError
                }
                return response
            } catch let error {
                throw E.ResponseError("Could not receive result for command \"\(description)\" (error \"\(error)\")")
            }
        }
    }

    @discardableResult public func send(command: Command, to connection: Socket? = nil) throws -> Response {
        return try self.send(
            command: command.getCompiledPacket(),
            to: connection,
            description: command.getDescription(),
            isAuth: command.isAuth()
        )
    }

    private func read(from socket: Socket) throws -> Response {
        self.stage = .Size
        while true {
            if let result = self.parse() {
                return try Response(from: result)
            }
            var data: Data = Data()
            let _ = try socket.read(into: &data)
            self.recvBuffer.append(contentsOf: Bytes(data))
        }
    }

    private func parse() -> [Data]? {
        var left = 0, right = 0
        let bufferSize = self.recvBuffer.count
        loop: while true {
            left = right
            switch self.stage {
            case .Size:
                guard let index = self.recvBuffer[left...].index(of: SSDB.NEW_LINE) else {
                    break loop
                }
                right = index
                right += 1
                let line = self.recvBuffer[left ..< (right - 1)]
                left = right
                if line.count == 0 {
                    self.recvBuffer = Bytes(self.recvBuffer[left...])
                    let result = self.response
                    self.response = []
                    return result
                }
                self.blockSize = Int(String(bytes: line, encoding: .ascii)!.trimmingCharacters(in: .whitespacesAndNewlines))!
                self.stage = .Payload
                fallthrough
            case .Payload:
                right = left + self.blockSize
                if right <= bufferSize, let n = self.recvBuffer[right...].index(of: SSDB.NEW_LINE) {
                    self.response.append(Data(bytes: self.recvBuffer[left ..< right]))
                    right = n + 1
                    self.stage = .Size
                    continue loop
                }
                break loop
            }
        }
        if left > 0 {
            self.recvBuffer = Bytes(self.recvBuffer[left...])
        }
        return nil
    }

    public func set(key: String, value: Data) throws {
