#import jetbrains.exodus.database.ByteIterable;
#import jetbrains.exodus.log.RandomAccessByteIterable;
#import jetbrains.exodus.log.RandomAccessLoggable;
#import jetbrains.exodus.log.iterate.CompressedUnsignedLongByteIterable;
#import jetbrains.exodus.log.iterate.FixedLengthByteIterable;
#import org.jetbrains.annotations.NotNull;

#/**
#* Stateless leaf node for immutable btree
#*/
#class LeafNode extends BaseLeafNode {
class LeafNode
#@NotNull
#private final RandomAccessLoggable loggable;
#private final int keyLength;
#private final int valueLength;
#
#LeafNode(@NotNull final RandomAccessLoggable loggable) {
#this.loggable = loggable;
#final RandomAccessByteIterable key = loggable.getData();
#keyLength = CompressedUnsignedLongByteIterable.getInt(key.iteratorAutoAdvance());
#valueLength = loggable.getDataLength() - CompressedUnsignedLongByteIterable.getCompressedSize(keyLength) - keyLength;
#}
  constructor: (loggable) ->
    @loggable = loggable
    # TODO

#@Override
#public long getAddress() {
#return loggable.getAddress();
#}

#public int getType() {
#return loggable.getType();
#}

#@Override
#public int compareKeyTo(@NotNull final ByteIterable iterable) {
#return loggable.getData().compare(0, keyLength, iterable);
#}

#@Override
#public int compareValueTo(@NotNull final ByteIterable iterable) {
#return loggable.getData().compare(keyLength, valueLength, iterable);
#}

#@Override
#@NotNull
#public ByteIterable getKey() {
#return new FixedLengthByteIterable(loggable.getData(), keyLength);
#}

#@Override
#@NotNull
#public ByteIterable getValue() {
#return new FixedLengthByteIterable(getRawValue(), valueLength);
#}

#@Override
#public boolean isMutable() {
#return false;
#}

#@NotNull
#RandomAccessByteIterable getRawValue() {
#return loggable.getData().clone(keyLength);
#}

#protected void doReclaim(@NotNull BTreeReclaimTraverser context, final int leafIndex) {
#final long keyAddress = context.currentNode.getKeyAddress(leafIndex);
#if (keyAddress == loggable.getAddress()) {
#final BTreeMutable tree = context.mainTree;
#tree.addExpiredLoggable(keyAddress);
#final BasePageMutable node = context.currentNode.getMutableCopy(tree);
#node.set(leafIndex, tree.createMutableLeaf(getKey(), getValue()), null);
#context.wasReclaim = true;
#context.setPage(node);
#}
#}

#protected void reclaim(@NotNull final BTreeReclaimTraverser context) {
#final ByteIterable keyIterable = getKey();
#if (!context.canMoveDown() && context.canMoveRight()) {
#final int leafIndex;
#final int cmp = context.compareCurrent(keyIterable);
#if (cmp > 0) {
#return;
#} else if (cmp == 0) {
#leafIndex = context.currentPos;
#} else {
#context.moveRight();
#leafIndex = context.getNextSibling(keyIterable).index;
#}
#if (leafIndex >= 0) {
#doReclaim(context, leafIndex);
#context.moveTo(leafIndex + 1);
#return;
#} else if (context.canMoveTo(-leafIndex - 1)) {
#return;
#}
#}
#// go up
#if (context.canMoveUp()) {
#while (true) {
#context.popAndMutate();
#context.moveRight();
#final int index = context.getNextSibling(keyIterable).index;
#if (index < 0) {
#if (context.canMoveTo(-index - 1) || !context.canMoveUp()) {
#context.moveTo(Math.max(-index - 2, 0));
#break;
#}
#} else {
#context.pushChild(index); // node is always internal
#break;
#}
#}
#}
#// go down
#while (context.canMoveDown()) {
#int index = context.getNextSibling(keyIterable).index;
#if (index < 0) {
#index = Math.max(-index - 2, 0);
#}
#context.pushChild(index);
#}
#int leafIndex = context.getNextSibling(keyIterable).index;
#if (leafIndex >= 0) {
#doReclaim(context, leafIndex);
#context.moveTo(leafIndex + 1);
#} else {
#context.moveTo(-leafIndex - 1);
#}
#}
#}
