package daruma.wfs.filter;

import daruma.xml.QName;
import daruma.xml.UniversalName;
import daruma.xml.SimpleXPath;
import daruma.xml.util.NodeUtil;
import daruma.xml.util.ElementUtil;

import daruma.xml.util.XMLParseErrorException;
import org.w3c.dom.Node;
import org.w3c.dom.Element;

import daruma.storage_manager.StorageManager;
import daruma.storage_manager.StorageException;
import daruma.storage_manager.type_definition.ColumnNameFactory;


public class PropertyPathConverter
{
	/* !!! [06/08/17 17:28 I.Noda] !!! 
	 * XPath の特殊文字の宣言
	 */
	public static final String XPathSep = "/" ;
	public static final String XPathAttributePrefix = "@" ;
	public static final String XPathSystemColumnNamePrefix = "_" ;
	public static final String XPathSystemColumnPrefix
				     = XPathAttributePrefix
					+ XPathSystemColumnNamePrefix ;

	private	PropertyPathConverter()
	{
	}

	public	static	String  convertPropertyElementToShortXPath
					( Element  element ,
					  StorageManager  storage )
						throws XMLParseErrorException
	{
		String	propertyNameString = ElementUtil.getChildNodesWholeText
						      ( element );

		return( PropertyPathConverter.convertPropertyPathToShortXPath
			( propertyNameString , element , storage ) );
	}

	public	static	String  convertPropertyPathToShortXPath
					( String  propertyPathString ,
					  Node  node ,
					  StorageManager  storage )
						throws XMLParseErrorException
	{
		/* <<< [06/08/17 17:28 I.Noda] <<< 
		 * path が "@_" で始まる場合は、システム属性と判断。
		 * できればもうちょっとエレガントな方法で解決したい。
		 */
		if(propertyPathString.startsWith(XPathSystemColumnPrefix)) {
			return propertyPathString
				.substring(XPathAttributePrefix.length()) ;
		}
		/* >>> [06/08/17 17:28 I.Noda] >>> */


		String[]	propertyPathList
				= propertyPathString.trim().split( XPathSep );

		if ( propertyPathList.length == 0 )
		{
			throw new XMLParseErrorException
				( "empty property path "
				  + "\"" + propertyPathString + "\"" );
		}


		SimpleXPath	path = new SimpleXPath();
		boolean		addSrsName = false;

		for ( String  p : propertyPathList )
		{
			boolean	isAttribute = false;

			if ( p.startsWith( PropertyPathConverter
					   .XPathAttributePrefix ) )
			{
				isAttribute = true;

				p = p.substring
				    ( PropertyPathConverter
				      .XPathAttributePrefix.length() );
			}

			UniversalName	pathEntry
					= PropertyPathConverter
					  .convertQNameStringToUniversalName
								( p , node );

			if ( isAttribute )
			{
				// XXX: should check element type
				if ( pathEntry.equals( new UniversalName
						       ( null , "srsName" ) ) )
				{
					addSrsName = true;
				}
				else
				{
					path.addAttribute( pathEntry );
				}
			}
			else
			{
				path.add( pathEntry );
			}
		}

		String	columnName;
		try
		{
			columnName = ColumnNameFactory
					.createColumnName( path , storage );

			// XXX: should not use concrete class here
			//      should use abstract method of TypeDefinition.
			if ( addSrsName )
			{
				columnName
				    = ColumnNameFactory
				       .getColumnName
					( columnName ,
					  daruma.storage_manager
					  .type_definition.types
					  .GeometryPropertyTypeDefinition
					  .ColNameGeoSrsNameSuffix );
			}
		}
		catch( StorageException  e )
		{
			throw new XMLParseErrorException( e.getMessage() , e );
		}

		return( columnName );
	}

	/*
	private	static	String  convertPropertyToID
					( String  propertyNameString ,
					  Node  node ,
					  StorageManager  storage )
						throws XMLParseErrorException
	{
		boolean	isAttribute = false;

		if ( propertyNameString.startsWith( PropertyPathConverter
						    .XPathAttributePrefix ) )
		{
			isAttribute = true;

			propertyNameString
				= propertyNameString.substring
				  ( PropertyPathConverter
				    .XPathAttributePrefix.length() );
		}


		UniversalName	propertyName;

		try
		{
			propertyName = PropertyPathConverter
					.convertQNameStringToUniversalName
							( propertyNameString ,
							  node );
		}
		catch( XMLParseErrorException  e )
		{
			throw e;
		}

		String	propertyID;
		try
		{
			propertyID = storage
				      .getUniversalNameID( propertyName );

			if ( propertyID == null )
			{
				throw new XMLParseErrorException
				  ( "property entry \"" + propertyName + "\""
				    + " not found" );
			}
		}
		catch( StorageException  e )
		{
			throw new XMLParseErrorException( e.getMessage() , e );
		}

		if ( isAttribute )
		{
			propertyID = PropertyPathConverter.XPathAttributePrefix
					+ propertyID;

		}

		return( propertyID );
	}
	*/

	protected static UniversalName  convertQNameStringToUniversalName
							( String  qNameString ,
							  Node  node )
						throws XMLParseErrorException
	{
		QName	qName = new QName( qNameString );

		if ( ! qName.isValid() )
		{
			throw new XMLParseErrorException
				( "invalid QName [" + qNameString + "]" );
		}

		String	ns = NodeUtil.lookupNamespaceURI( node ,
							  qName.getPrefix() );

		if ( qName.getPrefix() != null && ns == null )
		{
			throw new XMLParseErrorException
				( "unbounded prefix "
				  + "[" + qName.getPrefixForPrint() + "]"
				  + " of QName [" + qNameString + "]" );
		}

		return( new UniversalName( ns , qName.getLocalName() ) );
	}
}
