package daruma.wfs;

import daruma.xml.handler.MispDefaultHandler;
import daruma.xml.XMLTag;
import daruma.xml.Lexicon;
import daruma.xml.URI;
import daruma.xml.UniversalName;
import daruma.xml.util.XMLFormatConverter;
import daruma.xml.util.ElementUtil;
import daruma.wfs.SOAPFaultDocumentBuilder;

import daruma.storage_manager.schema.XMLSchemaSet;
import daruma.storage_manager.StorageManager;
import daruma.storage_manager.StorageException;
import daruma.storage_manager.type_definition.ElementName;
import daruma.storage_manager.type_definition.TypeName;

import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.Attributes;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import javax.xml.transform.TransformerException;
import javax.xml.parsers.ParserConfigurationException;

import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringReader;

import java.util.List;
import java.util.ArrayList;
import java.util.Date;

import daruma.util.Itk;


public class DescribeFeatureTypeHandler extends MispDefaultHandler
{
	private	StorageManager	storage;

	private	List<UniversalName>	typeList;

	private	boolean		readingTypeName;
	private	StringBuilder	typeNameBuffer;


	public	DescribeFeatureTypeHandler( OutputStream  out ,
					    XMLReader  parser ,
					    boolean  isTopLevelHandler ,
					    StorageManager  storage )
	{
		super( out , parser , isTopLevelHandler );

		this.storage = storage;

		this.typeList = new ArrayList<UniversalName>();

		this.readingTypeName = false;
		this.typeNameBuffer = null;
	}


	public	void	xStartElement( String uri ,
				       String localName ,
				       String qName ,
				       Attributes  attrs ) throws SAXException
	{
		assert super.getCurrentLevel() == 1
			|| super.getCurrentLevel() == 2;

		XMLTag	t = new XMLTag( uri , localName );

		final SAXParseException	mispNamespaceException
					 = new SAXParseException
					       ( t.getLocalName() + " tag "
						 + "should be in namespace "
						 + URI.MISP ,
						 super.getLocator() );

		// <DescribeFeatureType>
		if ( super.getCurrentLevel() == 1 )
		{
			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.throwError( mispNamespaceException );
			}

			if ( ! t.getLocalName()
				 .equals( "DescribeFeatureType" ) )
			{
				this.throwError( new SAXParseException
						 ( "unknown tag "
						   + t.getLocalName() + "." ,
						   super.getLocator() ) );

			}

			return;
		}
		// <DescribeFeatureType>/<TypeName>
		else if ( super.getCurrentLevel() == 2 )
		{
			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.throwError( mispNamespaceException );
			}

			if ( ! t.getLocalName().equals( "TypeName" ) )
			{
				this.throwError( new SAXParseException
						 ( "unexpected tag \""
						   + t.getLocalName()
						   + "\" found, "
						   + "TypeName tag expected." ,
						   super.getLocator() ) );
			}

