/**
 * 
 */
package org.maodian.flycat.netty.handler;

import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigDecimal;

import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.jabber.etherx.streams.Features;
import org.jabber.etherx.streams.Stream;
import org.maodian.flycat.holder.JAXBContextHolder;

/**
 * @author Cole Wen
 * 
 */
public class StreamElementExtractHandler extends ChannelInboundMessageHandlerAdapter<String> {
  private static final String XML_DECLARATION_MARK = "<?xml ";

  private StringBuilder xml;
  private boolean recvXmlDeclFlag = false;
  private boolean recvStrmTagFlag = false;
  private Unmarshaller unmarshaller;
  private Marshaller marshaller;
  private String language;

  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {
    xml = new StringBuilder(256);
    unmarshaller = JAXBContextHolder.getJAXBContext().createUnmarshaller();
    marshaller = JAXBContextHolder.getJAXBContext().createMarshaller();
    language = "en";
    super.channelActive(ctx);
  }

  /*
   * (non-Javadoc)
   * 
   * @see
   * io.netty.channel.ChannelInboundMessageHandlerAdapter#messageReceived(io
   * .netty.channel.ChannelHandlerContext, java.lang.Object)
   */
  @Override
  protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
    // xml declaration
    if (StringUtils.startsWith(msg, XML_DECLARATION_MARK)) {
        if (!recvXmlDeclFlag) {
        xml.append(msg);
        recvXmlDeclFlag = true;
        return;
      }
      // TODO: deal with duplicated xml declaration
      throw new RuntimeException("deal with duplicated xml declaration");
    }

    // get namespace prefix of <stream /> or <prefix:stream />
    if (!recvStrmTagFlag) {
      String prefix = StringUtils.startsWith(msg, "<stream ") ? "</stream>" 
          : StringUtils.substringBetween(msg, "<", ":stream ");
      String cloesTag = "</" + prefix + ":stream>";
      xml.append(msg).append(cloesTag);
      
      Stream initStream = (Stream) unmarshaller.unmarshal(new StringReader(xml.toString()));
      Stream respStream = new Stream();
      if (StringUtils.isNotBlank(initStream.getFrom())) {
        respStream.setTo(initStream.getFrom());
      }
      
      respStream.setId(RandomStringUtils.randomAlphabetic(32));
      if (StringUtils.isNotBlank(initStream.getLang())) {
        // store it for later use
        language = initStream.getLang();
      } 
      respStream.setLang(language);
      
      // only present version attribute when initial stream presents it
      if (initStream.getVersion() != null) {
        respStream.setVersion(new BigDecimal("1.0"));
      }
      
      respStream.setFeatures(new Features());
      recvStrmTagFlag = true;
      
      StringWriter writer = new StringWriter(256);
      marshaller.marshal(respStream, writer);
      ctx.write(writer.toString());
      return;
    }
    
    // TODO: deal with duplicated stream tag
    throw new RuntimeException("deal with duplicated stream tag");
  }

}

@@ -44,24 +44,133 @@
 y.handler;%0A%0A
+import io.netty.channel.ChannelHandlerContext;%0Aimport io.netty.channel.ChannelInboundMessageHandlerAdapter;%0A%0A
 import java.
@@ -245,16 +245,47 @@
 cimal;%0A%0A
+import javax.xml.XMLConstants;%0A
 import j
@@ -345,120 +345,132 @@
 shal
-ler;%0A%0Aimport io.netty.channel.ChannelHandlerContext;%0Aimport io.netty.channel.ChannelInboundMessageHandlerAdapter
+Exception;%0Aimport javax.xml.bind.Unmarshaller;%0Aimport javax.xml.validation.Schema;%0Aimport javax.xml.validation.SchemaFactory
 ;%0A%0Ai
@@ -1103,16 +1103,41 @@
 anguage;
+%0A  private Schema schema;
 %0A%0A  @Ove
@@ -1254,84 +1254,8 @@
 6);%0A
-    unmarshaller = JAXBContextHolder.getJAXBContext().createUnmarshaller();%0A
     
@@ -1343,16 +1343,330 @@
 = %22en%22;%0A
+    SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);%0A    schema = schemaFactory.newSchema(StreamElementExtractHandler.class.getResource(%22/xsd/streams.xsd%22));%0A    unmarshaller = JAXBContextHolder.getJAXBContext().createUnmarshaller();%0A    unmarshaller.setSchema(schema);%0A
     supe
@@ -2653,16 +2653,48 @@
  Stream 
+initStream;%0A      try %7B%0A        
 initStre
@@ -2761,24 +2761,243 @@
 String()));%0A
+      %7D catch (UnmarshalException e) %7B%0A        // TODO: deal with invalid stream xml and respond %3Cinvalid-namespace/%3E or ,bad-format/%3E%0A        // 4.8.1.  Stream Namespace%0A        throw new RuntimeException(%22%22);%0A      %7D%0A
       Stream
@@ -3937,11 +3937,10 @@
 %22);%0A  %7D%0A
-%0A
 %7D%0A

validate receiving stream xml against schema when unmarshelling