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 daruma.auth.PermissionManager;
import daruma.auth.AuthenticationInfo;

import daruma.xml.XMLTag;
import daruma.xml.URI;
import daruma.xml.handler.MispDefaultHandler;
import daruma.xml.util.XMLFormatConverter;

import daruma.wfs.GetCapabilitiesHandler;
import daruma.wfs.TransactionHandler;
import daruma.wfs.GetFeatureHandler;
import daruma.wfs.DescribeFeatureTypeHandler;
import daruma.wfs.RegisterFeatureTypeHandler;
import daruma.wfs.RegisterCoordinateSystemTransformationHandler;
import daruma.wfs.ManageAuthHandler;
import daruma.wfs.GetCoordinateSystemTransformationHandler;
import daruma.wfs.SOAPFaultDocumentBuilder;

import daruma.storage_manager.StorageManager;
import daruma.storage_manager.StorageException;

import daruma.util.PropertyReader;


public class WFSHandler extends MispDefaultHandler
{
	private	StorageManager		storage;
	private	PermissionManager	permissionManager;
	private AuthenticationInfo	authInfo;
	private boolean			isTopLevelHandler;


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

		this.authInfo = authInfo;
		this.storage = storage;
		this.permissionManager = permissionManager;
		this.isTopLevelHandler = isTopLevelHandler;
	}


	public	void	xStartElement( String uri ,
				       String localName ,
				       String qName ,
				       Attributes  attrs ) throws SAXException
	{
		assert super.getCurrentLevel() == 1;
		// level should be 1, because this handler do delegation only.


		XMLTag	t = new XMLTag( uri , localName );

		final SAXParseException	mispNamespaceException
					 = new SAXParseException
					       ( "top level "
						 + t.getLocalName() + " tag "
						 + "should be in namespace "
						 + URI.MISP + ".",
						 super.getLocator() );
		final SAXParseException	readOnlyExceprion
					 = new SAXParseException
					       ( "server is running"
						 + " in read only mode, "
						 + t.getLocalName() + " "
						 + "operation is"
						 + " not permitted"
						 + " in read only mode." ,
						 super.getLocator() );

		final boolean readOnly = (! PropertyReader.getProperty
					    ( "daruma.run_mode" , "normal")
					    .equals( "normal" ));

		final boolean requireCertification
				       = (! PropertyReader.getProperty
					    ( "daruma.authentication_required",
					      "true")
					  .equals( "false" ));

		if ( requireCertification
		     && ! this.authInfo.certificated() )
		{
		    this.throwError
			 ( new SAXParseException
			   ( "authorization required, permission denied",
			     super.getLocator() ) );
		}

		if ( t.getLocalName().equals( "GetCapabilities" ) )
		{
			try
			{
				this.storage.setReadOnly( true );
			}
			catch( StorageException e )
			{
				throw new SAXException( e.getMessage() , e );
			}

			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.throwError( mispNamespaceException );
			}

			super.setContentHandlerDelegator
				( new GetCapabilitiesHandler
				  ( super.getOutputStream() ,
				    super.getParser() ,
				    this.isTopLevelHandler ,
				    this.storage ) ,
				  uri , localName , qName , attrs );

			return;
		}
		else if ( t.getLocalName().equals( "RegisterFeatureType" ) )
		{
			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.throwError( mispNamespaceException );
			}

			if ( readOnly )
			{
				this.throwError( readOnlyExceprion );
			}

			super.setContentHandlerDelegator
				( new RegisterFeatureTypeHandler
				  ( super.getOutputStream() ,
				    super.getParser() ,
				    this.isTopLevelHandler ,
				    this.storage ) ,
				  uri , localName , qName , attrs );

			return;
		}
		else if ( t.getLocalName()
			  .equals( "RegisterCoordinateSystemTransformation" ) )
		{
			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.throwError( mispNamespaceException );
			}

			if ( readOnly )
			{
				this.throwError( readOnlyExceprion );
			}

			super.setContentHandlerDelegator
				( new RegisterCoordinateSystemTransformationHandler
				  ( super.getOutputStream() ,
				    super.getParser() ,
				    this.isTopLevelHandler ,
				    this.storage ) ,
				  uri , localName , qName , attrs );

			return;
		}
		else if ( t.getLocalName().equals( "ManageAuth" ) )
		{
			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.throwError( mispNamespaceException );
			}

			if ( readOnly )
			{
				this.throwError( readOnlyExceprion );
			}

			super.setContentHandlerDelegator
				( new ManageAuthHandler
				  ( super.getOutputStream() ,
				    super.getParser() ,
				    this.isTopLevelHandler ,
				    this.storage,
				    this.permissionManager ) ,
				  uri , localName , qName , attrs );

			return;
		}
		else if ( t.getLocalName()
			  .equals( "GetCoordinateSystemTransformation" ) )
		{
			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.throwError( mispNamespaceException );
			}

			if ( readOnly )
			{
				this.throwError( readOnlyExceprion );
			}

			super.setContentHandlerDelegator
				( new GetCoordinateSystemTransformationHandler
				  ( super.getOutputStream() ,
				    super.getParser() ,
				    this.isTopLevelHandler ,
				    this.storage ) ,
				  uri , localName , qName , attrs );

			return;
		}
		else if ( t.getLocalName().equals( "Transaction" ) )
		{
			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.throwError( mispNamespaceException );
			}

			if ( readOnly )
			{
				this.throwError( readOnlyExceprion );
			}

			super.setContentHandlerDelegator
				( new TransactionHandler
				  ( super.getOutputStream() ,
				    super.getParser() ,
				    this.isTopLevelHandler ,
				    this.storage ) ,
				  uri , localName , qName , attrs );

			return;
		}
		else if ( t.getLocalName().equals( "GetFeature" ) )
		{
			try
			{
				this.storage.setReadOnly( true );
			}
			catch( StorageException e )
			{
				throw new SAXException( e.getMessage() , e );
			}

			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.printError( mispNamespaceException );

				throw mispNamespaceException;
			}

			super.setContentHandlerDelegator
				( new GetFeatureHandler
				  ( super.getOutputStream() ,
				    super.getParser() ,
				    this.isTopLevelHandler ,
				    this.storage,
				    this.permissionManager,
				    this.authInfo ) ,
				  uri , localName , qName , attrs );

			return;
		}
		else if ( t.getLocalName().equals( "DescribeFeatureType" ) )
		{
			try
			{
				this.storage.setReadOnly( true );
			}
			catch( StorageException e )
			{
				throw new SAXException( e.getMessage() , e );
			}

			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.printError( mispNamespaceException );

				throw mispNamespaceException;
			}

			super.setContentHandlerDelegator
				( new DescribeFeatureTypeHandler
				  ( super.getOutputStream() ,
				    super.getParser() ,
				    this.isTopLevelHandler ,
				    this.storage ) ,
				  uri , localName , qName , attrs );

			return;
		}
		else
		{
			SAXParseException	e = new SAXParseException
						    ( "unknown top level tag "
						      + "\"" + t.getLocalName()
						      + "\"." ,
						      super.getLocator() );

			this.printError( e );

			throw e;
		}
	}


	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	throwError( SAXParseException e ) throws SAXException
	{
		this.printError( e );
		throw e;
	}

	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 );
		}
	}
}