			this.readingTypeName = true;
			this.typeNameBuffer = new StringBuilder();
		}
	}


	public void  xCharacters( char[]  str ,  int  offset ,  int  length )
							    throws SAXException
	{
		if ( this.readingTypeName )
		{
			this.typeNameBuffer.append( str , offset , length );
		}
	}


	public	void	xEndElement( String uri ,
				     String localName ,
				     String qName ) throws SAXException
	{
		if ( this.readingTypeName )
		{
			try
			{
				this.typeList.add
				( super.convertQNameStringToUniversalName
				  ( this.typeNameBuffer.toString() ) );
			}
			catch( SAXException  e )
			{
				this.throwError
					( new SAXParseException
					  ( "type definition of \""
					    + typeNameBuffer.toString()
					    + "\" not found"
					    + ": " + e.getMessage() ,
					    super.getLocator() ) );
			}

			this.readingTypeName = false;
			this.typeNameBuffer = null;
		}
	}

	public	void  xEndDocument() throws SAXException
	{
		/* <<< [06/09/07 16:53 I.Noda] <<< */
		//List<String>	allSchemas = new ArrayList<String>();
  		/* === [06/09/07 16:53 I.Noda] === */
		XMLSchemaSet schemaSet = new XMLSchemaSet() ;
		/* >>> [06/09/07 16:53 I.Noda] >>> */

		for( UniversalName  typeName : this.typeList )
		{
			try
			{
			    /* <<< [06/09/07 16:53 I.Noda] <<< */
			    //List<String>	schemas;
			    //
			    //schemas = this.storage.getSchemasOfElement
			    //	( new ElementName( typeName ) );
			    //
			    //allSchemas.addAll( schemas );
			    /* === [06/09/07 16:53 I.Noda] === */
			    this.storage.getSchemasOfElement
				(new ElementName( typeName ),
				 schemaSet) ;
			    /* >>> [06/09/07 16:53 I.Noda] >>> */
			}
			catch( StorageException  e )
			{
				this.throwError
					( new SAXParseException
					  ( "type definition of "
					    + typeName + " not found" ,
					    super.getLocator() ) );
			}
		}

		/* <<< [06/08/20 00:39 I.Noda] <<< 
		 * constructResponseInfo が完成すれば、
		 * 後半部分 (=== 以下)は省略すべし。
		 */
		/* !!! [06/09/07 16:53 I.Noda] !!! */
		//constructResponseInfo(allSchemas) ;
		constructResponseInfo(schemaSet) ;
		/* === [06/08/20 00:39 I.Noda] === */
		/* !!! [06/08/20 22:50 I.Noda] !!! 
		 * DOM による Response 出力への切り替えへの布石。
		 */
		if(! Itk.useDOMResponse) {

		    PrintWriter	out = super.getPrintWriter();

		    out.println( "<misp:DescribeFeatureTypeResponse"
				 + " xmlns:misp=\"" + URI.MISP + "\">" );
		    out.flush();

		    try {
			this.getOutputStream().flush();
		    }
		    catch( java.io.IOException  e ) {
			e.printStackTrace();

			throw new SAXException( e );
		    }

		    for( UniversalName  typeName : this.typeList ) {
			/* !!! [06/09/07 16:53 I.Noda] !!! */
			//for ( String  s : allSchemas ) {
			for ( XMLSchemaSet.Entry entry : schemaSet.getList()) {
			    out.println();
			    out.println( "<!-- ========== "
					 + typeName.getLocalName()
					 + " in namespace "
					 + typeName.getNamespace() + "'s"
					 + " definition"
					 + " ========== -->" );
			    try {
				/* <<< [06/09/07 16:53 I.Noda] <<< */
				//XMLFormatConverter
				//    .printTextWithoutXMLDeclaration
				//    ( new StringReader( s ) , out );
				/* === [06/09/07 16:53 I.Noda] === */
				XMLFormatConverter
				    .printTextWithoutXMLDeclaration
				    ( new StringReader(entry.getString()), 
				      out );
				/* >>> [06/09/07 16:53 I.Noda] >>> */
			    }
			    catch( TransformerException e ) {
				e.printStackTrace();
			    }
			}
		    }
		    /* !!! [06/09/07 16:53 I.Noda] !!! */
		    //if ( allSchemas.size() != 0 )
		    if ( schemaSet.size() != 0 )
			{
			    out.println();
			}

		    out.println( "</misp:DescribeFeatureTypeResponse>" );
		    out.flush();
		}
		/* >>> [06/08/20 00:39 I.Noda] >>> */
	}

    //------------------------------------------------------------
    /* !!! [06/08/20 00:39 I.Noda] !!! 
     * construct ResponseInfo
     */
    /* <<< [06/09/07 16:53 I.Noda] <<< */
    //private void constructResponseInfo(List<String> allSchemas) 
    //	throws SAXException
    /* === [06/09/07 16:53 I.Noda] === */
    private void constructResponseInfo(XMLSchemaSet schemaSet) 
    	throws SAXException
    /* >>> [06/09/07 16:53 I.Noda] >>> */
    {
	ResponseInfo responseInfo = this.getResponse() ;
	Document doc = responseInfo.document ;
	Element response = 
	    ElementUtil.genElementSimple
	    (Lexicon.MispDescribeFeatureTypeResponse,
	     null, doc, responseInfo.leaf, true) ;
	responseInfo.setResponseToLeaf(response) ;

	try {
	    Date ctime = this.storage.getCurrentTime(); 
	    Date btime= ctime ;
	    Date etime = btime ;

	    responseInfo.addResponseStatusToLeaf
		(this.storage.getMostRecentTransactionURI(),
		 btime, etime) ;
	} catch( StorageException  ex ) { 
	    ex.printStackTrace() ; 
	    throw new SAXException(ex) ;
	}
	
	/* !!! [06/09/07 16:53 I.Noda] !!! */
	//for( UniversalName  typeName : this.typeList ) {
	//for ( String  schemaStr : allSchemas ) {
	    for ( XMLSchemaSet.Entry entry : schemaSet.getList()) {
		/* <<< [06/09/07 16:53 I.Noda] <<< */
		//response.appendChild
		//    (doc.createComment(" ========== " 
		//		       + typeName.getLocalName()
		//		       + " in namespace "
		//		       + typeName.getNamespace() + "'s"
		//		       + " definition"
		//		       + " ========== " )) ;
		/* === [06/09/07 16:53 I.Noda] === */
		response.appendChild
		    (doc.createComment(" ========== " 
				       + "schema ID=" 
				       + entry.getID() + " / "
				       + "target NS="
				       + entry.getTargetNameSpace() 
				       + " ========== " )) ;
		/* >>> [06/09/07 16:53 I.Noda] >>> */
		try {
		    /* <<< [06/09/07 16:53 I.Noda] <<< */
		    //XMLFormatConverter.readNode(new StringReader(schemaStr),
		    //				response) ;
		    /* === [06/09/07 16:53 I.Noda] === */
		    XMLFormatConverter
			.readNode(new StringReader(entry.getString()),
				  response) ;
		    /* >>> [06/09/07 16:53 I.Noda] >>> */
		} catch ( TransformerException ex ) {
		    ex.printStackTrace();
		    throw new SAXException(ex) ;
		}
	    }
	/* !!! [06/09/07 16:53 I.Noda] !!! */
	//}
    }


    //------------------------------------------------------------
	public	void	throwError( SAXParseException  e ) throws SAXException
	{
		Document	doc;

		try
		{
			XMLFormatConverter
				.print( new SOAPFaultDocumentBuilder(e)
							      .newDocument() ,
					this.getOutputStream() );
		}
		catch( ParserConfigurationException  pe )
		{
			throw new SAXException( pe );
		}
		catch( TransformerException  te )
		{
			throw new SAXException( te );
		}

		throw e;
	}
}
