package daruma.wfs;

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

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

import daruma.xml.URI;
import daruma.xml.handler.MispDefaultHandler;
import daruma.xml.util.XMLFormatConverter;
import daruma.storage_manager.type_definition.ElementName;

import daruma.storage_manager.StorageManager;
import daruma.storage_manager.StorageException;
import daruma.auth.PermissionManager;
import daruma.auth.AuthenticationInfo;

import daruma.wfs.ResponseInfo;
import daruma.util.Itk;


public class SOAPHandler extends MispDefaultHandler
{
	private	StorageManager		storage;
	private PermissionManager	permissionManager;
	private	boolean			soapEnvelopePrinted;
	private	AuthenticationInfo	authInfo;

	public	SOAPHandler( OutputStream  out ,
			     XMLReader  parser ,
			     boolean  isTopLevelHandler ,
			     AuthenticationInfo  authInfo ,
			     StorageManager  storage,
			     PermissionManager  permissionManager )
	{
		super( out , parser , isTopLevelHandler );

		this.storage = storage;
		this.authInfo = authInfo;
		this.permissionManager = permissionManager;

		this.soapEnvelopePrinted = false;
	}


	public	void	xStartElement( String uri ,
				       String localName ,
				       String qName ,
				       Attributes  attrs ) throws SAXException
	{
		ElementName t = new ElementName( uri , localName );

		if ( t.getNamespaceNonNull().equals( URI.SOAP_ENV ) )
		{
			if ( super.getCurrentLevel() == 1 )
			{
				if ( ! t.equals
				     ( new ElementName( URI.SOAP_ENV ,
							"Envelope" ) ) )
				{
					SAXParseException
					e = new SAXParseException
						 ( "top level tag of SOAP"
						   + " shold be an"
						   + " \"Envelope\" tag " ,
						   super.getLocator() );

					this.printError( e );

					throw e;
				}
				
				this.setResponse(new ResponseInfo(true)) ;

				return;
			}
			else if ( t.getLocalName().equals( "Header" ) )
			{
				// XXX: not implemented yet
			}
			else if ( t.getLocalName().equals( "Body" ) )
			{

			    /* !!! [06/08/20 22:50 I.Noda] !!! 
			     * DOM による Response 出力への切り替えへの布石。
			     */
			    if(! Itk.useDOMResponse) {
				PrintWriter	out = super.getPrintWriter();

				out.println( "<SOAP-ENV:Envelope"
					     + " xmlns:SOAP-ENV=\""
					     + URI.SOAP_ENV + "\">" );
				out.println( "  <SOAP-ENV:Body>" );

				out.flush();

				this.soapEnvelopePrinted = true;
			    }
			}
		}
		else
		{
		    if(!this.hasResponse()) {
			this.setResponse(new ResponseInfo(false)) ;
		    }

		    /*
		     * とりあえず mostRecentTransactionURI をセット。
		     * ただし、正確には lock をかけた後でないと
		     * おかしい。
		     * （storage が必ずしも見えないのが困難の原因）
		     */
		    try {
			this.setMostRecentTransactionURI
			    (storage.getMostRecentTransactionURI()) ;
		    } catch(StorageException ex) {
			throw new SAXException(ex) ;
		    }

			// XXX: if ( this.soapEnvelopePrinted )
			// XXX: { add SOAP-ENV prefix }

			super.setContentHandlerDelegator
				( new WFSHandler
				  ( super.getOutputStream() ,
				    super.getParser() ,
				    (this.isTopLevelHandler()
				     && super.getCurrentLevel() == 1) ,
				    this.authInfo ,
				    this.storage,
				    this.permissionManager ) ,
				  uri , localName , qName , attrs );

			return;
		}
	}

	public void  xEndDocument() throws SAXException
	{
	    /* !!! [06/08/20 22:50 I.Noda] !!! 
	     * DOM による Response 出力への切り替えへの布石。
	     */
	    if(! Itk.useDOMResponse) {
		if ( this.soapEnvelopePrinted )
		{
			PrintWriter	out = super.getPrintWriter();

			out.println( "  </SOAP-ENV:Body>" );
			out.println( "</SOAP-ENV:Envelope>" );

			out.flush();
		}

	    } else {
		try {
		    PrintWriter out = super.getPrintWriter();
		    ResponseInfo response = this.getResponse() ;

		    if(response.headerIsOutputedP) {
			out.print(response.tailerStr) ;
		    } else {
			/* !!! [06/08/20 22:50 I.Noda] !!! 
			 * 本来なら XMLFormatConverter を使うべきだが、
			 * OutputStream と Writer の不整合により、
			 * うまく渡せない。
			 */
			//XMLFormatConverter.print(response.document, out);
			String resStr = 
			    XMLFormatConverter.toString(response.document) ;
			out.print(resStr) ;
		    }
		    out.flush() ;
		} catch(TransformerException ex) {
		    throw new SAXException(ex) ;
		}
	    }
	}


	public	void	xFatalError( SAXParseException  exception )
						throws SAXException
	{
		this.printError( exception );
	}

	public	void	xError( SAXParseException  exception )
						throws SAXException
	{
		this.printError( exception );
	}

	public	void	xWarning( SAXParseException exception )
						throws SAXException
	{
		this.printError( exception );
	}


	protected  void	printError( 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 );
		}
	}
}
