content: package com.akdeniz.googleplaycrawler.gsf;
  
  import java.io.IOException;
  import java.nio.ByteBuffer;
  
  import org.apache.mina.core.buffer.IoBuffer;
  import org.apache.mina.core.session.IoSession;
  import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
  import org.apache.mina.filter.codec.ProtocolCodecFactory;
  import org.apache.mina.filter.codec.ProtocolDecoder;
  import org.apache.mina.filter.codec.ProtocolDecoderOutput;
  import org.apache.mina.filter.codec.ProtocolEncoder;
  import org.apache.mina.filter.codec.ProtocolEncoderOutput;
  
  import com.akdeniz.googleplaycrawler.gsf.GoogleServicesFramework.BindAccountResponse;
  import com.akdeniz.googleplaycrawler.gsf.GoogleServicesFramework.Close;
  import com.akdeniz.googleplaycrawler.gsf.GoogleServicesFramework.DataMessageStanza;
  import com.akdeniz.googleplaycrawler.gsf.GoogleServicesFramework.HeartbeatAck;
  import com.akdeniz.googleplaycrawler.gsf.GoogleServicesFramework.IQStanza;
  import com.akdeniz.googleplaycrawler.gsf.GoogleServicesFramework.LoginResponse;
  import com.akdeniz.googleplaycrawler.gsf.packets.Packet;
  import com.akdeniz.googleplaycrawler.gsf.packets.UnknownResponse;
  /**
   * Protocol codec factory for MINA to encode & decode related packets.
   * 
   * @author akdeniz
   *
   */
  public class GSFCodecFactory implements ProtocolCodecFactory {
  
      private ProtocolEncoder protocolEncoder;
      private ProtocolDecoder protocolDecoder;
  
      public GSFCodecFactory() {
  	protocolEncoder = new GSFProtocolEncoder();
  	protocolDecoder = new GSFRequestDecoder();
      }
  
      @Override
      public ProtocolEncoder getEncoder(IoSession session) throws Exception {
  	return protocolEncoder;
      }
  
      @Override
      public ProtocolDecoder getDecoder(IoSession session) throws Exception {
  	return protocolDecoder;
      }
  
  }
  
  class GSFRequestDecoder extends CumulativeProtocolDecoder {
  
      @Override
      protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
  
  	ByteBuffer buffer = in.buf();
  	buffer.mark();
  	boolean success = parse(buffer, out);
  	if (!success) {
  	    buffer.reset();
  	    return false;
  	}
  	return true;
  
      }
      
      public static boolean parse(ByteBuffer buffer, ProtocolDecoderOutput out) throws IOException {
  
  	if (buffer.remaining() == 1) { // one byte sanity check
  	    byte[] data = new byte[1];
  	    buffer.get(data);
  	    out.write(new UnknownResponse(0, 0, data));
  	    return true;
  	}
  
  	if (buffer.remaining() < 2) {
  	    return false;
  	}
  
  	byte tag = buffer.get();
  	int length = Packet.unlength(buffer);
  
  	if (buffer.remaining() < length) {
  	    return false;
  	}
  	byte[] data = new byte[length];
  	buffer.get(data);
  
  	switch (tag) {
  	case 0x01:
  	    out.write(HeartbeatAck.parseFrom(data));
  	    return true;
  	case 0x03:
  	    out.write(LoginResponse.parseFrom(data));
  	    return true;
  	case 0x04:
  	    out.write(Close.parseFrom(data));
  	    return true;
  	case 0x07:
  	    out.write(IQStanza.parseFrom(data));
  	    return true;
  	case 0x08:
  	    out.write(DataMessageStanza.parseFrom(data));
  	    return true;
  	case 0x0E:
  	    out.write(BindAccountResponse.parseFrom(data));
  	    return true;
  	default:
  	    out.write(new UnknownResponse(tag, length, data));
  	    return true;
  	}
      }
  }
  
  class GSFProtocolEncoder implements ProtocolEncoder {
  
      @Override
      public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
  
  	if (message instanceof Packet) {
  	    Packet packet = (Packet) message;
  	    out.write(IoBuffer.wrap(packet.getBytes()));
  	} else {
  	    out.write(message);
  	}
      }
  
      @Override
      public void dispose(IoSession session) throws Exception {
      }
  }
  