package daruma.storage_manager.type_definition;

import daruma.storage_manager.type_definition.TypeDefinition;
import daruma.storage_manager.type_definition.TypeException;
import daruma.storage_manager.type_definition.ColumnNameFactory;

import daruma.geometry.TransformationContext;

import daruma.sql.TableColumn;
import daruma.sql.TableColumnDefinition;
import daruma.sql.SQLDataType;
import daruma.sql.DatabaseConnection;
import daruma.sql.DatabaseConnectionException;
import daruma.sql.DatabaseCalendarConfig;
import java.sql.SQLException;
import java.sql.Timestamp;

import daruma.storage_manager.StorageManager;

import daruma.xml.util.DOMDocumentFactory;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import javax.xml.parsers.ParserConfigurationException;

import daruma.util.ISO8601DateFormat;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;

import daruma.util.LogWriter;
import daruma.util.CharacerCodeConstant;
import java.io.UnsupportedEncodingException;


public class TypedInstance
{
	private	List<TableColumn>	columns;
	private	TypeDefinition		type;
	private	long			id;

	public	TypedInstance( List<TableColumn>  columns ,
			       TypeDefinition  type )
	{
		this.columns = columns;
		this.type    = type;
		this.id      = -1;
	}

	public	TypedInstance( TypeDefinition  type ,
			       DatabaseConnection.QueryResult  queryResult ,
			       StorageManager  storage ) throws TypeException
	{
		this.type    = type;
		this.columns = new ArrayList<TableColumn>();

		SQLDataType	sqlDataType= this.type.getSingleSQLDataType();
		if ( sqlDataType != null )
		{
			String	value = null;

			try
			{
				value = this.internalGetValueFromResultSet
					( queryResult ,
					  ColumnNameFactory
					  .getTopLevelElementColumnName() ,
					  sqlDataType );
			}
			catch( TypeException  e )
			{
				throw e;
			}

			this.columns.add( new TableColumn
					  ( new TableColumnDefinition
					    ( ColumnNameFactory
					      .getTopLevelElementColumnName() ,
					      sqlDataType ) ,
					    value ) );

			return;
		}


		List<TableColumnDefinition>	columnDefinition;
		columnDefinition = this.type.getCompositeSQLDataType( storage ,
								      null,
								      null );

		if ( columnDefinition == null )
		{
			throw new TypeException
				  ( "GetFeature: single type"
				    + " not implemented yet" );
		}

		for( TableColumnDefinition  c : columnDefinition )
		{
			String	value;

			try
			{
				value = this.internalGetValueFromResultSet
					( queryResult ,
					  c.getColumnName() ,
					  c.getSQLDataType() );
			}
			catch( TypeException  e )
			{
				throw e;
			}

			this.columns.add( new TableColumn( c , value ) );
		}
	}


	private	String	internalGetValueFromResultSet
				( DatabaseConnection.QueryResult  queryResult ,
				  String  columnName ,
				  SQLDataType  type ) throws TypeException
	{
		String	realColumnName = type.getSQLRetrieveColumnString
								( columnName );


		try
		{
			this.id = queryResult.getLong( "_id_" );

			if ( Date.class.isAssignableFrom
					     ( type.getJavaClass() ) )
			{
				Timestamp  t = queryResult.getTimestamp
						( realColumnName,
						  DatabaseCalendarConfig
						  .getCalendar() );

				ISO8601DateFormat  f = new ISO8601DateFormat();

				return( f.format( t ) );
			}
			else
			{
				// XXX: get*() should be choosed by sqlDataType

				return( queryResult.getString
					( realColumnName ) );
			}
		}
		catch( DatabaseConnectionException  e )
		{
			throw new TypeException( e );
		}
	}


	public	List<TableColumn>	getColumns()
	{
		return( this.columns );
	}

	public	TypeDefinition	getType()
	{
		return( this.type );
	}


	public	Node	convertToXMLElement( ElementName  elementName ,
					     StorageManager  storage ,
					     TransformationContext  trans )
							  throws TypeException
	{
		LogWriter.qwrite( "DEBUG",
				  this.getClass().getName(),
				  ".convertToXMLElement()" );

		Document	doc;

		try
		{
			doc = DOMDocumentFactory.create();
		}
		catch( ParserConfigurationException  e )
		{
			throw new TypeException( e );
		}

		DocumentFragment	frag = doc.createDocumentFragment();

		Element	e = doc.createElementNS
				( elementName.getNamespace() ,
				  elementName.getLocalName() );

		this.type.convertToXMLElement( e , doc , storage , trans ,
					       this.getColumns() , 0 ,
					       id );
		return( e );
	}
}
