import Foundation

private enum PeerOperationLogIndexNamespace: Int8 {
    case nextTagLocalIndex = 0
    case nextMergedIndex = 1
}

final class PeerOperationLogMetadataTable: Table {
    static func tableSpec(_ id: Int32) -> ValueBoxTable {
        return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
    }
    
    private func keyForNextMergedIndex() -> ValueBoxKey {
        let key = ValueBoxKey(length: 1)
        key.setInt8(0, value: PeerOperationLogIndexNamespace.nextMergedIndex.rawValue)
        return key
    }
    
    private func keyForNextLocalIndex(peerId: PeerId, tag: PeerOperationLogTag) -> ValueBoxKey {
        let key = ValueBoxKey(length: 1 + 8 + 1)
        key.setInt8(0, value: PeerOperationLogIndexNamespace.nextTagLocalIndex.rawValue)
        key.setInt64(1, value: peerId.toInt64())
        key.setUInt8(9, value: tag.rawValue)
        return key
    }
    
    func getNextLocalIndex(peerId: PeerId, tag: PeerOperationLogTag) -> Int32 {
        if let value = self.valueBox.get(self.table, key: self.keyForNextLocalIndex(peerId: peerId, tag: tag)) {
            assert(value.length == 4)
            var index: Int32 = 0
            value.read(&index, offset: 0, length: 4)
            return index
        } else {
            return 0
        }
    }
    
    func setNextLocalIndex(peerId: PeerId, tag: PeerOperationLogTag, index: Int32) {
        var indexValue: Int32 = index
        self.valueBox.set(self.table, key: self.keyForNextLocalIndex(peerId: peerId, tag: tag), value: MemoryBuffer(memory: &indexValue, capacity: 4, length: 4, freeWhenDone: false))
    }
    
    func takeNextLocalIndex(peerId: PeerId, tag: PeerOperationLogTag) -> Int32 {
        let index = self.getNextLocalIndex(peerId: peerId, tag: tag)
        self.setNextLocalIndex(peerId: peerId, tag: tag, index: index + 1)
        return index
    }
    
    func getNextMergedIndex() -> Int32 {
        if let value = self.valueBox.get(self.table, key: self.keyForNextMergedIndex()) {
            assert(value.length == 4)
            var index: Int32 = 0
            value.read(&index, offset: 0, length: 4)
            return index
        } else {
            return 1
        }
    }
    
    private func setNextMergedIndex(_ index: Int32) {
        var indexValue: Int32 = index
        self.valueBox.set(self.table, key: self.keyForNextMergedIndex(), value: MemoryBuffer(memory: &indexValue, capacity: 4, length: 4, freeWhenDone: false))
    }
    
    func takeNextMergedIndex() -> Int32 {
        let index = self.getNextMergedIndex()
        self.setNextMergedIndex(index + 1)
        return index
    }
}