package morfologik.stemming;

import java.nio.ByteBuffer;

/**
 * Encodes <code>dst</code> relative to <code>src</code> by trimming whatever
 * non-equal suffix <code>src</code> has. The output code is (bytes):
 * 
 * <pre>
 * {K}{suffix}
 * </pre>
 * 
 * where (<code>K</code> - 'A') bytes should be trimmed from the end of
 * <code>src</code> and then the <code>suffix</code> should be appended to the
 * resulting byte sequence.
 * 
 * <p>
 * Examples:
 * </p>
 * 
 * <pre>
 * src: foo
 * dst: foobar
 * encoded: Abar
 * 
 * src: foo
 * dst: bar
 * encoded: Dbar
 * </pre>
 */
public class TrimSuffixEncoder implements ISequenceEncoder {
  /**
   * Maximum encodable single-byte code.
   */
  private static final int REMOVE_EVERYTHING = 255;

  public ByteBuffer encode(ByteBuffer reuse, ByteBuffer source, ByteBuffer target) {
    int sharedPrefix = BufferUtils.sharedPrefixLength(source, target);
    int truncateBytes = source.remaining() - sharedPrefix;
    if (truncateBytes >= REMOVE_EVERYTHING) {
      truncateBytes = REMOVE_EVERYTHING;
      sharedPrefix = 0;
    }

    reuse = BufferUtils.clearAndEnsureCapacity(reuse, 1 + target.remaining() - sharedPrefix); 

    assert target.hasArray() && 
           target.position() == 0 && 
           target.arrayOffset() == 0;

    final byte suffixTrimCode = (byte) (truncateBytes + 'A');
    reuse.put(suffixTrimCode)
         .put(target.array(), sharedPrefix, target.remaining() - sharedPrefix)
         .flip();

    return reuse;
  }
  
  @Override
  public int prefixBytes() {
    return 1;
  }
  
  public ByteBuffer decode(ByteBuffer reuse, ByteBuffer source, ByteBuffer encoded) {
    assert encoded.remaining() >= 1;

    int suffixTrimCode = encoded.get(encoded.position());
    int truncateBytes = (suffixTrimCode - 'A') & 0xFF;
    if (truncateBytes == REMOVE_EVERYTHING) {
      truncateBytes = source.remaining();
    }

    final int len1 = source.remaining() - truncateBytes;
    final int len2 = encoded.remaining() - 1;

    reuse = BufferUtils.clearAndEnsureCapacity(reuse, len1 + len2);

    assert source.hasArray() && 
           source.position() == 0 && 
           source.arrayOffset() == 0;

    assert encoded.hasArray() && 
           encoded.position() == 0 && 
           encoded.arrayOffset() == 0;

    reuse.put(source.array(), 0, len1)
         .put(encoded.array(), 1, len2)
         .flip();

    return reuse;
  }
  
  @Override
  public String toString() {
    return getClass().getSimpleName();
  }
}