package daruma.storage_manager.type_definition.types;

import daruma.storage_manager.type_definition.AbstractCompositorTypeDefinition;
import daruma.storage_manager.type_definition.TypeDefinition;
import daruma.storage_manager.type_definition.TypeName;
import daruma.storage_manager.type_definition.ElementName;
import daruma.storage_manager.type_definition.TypedInstance;
import daruma.storage_manager.type_definition.TypeException;
import daruma.storage_manager.type_definition.XMLSchemaElementDefinition;
import daruma.storage_manager.schema.AnnotationElementChecker;
import daruma.storage_manager.StorageManager;
import daruma.storage_manager.StorageException;

import daruma.sql.TableColumn;
import daruma.sql.TableColumnDefinition;
import daruma.xml.UniversalName;
import daruma.xml.SimpleXPath;
import daruma.util.*;
import daruma.util.Pair;
import daruma.util.LogWriter;


import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;

import java.util.List;
import java.util.ArrayList;

// XXX: for debug only
import daruma.xml.util.ElementUtil;


public class SequenceTypeDefinition extends AbstractCompositorTypeDefinition
{
	public	SequenceTypeDefinition()
	{
		super();
	}

	public	Pair<TypedInstance, Integer>
				createInstance( Element  element ,
						ElementName  topLevelElement ,
						SimpleXPath  path ,
						StorageManager  storage ,
						int  elementIndex )
							  throws TypeException
	{
		LogWriter.qwrite("DEBUG", "");
		LogWriter.qwrite("DEBUG", "");
		LogWriter.qwrite("DEBUG",  this.getClass().getName(),
				 ": createInstance()" );
		LogWriter.qwrite("DEBUG",  "elementIndex = ", elementIndex );
		LogWriter.qwrite("DEBUG",  "--" );
		super.debugPrint();
		LogWriter.qwrite("DEBUG",  "--" );
		LogWriter.qwrite("DEBUG", element);


		List<TableColumn>  columnList = new ArrayList<TableColumn>();

		NodeList	childNodes   = element.getChildNodes();
		int		nThChildNode = elementIndex;

		for( Entry  entry  :  super.getEntries() )
		{
			//
			// debug print
			//
			String	debugString = "next entry = ";

			if ( entry.isCompositeEntry() )
			{
				debugString += "composite, "
					       + entry.getCompositeEntry()
							.getClass().getName();
			}
			else
			{
				debugString += "atomic";
			}
			LogWriter.qwrite("DEBUG",  debugString );



			//
			// if composite entry
			//
			if ( entry.isCompositeEntry() )
			{
				Pair<TypedInstance, Integer>
				  r = entry.getCompositeEntry().createInstance
				      ( element ,
					topLevelElement ,
					path ,
					storage ,
					nThChildNode );

				columnList.addAll( r.getFirst().getColumns() );

				LogWriter.qwrite("DEBUG",
						 "nThChildNode updated from ",
						 nThChildNode, " to ",
						 r.getSecond() );

				nThChildNode = r.getSecond();

				continue;
			}


			//
			// else if single entry
			//
			Node	n = null;

			XMLSchemaElementDefinition	elementDefinition
							= entry.getAtomEntry();

			ElementName	expectedElementName
					= elementDefinition.getElementName();

			long		minOccurs = elementDefinition
							   .getMinOccurs();

			Element		childElement = null ;
			ElementName	childElementName = null;
			for(;;)
			{
				if ( nThChildNode >= childNodes.getLength() )
				{
					LogWriter.qwrite("DEBUG",
							 "child index = ",
							 nThChildNode,
							 ", number of child nodes = ",
							 childNodes.getLength() );

					if ( minOccurs == 0 )
					{
						break;
					}
					else
					{
						throw new TypeException
						 ( "too few child elements of "
						   + new ElementName( element )
								 .toString() );
					}
				}

				n = childNodes.item( nThChildNode );

				if ( n instanceof Element )
				{
					if ( ! AnnotationElementChecker
					       .isXMLSchemaAnnotationElement
							     ( (Element)n ) )
					{
						childElement = (Element)n;

						childElementName
							= new ElementName
							     ( childElement );

						break;
					}
				}

				nThChildNode ++;
			}



			LogWriter.qwrite("DEBUG",
					 "tag[", nThChildNode, "] = [",
					 ((childElementName == null)? "null" : childElementName),
					 "]" );
			LogWriter.qwrite("DEBUG",
					 "expected element = [",
					 expectedElementName,
					 "], minOccurs = ", minOccurs );

			boolean	dataExists = true;

			if ( childElementName == null
			  || ! childElementName.equals( expectedElementName ) )
			{
				LogWriter.qwrite("DEBUG",  "unmached" );

				if ( minOccurs != 0 )
				{
					throw new TypeException
					  ( "unexpected element \""
					    + childElementName
					    + "\", expected was "
					    + expectedElementName );
				}

				dataExists = false;
			}


			TypeDefinition	type = elementDefinition.getType();


			SimpleXPath	p = new SimpleXPath( path );

			if ( path != null )
			{
				p.add( new UniversalName( element ) );
			}

			if ( dataExists )
			{
				TypedInstance	obj = type.createInstance
							   ( childElement ,
							     topLevelElement ,
							     p ,
							     storage ,
							     0 ).getFirst();
				columnList.addAll( obj.getColumns() );
			}
			else
			{
				p.add( expectedElementName );

				List<TableColumnDefinition>
				  columnDefs = type.getCompositeSQLDataType
						      ( storage , p , null );

				if ( columnDefs == null )
				{
					String columnName;

					try
					{
						columnName
						  = storage
						    .getShortXPathStringForDB
						     ( p );
					}
					catch( StorageException e )
					{
						throw new TypeException
						    ( e.getMessage(), e );
					}

					TableColumn	tc;
					tc = new TableColumn
					    ( new TableColumnDefinition
					      ( columnName,
						type.getSingleSQLDataType() ) ,
					      null );

					tc.setIsEmptyColumn( true );

					columnList.add( tc );
				}
				else
				{
					for ( TableColumnDefinition  c
						      : columnDefs )
					{
						TableColumn	tc;
						tc = new TableColumn
							( c , null );

						tc.setIsEmptyColumn( true );

						columnList.add( tc );
					}
				}
			}


			if ( dataExists )
			{
				nThChildNode ++;
			}

			LogWriter.qwrite("DEBUG", "");
			LogWriter.qwrite("DEBUG", "");
		}

		return( new Pair<TypedInstance, Integer>
			( new TypedInstance( columnList , this ) ,
			  nThChildNode ) );
	}
}
