package daruma.wfs.transaction;

import daruma.storage_manager.StorageManager;
import daruma.storage_manager.StorageException;
import daruma.storage_manager.BulkInsertBuffer;
import daruma.storage_manager.type_definition.ElementName;
import daruma.storage_manager.type_definition.TypeName;
import daruma.storage_manager.type_definition.TypeDefinition;
import daruma.storage_manager.type_definition.TypedInstance;
import daruma.storage_manager.type_definition.TypeException;

import daruma.wfs.TransactionHandler;
import daruma.wfs.TransactionResultInfo ;

import daruma.xml.SimpleXPath;
import daruma.xml.handler.XSAXDOMCreateHandler;
import daruma.xml.util.ElementUtil;
import daruma.util.Pair;
import daruma.util.LogWriter;

import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.w3c.dom.Element;

import java.io.OutputStream;
import java.util.List;

public class InsertAnElementHandler extends XSAXDOMCreateHandler
{
	private	StorageManager		storage;
	private	BulkInsertBuffer	insertBuffer;
	private	TransactionHandler	errorReceiver;

	private TransactionResultInfo resultInfo ;

	public	InsertAnElementHandler
			( OutputStream  out ,
			  XMLReader  parser ,
			  boolean  isTopLevelHandler ,
			  StorageManager  storage ,
			  BulkInsertBuffer buffer ,
			  TransactionHandler  errorReceiver,
			  TransactionResultInfo resultInfo)
	{
		super( out , parser , isTopLevelHandler );
		super.setInhibitEndPrefixBeforeEndDocument( true );

		this.storage       = storage;
		this.insertBuffer  = buffer;
		this.errorReceiver = errorReceiver;
		this.resultInfo    = resultInfo;
	}

	public  void  xEndDocument() throws SAXException
	{
		Element	element = super.getDocumentElement();

		LogWriter.qwrite("DEBUG", "data = [", element, "]") ;

		ElementName	elementName = new ElementName
						  ( element.getNamespaceURI() ,
						    element.getLocalName() );

		TypeName	typeName = null;

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

		LogWriter.qwrite("DEBUG",  "typeName = [", typeName, "]" );

		if ( typeName == null )
		{
			this.throwError( new SAXParseException
					 ( "type of element "
					   + elementName.getLocalName()
					   + " in namespace "
					   + elementName.getNamespace()
					   + " not found" ,
					   super.getLocator() ) );
		}

		TypeDefinition	type = null;

		try
		{
			type = storage.getTypeDefinition( typeName );
		}
		catch( StorageException  e )
		{
			this.throwError( new SAXParseException
					     ( e.getMessage() ,
					       super.getLocator() ,
					       e ) );
		}

		if ( type == null )
		{
			this.throwError( new SAXParseException
					 ( "type definition of "
					   + elementName.getLocalName()
					   + " in namespace "
					   + elementName.getNamespace()
					   + " not found" ,
					   super.getLocator() ) );
		}


		TypedInstance	obj = null;

		try
		{
			Pair<TypedInstance, Integer>
				r = type.createInstance( element ,
							 elementName ,
							 (SimpleXPath)null ,
							 this.storage ,
							 0 );
			obj = r.getFirst();

			if ( ElementUtil.hasExtraChildElements
			     ( element , r.getSecond() ) )
			{
				List<Element> ex = ElementUtil
						   .getExtraChildElements
						    ( element ,
						      r.getSecond() );

				for( Element e : ex )
				{
					ElementUtil.debugPrint( e );
				}

				throw new TypeException
					  ( "too many child elements of "
					    + new ElementName( element )
								.toString() );
			}
		}
		catch( TypeException  e )
		{
			this.throwError( new SAXParseException
					 ( "invalid element " + elementName
					   + ": " + e.getMessage() ,
					   super.getLocator() ) );
		}


		// XXX
		if ( obj == null )
		{
			this.throwError( new SAXParseException
					 ( "invalid element "
					   + elementName.getLocalName()
					   + " in namespace "
					   + elementName.getNamespace() ,
					   super.getLocator() ) );
		}


		try
		{
			this.insertBuffer.insertElement( elementName , obj );

			long nElements = this.insertBuffer.size();

			// XXX: magic number 100
			//      should give by option or config file
			if ( nElements >= 100 )
			{
			    this.storage.bulkInsertElement
				( this.insertBuffer,
				  super.getTransactionSN() );

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


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

		throw e;
	}
}
