package net.ihiroky.niotty.stage.codec.frame;

import net.ihiroky.niotty.LoadStage;
import net.ihiroky.niotty.LoadStageContext;
import net.ihiroky.niotty.buffer.Buffers;
import net.ihiroky.niotty.buffer.DecodeBuffer;
import net.ihiroky.niotty.event.MessageEvent;
import net.ihiroky.niotty.event.TransportStateEvent;

/**
 * @author Hiroki Itoh
 */
public class FrameLengthRemoveDecoder implements LoadStage<DecodeBuffer, DecodeBuffer> {

    private int poolingFrameBytes;
    private DecodeBuffer pooling;

    @Override
    public void load(LoadStageContext<DecodeBuffer, DecodeBuffer> context, MessageEvent<DecodeBuffer> event) {
        DecodeBuffer input = event.getMessage();

        for (;;) {
            int frameBytes = poolingFrameBytes;

            // load frame length
            if (frameBytes == 0) {
                input = readFully(input, FrameLengthPrependEncoder.MINIMUM_WHOLE_LENGTH);
                if (input == null) {
                    return;
                }
                frameBytes = input.readVariableByteInteger();
            }

            // load frame
            DecodeBuffer output = readFully(input, frameBytes);
            if (output == null) {
                poolingFrameBytes = frameBytes;
                return;
            }

            poolingFrameBytes = 0;
            if (output.remainingBytes() == frameBytes) {
                context.proceed(new MessageEvent<>(event.getTransport(), output));
                return;
            }
            // if (output.remainingBytes() > frameBytes) {
            output = Buffers.newDecodeBuffer(frameBytes);
            output.drainFrom(input, frameBytes);
            context.proceed(new MessageEvent<>(event.getTransport(), output));
        }
    }

    @Override
    public void load(LoadStageContext<DecodeBuffer, DecodeBuffer> context, TransportStateEvent event) {
        context.proceed(event);
    }

    private DecodeBuffer readFully(DecodeBuffer input, int requiredLength) {
        if (pooling != null) {
            pooling.drainFrom(input);
            if (pooling.remainingBytes() >= requiredLength) {
                DecodeBuffer fulfilled = pooling;
                pooling = null;
                return fulfilled;
            }
            return null;
        }

        if (input.remainingBytes() >= requiredLength) {
            return input;
        }

        DecodeBuffer p = Buffers.newDecodeBuffer(new byte[requiredLength], 0, 0);
        p.drainFrom(input);
        pooling = p;
        return null;
    }

    int getPoolingFrameBytes() {
        return poolingFrameBytes;
    }

    DecodeBuffer getPooling() {
        return pooling;
    }
}

@@ -460,24 +460,25 @@
 ngFrameBytes
+_
 ;%0A    privat
@@ -499,16 +499,17 @@
  pooling
+_
 ;%0A%0A    @
@@ -743,16 +743,17 @@
 ameBytes
+_
 ;%0A%0A     
@@ -1222,16 +1222,17 @@
 ameBytes
+_
  = frame
@@ -1306,16 +1306,17 @@
 ameBytes
+_
  = 0;%0A  
@@ -2006,16 +2006,17 @@
 (pooling
+_
  != null
@@ -2038,16 +2038,17 @@
  pooling
+_
 .drainFr
@@ -2081,16 +2081,17 @@
 (pooling
+_
 .remaini
@@ -2165,24 +2165,25 @@
 ed = pooling
+_
 ;%0A          
@@ -2195,16 +2195,17 @@
  pooling
+_
  = null;
@@ -2507,16 +2507,17 @@
  pooling
+_
  = p;%0A  
@@ -2603,24 +2603,25 @@
 ngFrameBytes
+_
 ;%0A    %7D%0A%0A   
@@ -2645,24 +2645,24 @@
 Pooling() %7B%0A
-
         retu
@@ -2671,16 +2671,17 @@
  pooling
+_
 ;%0A    %7D%0A

Fix field name in package codec.frame