package redradishes.decoder;

import redradishes.UncheckedCharacterCodingException;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CoderResult;

public class BulkStringBuilders {
  private static final BulkStringBuilderFactory<byte[]> BYTE_ARRAY_BUILDER_FACTORY = (length, charsetDecoder) -> {
    byte[] bytes = new byte[length];
    return new BulkStringBuilderFactory.Builder<byte[]>() {
      int offset = 0;

      @Override
      public void append(ByteBuffer buffer) {
        int len = buffer.remaining();
        buffer.get(bytes, offset, len);
        offset += len;
      }

      @Override
      public void appendLast(ByteBuffer buffer) {
        append(buffer);
      }

      @Override
      public byte[] build() {
        return bytes;
      }
    };
  };
  private static final BulkStringBuilderFactory<CharSequence> CHAR_SEQUENCE_BUILDER_FACTORY = charSequence();
  private static final BulkStringBuilderFactory<Integer> INTEGER_BUILDER_FACTORY =
      CHAR_SEQUENCE_BUILDER_FACTORY.map(Object::toString).map(Integer::valueOf);
  private static final BulkStringBuilderFactory<Long> LONG_BUILDER_FACTORY =
      CHAR_SEQUENCE_BUILDER_FACTORY.map(Object::toString).map(Long::valueOf);

  public static BulkStringBuilderFactory<CharSequence> charSequence() {
    return (length, charsetDecoder) -> {
      CharBuffer charBuffer = CharBuffer.allocate((int) (length * charsetDecoder.maxCharsPerByte()));
      return new BulkStringBuilderFactory.Builder<CharSequence>() {
        @Override
        public void append(ByteBuffer buffer) {
          checkResult(charsetDecoder.decode(buffer, charBuffer, false));
        }

        @Override
        public void appendLast(ByteBuffer buffer) {
          checkResult(charsetDecoder.decode(buffer, charBuffer, true));
          checkResult(charsetDecoder.flush(charBuffer));
          charsetDecoder.reset();
        }

        @Override
        public CharSequence build() {
          charBuffer.flip();
          return charBuffer;
        }

        private void checkResult(CoderResult coderResult) {
          if (!coderResult.isUnderflow()) {
            try {
              coderResult.throwException();
            } catch (CharacterCodingException e) {
              throw new UncheckedCharacterCodingException(e);
            }
          }
        }
      };
    };
  }

  public static BulkStringBuilderFactory<String> string() {
    return charSequence().map(Object::toString);
  }

  public static BulkStringBuilderFactory<Integer> integer() {
    return INTEGER_BUILDER_FACTORY;
  }

  public static BulkStringBuilderFactory<Long> _long() {
    return LONG_BUILDER_FACTORY;
  }

  public static BulkStringBuilderFactory<byte[]> byteArray() {
    return BYTE_ARRAY_BUILDER_FACTORY;
  }
}

@@ -340,16 +340,22 @@
 ACTORY =
+%0A     
  (length
@@ -378,58 +378,8 @@
 ) -%3E
- %7B%0A    byte%5B%5D bytes = new byte%5Blength%5D;%0A    return
  new
@@ -426,24 +426,89 @@
 %3E() %7B%0A      
+  private final byte%5B%5D bytes = new byte%5Blength%5D;%0A        private 
 int offset =
@@ -510,32 +510,34 @@
 set = 0;%0A%0A      
+  
 @Override%0A      
@@ -522,32 +522,34 @@
       @Override%0A
+  
       public voi
@@ -570,32 +570,34 @@
 uffer buffer) %7B%0A
+  
         int len 
@@ -626,16 +626,18 @@
         
+  
 buffer.g
@@ -668,16 +668,18 @@
         
+  
 offset +
@@ -683,35 +683,39 @@
 t += len;%0A      
+ 
+ 
 %7D%0A%0A
+  
       @Override%0A
@@ -716,24 +716,26 @@
 rride%0A      
+ 
+ 
 public void 
@@ -774,16 +774,18 @@
         
+  
 append(b
@@ -792,16 +792,18 @@
 uffer);%0A
+  
       %7D%0A
@@ -805,24 +805,26 @@
    %7D%0A%0A      
+  
 @Override%0A  
@@ -817,24 +817,26 @@
   @Override%0A
+  
       public
@@ -857,24 +857,26 @@
 ) %7B%0A        
+ 
+ 
 return bytes
@@ -887,17 +887,16 @@
     
+  
 %7D%0A    
-%7D;%0A
   %7D;
@@ -993,463 +993,119 @@
 RY =
- charSequence();%0A  private static final BulkStringBuilderFactory%3CInteger%3E INTEGER_BUILDER_FACTORY =%0A      CHAR_SEQUENCE_BUILDER_FACTORY.map(Object::toString).map(Integer::valueOf);%0A  private static final BulkStringBuilderFactory%3CLong%3E LONG_BUILDER_FACTORY =%0A      CHAR_SEQUENCE_BUILDER_FACTORY.map(Object::toString).map(Long::valueOf);%0A%0A  public static BulkStringBuilderFactory%3CCharSequence%3E charSequence() %7B%0A    return (length, charsetDecoder) -%3E %7B%0A     
+%0A      (length, charsetDecoder) -%3E new BulkStringBuilderFactory.Builder%3CCharSequence%3E() %7B%0A        private final
  Cha
@@ -1201,75 +1201,8 @@
 ));%0A
-      return new BulkStringBuilderFactory.Builder%3CCharSequence%3E() %7B
 %0A   
@@ -2043,19 +2043,446 @@
    %7D;%0A  
-  %7D
+private static final BulkStringBuilderFactory%3CInteger%3E INTEGER_BUILDER_FACTORY =%0A      CHAR_SEQUENCE_BUILDER_FACTORY.map(Object::toString).map(Integer::valueOf);%0A  private static final BulkStringBuilderFactory%3CLong%3E LONG_BUILDER_FACTORY =%0A      CHAR_SEQUENCE_BUILDER_FACTORY.map(Object::toString).map(Long::valueOf);%0A%0A  public static BulkStringBuilderFactory%3CCharSequence%3E charSequence() %7B%0A    return CHAR_SEQUENCE_BUILDER_FACTORY
 ;%0A  %7D%0A%0A 
@@ -2551,30 +2551,45 @@
  return 
-charSequence()
+CHAR_SEQUENCE_BUILDER_FACTORY
 .map(Obj

Clean up BulkStringBuilders