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.StorageManager;
import daruma.storage_manager.StorageException;
import daruma.sql.TableColumn;
import daruma.xml.UniversalName;
import daruma.xml.SimpleXPath;
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;


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

	@Override
	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",  "--" );


		ElementName	parentElementName = new ElementName( element );

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

		List<Element>	tagList = new ArrayList<Element>();

		for(;;)
		{
			if ( nThChildNode >= childNodes.getLength() )
			{
				throw new TypeException
					  ( "too few child elements of "
					    + parentElementName.toString() );
			}

			Node	n = childNodes.item( nThChildNode );

			if ( n instanceof Element )
			{
				// XXX: check tag equals <annotation>

				tagList.add( (Element)n );

				if ( tagList.size()
				     == this.getEntries().size() )
				{
					nThChildNode ++;
					break;
				}
			}

			nThChildNode ++;
		}


		for( Entry  entry  :  this.getEntries() )
		{
			ElementName	expectedElementName
					= entry.getAtomEntry()
						.getElementName();

			LogWriter.qwrite("DEBUG",
					 "[", expectedElementName, "]" );
		}

		for( Element  t  :  tagList )
		{
			ElementName	tag = new ElementName( t );

			LogWriter.qwrite("DEBUG",  "[[", tag, "]]" );
		}


		List<Element>	sortedTagList = new ArrayList<Element>();

		// XXX: should use Map?
		for( Entry  entry  :  this.getEntries() )
		{
			for( Element  t  :  tagList )
			{
				if ( entry.getAtomEntry().getElementName()
				     .equals( new ElementName( t ) ) )
				{
					sortedTagList.add( t );

					break;
				}
			}
		}

		if ( sortedTagList.size() != this.getEntries().size() )
		{
			throw new TypeException
				  ( "invalid child elements of "
				    + parentElementName.toString() );
		}


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

		for ( int  i = 0  ;  i < super.getEntries().size()  ;  ++ i )
		{
			Entry	entry = super.getEntries().get(i);

			assert ! entry.isCompositeEntry();

			ElementName	expectedElementName
					= entry.getAtomEntry()
						.getElementName();

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

			TypeName	typeName
					= entry.getAtomEntry().getTypeName();

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

			TypeDefinition	type;

			try
			{
				type = storage.getTypeDefinition( typeName );
			}
			catch( StorageException  se )
			{
				throw new TypeException( se );
			}

			LogWriter.qwrite("DEBUG",  "type.class = ",
					 type.getClass().getName() );


			SimpleXPath	p = new SimpleXPath( path );

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


			TypedInstance	obj = type.createInstance
						   ( sortedTagList.get(i) ,
						     topLevelElement ,
						     p ,
						     storage ,
						     0 ).getFirst();

			columnList.addAll( obj.getColumns() );
		}

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


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