/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 * 
 *    (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.filter.expression;

import java.util.regex.Pattern;

import org.geotools.factory.Hints;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;

import com.vividsolutions.jts.geom.Geometry;

/**
 * Creates a property accessor for simple features.
 * <p>
 * The created accessor handles a small subset of xpath expressions, a
 * non-nested "name" which corresponds to a feature attribute, and "@id",
 * corresponding to the feature id.
 * </p>
 * <p>
 * THe property accessor may be run against {@link SimpleFeature}, or 
 * against {@link SimpleFeature}. In the former case the feature property 
 * value is returned, in the latter the feature property type is returned. 
 * </p>
 * 
 * @author Justin Deoliveira, The Open Planning Project
 * 
 *
 *
 * @source $URL: http://svn.osgeo.org/geotools/branches/2.7.x/modules/library/main/src/main/java/org/geotools/filter/expression/SimpleFeaturePropertyAccessorFactory.java $
 */
public class SimpleFeaturePropertyAccessorFactory implements
        PropertyAccessorFactory {

    /** Single instnace is fine - we are not stateful */
    static PropertyAccessor ATTRIBUTE_ACCESS = new SimpleFeaturePropertyAccessor();
    static PropertyAccessor DEFAULT_GEOMETRY_ACCESS = new DefaultGeometrySimpleFeaturePropertyAccessor();
    static PropertyAccessor FID_ACCESS = new FidSimpleFeaturePropertyAccessor();
    static Pattern idPattern = Pattern.compile("@(\\w+:)?id");
    static Pattern propertyPattern = Pattern.compile("(\\w+:)?(\\w+)");

    public PropertyAccessor createPropertyAccessor(Class type, String xpath,
            Class target, Hints hints) {

    	if ( xpath == null ) 
    		return null;
    	
        if (!SimpleFeature.class.isAssignableFrom(type) && !SimpleFeatureType.class.isAssignableFrom(type))
            return null; // we only work with simple feature

        //if ("".equals(xpath) && target == Geometry.class)
        if ("".equals(xpath))
            return DEFAULT_GEOMETRY_ACCESS;

        //check for fid access
        if (idPattern.matcher(xpath).matches())
            return FID_ACCESS;

        //check for simple property acess
        if (propertyPattern.matcher(xpath).matches()) {
        	return ATTRIBUTE_ACCESS;	
        }
        
        return null;
    }

    /**
     * We strip off namespace prefix, we need new feature model to do this
     * property
     * <ul>
     * <li>BEFORE: foo:bar
     * <li>AFTER: bar
     * </ul>
     * 
     * @param xpath
     * @return xpath with any XML prefixes removed
     */
    static String stripPrefix(String xpath) {
        int split = xpath.indexOf(":");
        if (split != -1) {
            return xpath.substring(split + 1);
        }
        return xpath;
    }

    /**
     * Access to SimpleFeature Identifier.
     * 
     * @author Jody Garnett, Refractions Research Inc.
     */
    static class FidSimpleFeaturePropertyAccessor implements PropertyAccessor {        
        public boolean canHandle(Object object, String xpath, Class target) {
        	//we only work against feature, not feature type
            return object instanceof SimpleFeature && xpath.matches("@(\\w+:)?id");
        }
        public Object get(Object object, String xpath, Class target) {
            SimpleFeature feature = (SimpleFeature) object;
            return feature.getID();
        }
    }
    static class DefaultGeometrySimpleFeaturePropertyAccessor implements PropertyAccessor {
        
        public boolean canHandle(Object object, String xpath, Class target) {
        	if ( !"".equals( xpath ) )
        		return false;
        	
//        	if ( target != Geometry.class ) 
//        		return false;
        	
        	if ( !( object instanceof SimpleFeature || object instanceof SimpleFeatureType ) ) {
        		return false;
        	}
        	
        	return true;
            
        }
        public Object get(Object object, String xpath, Class target) {
        	if ( object instanceof SimpleFeature ) {
        	    SimpleFeature f = (SimpleFeature) object;
        	    Object defaultGeometry = f.getDefaultGeometry();

                    // not found? Ok, let's do a lookup then...
                    if ( defaultGeometry == null ) {
                        for ( Object o : f.getAttributes() ) {
                            if ( o instanceof Geometry ) {
                                defaultGeometry = o;
                                break;
                            }
                        }
                    }
                    
                    return defaultGeometry;
        	}
        	
                if ( object instanceof SimpleFeatureType ) {
                    SimpleFeatureType ft = (SimpleFeatureType) object;
                    GeometryDescriptor gd = ft.getGeometryDescriptor();
                
                    if ( gd == null ) {
                        //look for any geometry descriptor
                        for ( AttributeDescriptor ad : ft.getAttributeDescriptors() ) {
                            if ( Geometry.class.isAssignableFrom( ad.getType().getBinding() ) ) {
                                return ad;
                            }
                        }
                    }
                    
                    return gd;
                }
            
        	return null;
        }
    }

    static class SimpleFeaturePropertyAccessor implements PropertyAccessor {
        public boolean canHandle(Object object, String xpath, Class target) {
        	xpath = stripPrefix(xpath);
        	
        	if ( object instanceof SimpleFeature ) {
        		return ((SimpleFeature) object).getType().getDescriptor(xpath) != null;
        	}
        	
        	if ( object instanceof SimpleFeatureType ) {
        		return ((SimpleFeatureType) object).getDescriptor( xpath ) != null;
        	}
        	
        	return false;
        }
        
        public Object get(Object object, String xpath, Class target) {
        	xpath = stripPrefix(xpath);
        	
        	if ( object instanceof SimpleFeature ) {
        		return ((SimpleFeature) object).getAttribute( xpath );
        	}
        	
        	if ( object instanceof SimpleFeatureType ) {
        		return ((SimpleFeatureType) object).getDescriptor( xpath );
        	}
        	
        	return null;
        }
    }

}
