package daruma.wfs;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.SocketException;

import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import daruma.util.CloseIgnoreInputStream;
import daruma.util.CountOutputStream;
import daruma.util.FatalException;
import daruma.util.LogWriter;

import daruma.wfs.SOAPHandler;
import daruma.xml.handler.MispDefaultHandler;
import daruma.xml.util.EndOfDocumentException;
import daruma.xml.util.XMLFormatConverter;
import daruma.storage_manager.StorageManager;
import daruma.auth.PermissionManager;
import daruma.auth.AuthenticationInfo;

import daruma.util.Itk;


public class SOAPWFSProcessor
{
	private	String	debugTag = "";

	public	void	setDebugTag( String  debugTag )
	{
		this.debugTag = debugTag;
	}


	public	void	process( InputStream  in ,  OutputStream  out ,
				 AuthenticationInfo  authInfo ,
				 StorageManager  storage,
				 PermissionManager permissionManager )
					throws daruma.util.FatalException,
					       SocketException
	{
		LogWriter.qwrite( "DEBUG", "debugTag = [", this.debugTag, "]" );

		CountOutputStream  cOut = new CountOutputStream( out );


		//
		// create parser
		//
		SAXParserFactory	f = SAXParserFactory.newInstance();
		f.setNamespaceAware( true );

		XMLReader		parser;
		MispDefaultHandler	h;

		try
		{
			parser = f.newSAXParser().getXMLReader();

			h = new SOAPHandler( cOut, parser, true,
					     authInfo, storage, permissionManager );

			h.setThrowEODWithEndOfTopLevelElement( true );

			/*
			h.setDebugFlag( true );
			*/

			parser.setContentHandler( h );
			parser.setErrorHandler( h );
		}
		catch( ParserConfigurationException  e )
		{
			throw new FatalException
				  ( "Can't create SAX parser" , e );
		}
		catch( SAXException  e )
		{
			throw new FatalException
				  ( "Can't create SAX parser" , e );
		}


		//
		// parse
		//
		try
		{
			PrintWriter	w = new PrintWriter( cOut );

			LogWriter.qwrite( "DEBUG", "parse start" );

			try
			{
				parser.parse
				  ( new InputSource
				    ( new CloseIgnoreInputStream( in ) ) );
			}
			catch( EndOfDocumentException  e )
			{
				LogWriter.qwrite( "DEBUG",  "end of document" );
			}

			LogWriter.qwrite( "DEBUG",  "parse succeeded" );

			w.flush();
			cOut.flush();
		}
		catch( SAXParseException  e )
		{
			LogWriter.qwrite( "DEBUG", "failed: ", e );

			sendSOAPError( cOut , e , h.getResponse() ,
				       (cOut.getByteCount() == 0) );

			notifyParseError( e.getMessage() );
		}
		catch( SAXException  e )
		{
			LogWriter.qwrite( "DEBUG", "failed: ", e );

			sendSOAPError( cOut ,
				       new SAXParseException
					   ( e.getMessage() ,
					     h.getLocator() ,
					     e ) ,
				       h.getResponse() ,
				       (cOut.getByteCount() == 0) );

			notifyParseError( e.getMessage() );
		}
		catch( SocketException e )
		{
			LogWriter.qwrite( "DEBUG", "failed: ", e );
			throw e;
		}
		catch( IOException  e )
		{
			LogWriter.qwrite( "DEBUG", "failed: ", e );
			e.printStackTrace(System.err);
		}
		catch( NullPointerException  e )
		{
			LogWriter.qwrite( "ERROR", "internal error",
					  e.getMessage(), e );
			e.printStackTrace();

			sendSOAPError( cOut , e , h.getResponse() ,
				       (cOut.getByteCount() == 0) );

			notifyParseError( e.getMessage() );
		}
		catch( ClassCastException  e )
		{
			LogWriter.qwrite( "ERROR", "internal error",
					  e.getMessage(), e );
			e.printStackTrace();

			sendSOAPError( cOut , e , h.getResponse() ,
				       (cOut.getByteCount() == 0) );

			notifyParseError( e.getMessage() );
		}
		catch( Exception  e )
		{
			LogWriter.qwrite( "DEBUG",  "failed: ", e );
			e.printStackTrace();

			sendSOAPError( cOut , e , h.getResponse() ,
				       (cOut.getByteCount() == 0) );

			notifyParseError( e.getMessage() );
		}
	}


	private	static	void	notifyParseError( String  errorMessage )
	{
		LogWriter.qwrite("INFO", "xml parse error: ", errorMessage);
	}


	// XXX: tentative method, should send error document in SAX handler
	private	static	void	sendSOAPError( OutputStream  out ,
					       Exception  exception ,
					       ResponseInfo  response ,
					       boolean  forceWrite )
    {
	PrintWriter w = new PrintWriter( out );

	try
	{
	    if ( forceWrite
	      || ! Itk.useDOMResponse )
	    {
		if ( response != null )
		{
		    XMLFormatConverter.print( response.document , out );
		}
		else
		{
		    XMLFormatConverter
			.print( new SOAPFaultDocumentBuilder( exception )
				    .newDocument() ,
				out );
		}
	    }
	    else
	    {
		if(response != null
		   && response.headerIsOutputedP)
		{
		    w.print(response.tailerStr);
		}
	    }
	}
	catch( TransformerException  e )
	{
	    e.printStackTrace();
	}
	catch( ParserConfigurationException  e )
	{
	    e.printStackTrace();
	}

	try
	{
	    w.flush();
	    out.flush();
	}
	catch( IOException  e )
	{
	    e.printStackTrace();
	}
    }
}
