/*
 * MyProtocolParser.java
 *
 * Created on 2008¦~05¤ë08¤é ¬P´Á¥|, ¤U¤È9:06
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package grizzlyserver;

import com.sun.grizzly.ProtocolParser;
import com.sun.grizzly.util.ByteBufferFactory;
import com.sun.grizzly.util.WorkerThread;
import java.nio.ByteBuffer;

/**
 *
 * @author kentsang
 */
public class MyProtocolParser implements ProtocolParser<ByteBuffer> {

    // saved buffer
    private ByteBuffer savedBuffer = null;

    // saved buffer
    private ByteBuffer packetBuffer = null;

    public static final byte STX = 0x02;
    
    public static final byte ETX = 0x03;
    
    // array index of STX in data
    private int start = -1;
    
    // array index of ETX in data
    private int end = -1;

    // current position of savedBuffer
    private int pos = 0;
    
    // for debug 
    private static boolean printArray = true;
    
    // for debug
    private static boolean debug = true;
    
    /**
     * Creates a new instance of MyProtocolParser
     */
    public MyProtocolParser() {
    }
    
    public void startBuffer(ByteBuffer bb) {
        if (savedBuffer != null && savedBuffer != bb) System.out.println("WARNING: savedBuffer wasn't released!");
        if (debug) System.out.println("startBuffer / bb = " + bb);
        
        if (savedBuffer != null) return;
        
        savedBuffer = bb;
        savedBuffer.flip();
        if (debug) System.out.println("startBuffer / bb = " + savedBuffer);
        if (debug) printByteBuffer();
        //partial = false;
    }
    
    public boolean hasMoreBytesToParse() {
        if (debug) System.out.println("hasMoreBytesToParse()");
        if (savedBuffer == null) {
            if (debug) System.out.println("hasMoreBytesToParse() savedBuffer == null, return false");
            return false;
        }
        
        boolean result = pos < savedBuffer.limit();
        if (debug) System.out.println("hasMoreBytesToParse() pos / limit / return = " + pos + " / " + savedBuffer.limit() + " / " + result);
        return result;
    }
    
    public boolean isExpectingMoreData() {
        boolean isExpectingMore = (start > -1 && end == -1);
        if (debug) System.out.println(this.toString() + " | isExpectingMoreData() = " + isExpectingMore);
        return isExpectingMore;
    }
    
    public ByteBuffer getNextMessage() {
        System.out.print("\r\nGetMessage:printByteBuffer: ");
        printPacketBuffer();
        System.out.println("\r\n");
        return packetBuffer;
    }
    
    public boolean hasNextMessage() {
        if (debug) System.out.print("hasNextMessage() before savedBuffer = " + savedBuffer + " start: " + start + " pos: " + pos + " end: " + end);
        if (debug) printByteBuffer();
        if (savedBuffer == null) return false;

        if (end != -1) {
            // If complete packet was just parsed but we have more bytes to parse
            start = -1;
            end = -1;
        }
        
        for(; pos<savedBuffer.limit(); pos++) {
            byte b = savedBuffer.get(pos);
            if (b == STX) {
                if (debug) System.out.println("STX !!!! pos = " + pos);
                if (start != -1) System.out.println("WARNING STX is met twice!");
                start = pos;
            } else if (b == ETX && start > -1) {
                if (debug) System.out.println("ETX !!!! pos = " + pos);
                end = pos;
                packetBuffer = createPacketBuffer(savedBuffer, start, end);
                if (debug) printPacketBuffer();
                pos++;
                return true;
            }
        }
        
        if (debug) System.out.println("hasNextMessage() start = " + start + " position = " + pos + " end = " + end);
        
        return false;
    }
    
    public boolean releaseBuffer() {
        if (debug) System.out.println("releaseBuffer() pos / savedBuffer = " + savedBuffer.position() + " / " + savedBuffer);
        
        boolean saveCurrentParserState = false;
        
        if (isExpectingMoreData()) {
            if (start > 0) {
                // If we expect more data to come, but start is not aligned to position 0 - align it
                savedBuffer.position(start);
                savedBuffer.compact();
                pos -= start;
                start = 0;
            } else if (savedBuffer.limit() == savedBuffer.capacity()) {
                // We expect more data, but buffer is full. Reallocate the buffer
                savedBuffer = reallocateByteBuffer(savedBuffer);
            }
            
            savedBuffer.position(pos);
            savedBuffer.limit(savedBuffer.capacity());
            savedBuffer = null;
            saveCurrentParserState = true;
            if (debug) System.out.println("releaseBuffer() saveState. start: " + start + " pos: " + pos + " end: " + end);
        } else {
            start = end = -1;
            pos = 0;
            
            if (savedBuffer != null) {
                savedBuffer.clear();
            }
            savedBuffer = null;
            if (debug) System.out.println("releaseBuffer() savedBuffer cleared! savedBuffer = null");
        }
        
        return saveCurrentParserState;
    }
    
    public void printByteBuffer(){
        if (!printArray) return;
        if (debug) System.out.print("PrintByteBuffer (savedBuffer) = " + savedBuffer);
        if (savedBuffer == null) return;
        for (int i = 0 ; i < savedBuffer.limit() && i < 100 ; i++) {
            System.out.print(Integer.toHexString((int)savedBuffer.get(i) & 0xff) + " ");
        }
        System.out.println("");
    }
    
    public void printPacketBuffer(){
        if (!printArray) return;
        if (debug) System.out.print("PrintPacketBuffer (packetBuffer) = " + packetBuffer);
        if (packetBuffer == null) return;
        for (int i = 0 ; i < packetBuffer.limit() && i < 100 ; i++) {
            System.out.print(Integer.toHexString((int)packetBuffer.get(i) & 0xff) + " ");
        }
        System.out.println("");
    }
    
    private static ByteBuffer createPacketBuffer(ByteBuffer srcBuffer, int start, int end) {
        // Slice source ByteBuffer and get packet ByteBuffer, related to the single packet
        int currentPos = srcBuffer.position();
        int currentLimit = srcBuffer.limit();
        
        srcBuffer.position(start);
        srcBuffer.limit(end + 1);
        ByteBuffer packetByteBuffer = srcBuffer.slice();
        
        // Restore saved ByteBuffer
        srcBuffer.limit(currentLimit);
        srcBuffer.position(currentPos);
        
        return packetByteBuffer;
    }

    private ByteBuffer reallocateByteBuffer(ByteBuffer savedBuffer) {
        savedBuffer.position(0);
        savedBuffer.limit(savedBuffer.capacity());
        ByteBuffer newBuffer = 
                ByteBufferFactory.allocateView(savedBuffer.capacity() * 2, 
                savedBuffer.isDirect());
        newBuffer.put(savedBuffer);
        
        WorkerThread workerThread = (WorkerThread) Thread.currentThread();
        workerThread.setByteBuffer(newBuffer);
        return newBuffer;
    }
}