package daruma.wfs;

/* !!! [06/08/22 18:56 I.Noda] !!! 
 * TransactionResultInfo を導入。
 */
import daruma.wfs.TransactionResultInfo ;

import daruma.wfs.filter.FilterHandler;

import daruma.xml.handler.MispDefaultHandler;
import daruma.xml.SAXExceptionObserver;
import daruma.xml.XMLTag;
import daruma.xml.URI;

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 daruma.storage_manager.type_definition.TypeDefinition;

import daruma.geometry.TransformationContext;

import org.xml.sax.XMLReader;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import java.io.OutputStream;


public class TransactionDeleteHandler 
    /* !!! [06/08/18 23:19 I.Noda] !!! 
     * MISPDefaultHandler の導入
     */
    extends MispDefaultHandler
    //extends XSAXDefaultHandler
    implements SAXExceptionObserver
{
	private	enum DeleteMode { PRESERVE , OVERRIDE , RESTORE };

	private	StorageManager		storage;
	private	SAXExceptionObserver	errorReceiver;

	/* !!! [06/08/22 18:56 I.Noda] !!! 
	 * TransactionResultInfo を導入。
	 */
	private TransactionResultInfo resultInfo ;

	private	ElementName		specifiedElement;
	private	TypeDefinition		specifiedElementType;
	private	DeleteMode		deleteMode;
	private	FilterHandler		filter;
	private	boolean			filterTagFound;


	public	TransactionDeleteHandler( OutputStream  out ,
					  XMLReader  parser ,
					  boolean  isTopLevelHandler ,
					  StorageManager  storage ,
					  SAXExceptionObserver  errorReceiver,
					  /* !!! [06/08/22 18:56 I.Noda] !!! 
					   * TransactionResultInfo を導入。
					   */
					  TransactionResultInfo resultInfo)
	{
		super( out , parser , isTopLevelHandler );

		this.storage              = storage;
		this.errorReceiver        = errorReceiver;
		this.specifiedElement     = null;
		this.specifiedElementType = null;
		this.deleteMode           = DeleteMode.PRESERVE;
		this.filter               = null;
		this.filterTagFound       = false;

		/* !!! [06/08/22 18:56 I.Noda] !!! 
		 * TransactionResultInfo を導入。
		 */
		this.resultInfo  = resultInfo ;
		if(this.resultInfo.countDelete < 0) {
		    this.resultInfo.countDelete = 0 ;
		}
	}

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

		if ( super.getCurrentLevel() == 1 )
		{
			if ( ! t.getURI().equals( URI.MISP ) )
			{
				this.throwError( mispNamespaceException );

				throw mispNamespaceException;
			}

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


			// XXX: should unify codes
			//      with Transaction*.java, GetFeatureHandler.java

			//
			// typeName
			//
			String	typeNameString = attrs.getValue
							( "" , "typeName" );

			if ( typeNameString == null )
			{
				this.throwError( new SAXParseException
						 ( "typeName attribute"
						   + " not found in "
						   + t.getLocalName()
						   + " tag." ,
						   super.getLocator() ) );
			}

			try
			{
				this.specifiedElement
				 = new ElementName
				   ( super.convertQNameStringToUniversalName
							  ( typeNameString ) );
			}
			catch( SAXException  e )
			{
				this.throwError( new SAXParseException
						 ( e.getMessage() ,
						   super.getLocator() ) );
			}


			TypeName	elementTypeName = null;

			try
			{
				elementTypeName
					= this.storage.getElementTypeName
							( specifiedElement );
			}
			catch( StorageException  e )
			{
				this.throwError
				     ( new SAXParseException
				       ( e.getMessage() ,
					 super.getLocator() ,
					 e ) );
			}

			if ( elementTypeName == null )
			{
				this.throwError
				     ( new SAXParseException
				       ( "element "
					 + specifiedElement.getLocalName()
					 + " in namespace "
					 + specifiedElement.getNamespace()
					 + " not registered" ,
					 super.getLocator() ) );
			}

			try
			{
				this.specifiedElementType
					= this.storage.getTypeDefinition
						      ( elementTypeName );
			}
			catch( StorageException  e )
			{
				this.throwError
				     ( new SAXParseException
				       ( "type of element "
					 + specifiedElement.getLocalName()
					 + " in namespace "
					 + specifiedElement.getNamespace()
					 + " not registered" ,
					 super.getLocator() ) );
			}



			//
			// delete mode
			//
			String	deleteModeString
				= attrs.getValue( "" , "mode" );

			if ( deleteModeString != null )
			{
				if ( deleteModeString.equals("preserve") )
				{
					this.deleteMode = DeleteMode.PRESERVE;
				}
				else if ( deleteModeString.equals("override") )
				{
					this.deleteMode = DeleteMode.OVERRIDE;
				}
				else if ( deleteModeString.equals("restore") )
				{
					this.deleteMode = DeleteMode.RESTORE;
				}
				else
				{
					this.throwError
					  ( new SAXParseException
					    ( "mode attribute of Delete"
					      + " must be one of ["
					      + "preserve|override|restore]" ,
					      super.getLocator() ) );
				}
			}

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

				throw mispNamespaceException;
			}

			if ( ! t.getLocalName().equals( "Filter" ) )
			{
				SAXParseException
					e = new SAXParseException
					    ( "unexpected tag \""
					      + t.getLocalName()
					      + "\" found, "
					      + "expected was Filter tag." ,
					      super.getLocator() );

				this.throwError( e );
			}

			if ( this.filterTagFound )
			{
				SAXParseException
					e = new SAXParseException
						( "Filter multiply specified" ,
						  super.getLocator() );

				this.throwError( e );
			}

			this.filterTagFound = true;

			this.filter = new FilterHandler
					  ( super.getOutputStream() ,
					    super.getParser() ,
					    false ,
					    this.specifiedElementType ,
					    new TransformationContext() ,
					    this.storage ,
					    this );

			super.setContentHandlerDelegator
				( this.filter ,
				  uri , localName , qName , attrs );

			return;
		}
	}

	public	void  xEndDocument() throws SAXException
	{
		if ( ! this.filterTagFound )
		{
			this.throwError( new SAXParseException
					 ( "Filter tag not found" ,
					   super.getLocator() ) );
		}


		try
		{
			int count = 0;

			if ( this.deleteMode == DeleteMode.PRESERVE )
			{
				count = this.storage.deleteElement
					( this.specifiedElement ,
					  this.filter.getPredicate() ,
					  this.getTransactionSN(),
					  true );
			}
			else if ( this.deleteMode == DeleteMode.OVERRIDE )
			{
				count = this.storage.deleteElement
					( this.specifiedElement ,
					  this.filter.getPredicate() ,
					  this.getTransactionSN(),
					  false );
			}
			else if ( this.deleteMode == DeleteMode.RESTORE )
			{
				count = this.storage.restoreElement
					( this.specifiedElement ,
					  this.filter.getPredicate(),
					  this.getTransactionSN()
					  );
			}

			this.resultInfo.countDelete += count;
		}
		catch( StorageException  e )
		{
			this.throwError( new SAXParseException
					 ( e.getMessage() ,
					   super.getLocator() ) );
		}
	}


	public	void	notifyError( SAXParseException  e ) throws SAXException
	{
		this.errorReceiver.notifyError( e );
	}

	public	void	throwError( SAXParseException  e ) throws SAXException
	{
		this.notifyError( e );

		throw e;
	}
}
