<%--
  -Copyright (C) 2004-2006  Autodesk, Inc.
  -This library is free software; you can redistribute it and/or
  -modify it under the terms of version 2.1 of the GNU Lesser
  -General Public License as published by the Free Software Foundation.

  -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.

  -You should have received a copy of the GNU Lesser General Public
  -License along with this library; if not, write to the Free Software
  -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
--%>

<%@ page import="org.osgeo.mapguide.*" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%@ page import="java.text.*" %>
<%@ page import="javax.servlet.jsp.*" %>
<%@ page import="javax.servlet.http.*" %>
<%@ page import="org.json.*" %>
<%@ include file ="property.jsp" %>
<%@ include file ="feature.jsp" %>

<%!
    public class Query
    {
        Map<String, String> args = null;
        private MgSiteConnection site = null;
        private String[] numOperators = null;
        private String[] numExpressions = null;
        private String[] strOperators = null;
        private String[] strExpressions = null;
        
        public Query(Map<String, String> incomingArgs) throws MgException
        {
            this.args = incomingArgs;
            this.site = new MgSiteConnection();
            this.site.Open(new MgUserInformation(this.args.get("SESSION")));
            this.numOperators = new String[] {"Equal to", "Not equal to", "Greater than", "Greater than or equal to", "Less than", "Less than or equal to"};
            this.numExpressions = new String[] {" = %s", " != %s", " > %s", " >= %s", " < %s", " <= %s"};
            this.strOperators = new String[] {"Begins with", "Contains", "Equal to"};
            this.strExpressions = new String[] {" like '%s%%'", " like '%%%s%%'", " = '%s'"};
        }
        
        public String getNumOp()
        {
            JSONArray jsonArray = new JSONArray();
            for(int i=0;i<6;i++)
            {
                jsonArray.put(numOperators[i]);
            }
            return jsonArray.toString();
        }

        public String getStrOp()
        {
            JSONArray jsonArray = new JSONArray();
            for(int i=0;i<3;i++)
            {
                jsonArray.put(strOperators[i]);
            }
            return jsonArray.toString();
        }
        
        public ArrayList<String> GetMapLayerNames() throws MgException
        {
            MgMap map = new MgMap(this.site);
            map.Open(this.args.get("MAPNAME"));
            MgLayerCollection layers = map.GetLayers();
            ArrayList<String> layerNames = new ArrayList<String>();

            for(int i=0; i<layers.GetCount(); i++)
            {
                MgLayer layer = (MgLayer) layers.GetItem(i);

                //TODO: Exclue Raster and Drawing Layers???
                
                if(!layer.GetName().startsWith("_") && !layer.GetFeatureSourceId().toUpperCase().startsWith("SESSION"))
                    layerNames.add(layer.GetLegendLabel());
            }
            Collections.sort(layerNames);

            return layerNames;
        }
        
        public ArrayList<Property> GetLayerProperties() throws MgException
        {
            ArrayList<Property> properties = new ArrayList<Property>();
            MgMap map = new MgMap(this.site);
            map.Open(this.args.get("MAPNAME"));
            MgLayer layer = (MgLayer) map.GetLayers().GetItem(this.args.get("LAYERNAME"));

            MgClassDefinition classDef = layer.GetClassDefinition();

            for(int i=0; i<classDef.GetProperties().GetCount(); i++)
            {
                MgPropertyDefinition propertyDef = classDef.GetProperties().GetItem(i);

                if(propertyDef.GetPropertyType() == MgFeaturePropertyType.DataProperty)
                {
                    MgDataPropertyDefinition propertyDataDef = (MgDataPropertyDefinition) propertyDef;
                    int dataType = propertyDataDef.GetDataType();
                    if(this.IsValidDataType(dataType))
                    {
                        properties.add(new Property(propertyDataDef.GetName(), dataType == MgPropertyType.String));
                    }
                }
            }
            return properties;
        }
        
        public boolean ToggleSpatialFilter() throws MgException
        {
            boolean result = true;
            MgMap map = new MgMap(this.site);
            map.Open(this.args.get("MAPNAME"));

            MgLayerCollection layers = map.GetLayers();
            if(layers.Contains("_QuerySpatialFilter"))
            {
                MgLayer layer = (MgLayer) layers.GetItem("_QuerySpatialFilter");
                if(this.args.get("VISIBLE").equals("true"))
                    layer.SetVisible(true);
                else
                    layer.SetVisible(false);

                map.Save();
            }

            return result;
        }

        public boolean ShowSpatialFilter() throws MgException, FileNotFoundException, IOException
        {
            boolean result = true;
            MgResourceIdentifier sdfResId = new MgResourceIdentifier("Session:"+this.args.get("SESSION")+"//Filter.FeatureSource");

            MgResourceService resourceService = (MgResourceService)this.site.CreateService(MgServiceType.ResourceService);
            MgFeatureService featureService = (MgFeatureService)this.site.CreateService(MgServiceType.FeatureService);

            MgFeatureCommandCollection updateCommands = new MgFeatureCommandCollection();

            MgMap map = new MgMap(this.site);
            map.Open(this.args.get("MAPNAME"));

            MgLayer layer = null;
            MgLayerCollection layers = map.GetLayers();
            if(layers.Contains("_QuerySpatialFilter"))
            {
                layer = (MgLayer) layers.GetItem("_QuerySpatialFilter");
                updateCommands.Add(new MgDeleteFeatures("Filter", "ID like '%'"));
            }
            else
            {
                // Create the Feature Source (SDF)

                MgFeatureSchema sdfSchema = this.CreateFilterSchema();
                MgFileFeatureSourceParams sdfParams = new MgFileFeatureSourceParams("OSGeo.SDF", "MAPCS", map.GetMapSRS(), sdfSchema);
                featureService.CreateFeatureSource(sdfResId, sdfParams);

                // Create the Layer

                MgResourceIdentifier layerResId = new MgResourceIdentifier("Session:"+this.args.get("SESSION")+"//Filter.LayerDefinition");

                String lineSep = System.getProperty("line.separator");
                BufferedReader buffReader = new BufferedReader(new FileReader(GetQueryXmlTemplatePath()));
                String nextLine = "";
                StringBuffer strBuffer = new StringBuffer();
                while((nextLine = buffReader.readLine()) != null)
                {
                    strBuffer.append(nextLine);
                    strBuffer.append(lineSep);
                }
                String layerDefinition = strBuffer.toString();
                layerDefinition = layerDefinition.replaceAll("%s", sdfResId.ToString());

                MgByteSource byteSource = new MgByteSource(layerDefinition.getBytes(), layerDefinition.length());
                resourceService.SetResource(layerResId, byteSource.GetReader(), null);

                layer = new MgLayer(layerResId, resourceService);

                layer.SetName("_QuerySpatialFilter");
                layer.SetLegendLabel("_QuerySpatialFilter");
                layer.SetDisplayInLegend(false);
                layer.SetSelectable(false);

                layers.Insert(0, layer);
            }

            // Make the layer visible

            layer.SetVisible(true);
            map.Save();

            // Add the geometry to the filter feature source

            MgPolygon polygon = this.CreatePolygonFromGeomText(this.args.get("GEOMTEXT"));
            MgAgfReaderWriter agfWriter = new MgAgfReaderWriter();
            MgByteReader byteReader = agfWriter.Write(polygon);

            MgPropertyCollection propertyValues = new MgPropertyCollection();
            propertyValues.Add(new MgGeometryProperty("Geometry", byteReader));

            updateCommands.Add(new MgInsertFeatures("Filter", propertyValues));

            featureService.UpdateFeatures(sdfResId, updateCommands, false);

            return result;
        }
            
        public ArrayList<Feature> Execute() throws MgException
        {
            ArrayList<Feature> result = new ArrayList<Feature>();

            MgMap map = new MgMap(this.site);
            map.Open(this.args.get("MAPNAME"));
            MgLayer layer = (MgLayer) map.GetLayers().GetItem(this.args.get("LAYERNAME"));

            MgFeatureService featureService = (MgFeatureService)this.site.CreateService(MgServiceType.FeatureService);
            MgResourceIdentifier resId = new MgResourceIdentifier(layer.GetFeatureSourceId());
            String featureGeometry = layer.GetFeatureGeometryName();

            // Initialize the coordinate system transform

            MgClassDefinition classDef = layer.GetClassDefinition();
            MgGeometricPropertyDefinition geomProp = (MgGeometricPropertyDefinition) classDef.GetProperties().GetItem(featureGeometry);
            String spatialContext = geomProp.GetSpatialContextAssociation();

            MgCoordinateSystemTransform csTransform = null;
            MgCoordinateSystemTransform csInverseTransform = null;
            MgCoordinateSystemFactory coordSysFactory = new MgCoordinateSystemFactory();

            MgSpatialContextReader scReader = featureService.GetSpatialContexts(resId, false);
            while(scReader.ReadNext() && csTransform==null)
            {
                if(spatialContext.equals(scReader.GetName()))
                {
                    MgCoordinateSystem source = coordSysFactory.Create(scReader.GetCoordinateSystemWkt());
                    MgCoordinateSystem target = coordSysFactory.Create(map.GetMapSRS());
                    csTransform = coordSysFactory.GetTransform(source, target);
                    csInverseTransform = coordSysFactory.GetTransform(target, source);
                }
            }
            scReader.Close();

            // Execute the query

            int queryMax = Integer.parseInt(this.args.get("QUERYMAX").trim());
            MgFeatureQueryOptions queryOptions = new MgFeatureQueryOptions();

            if(this.args.get("USEPROPERTYFILTER").equals("true"))
            {
                String propertyFilter = this.args.get("PROPERTYNAME");
                int operator = Integer.valueOf(this.args.get("OPERATOR"));
                int count = 0;
                if(this.args.get("ISSTRING").equals("true"))
                {
                    propertyFilter = propertyFilter + this.strExpressions[operator].replaceAll("%s", this.args.get("VALUE"));
                }
                else
                {
                    propertyFilter = propertyFilter + this.numExpressions[operator].replaceAll("%s", this.args.get("VALUE"));
                }
                queryOptions.SetFilter(propertyFilter);
            }

            if(this.args.get("USESPATIALFILTER").equals("true"))
            {
                MgPolygon polygon = this.CreatePolygonFromGeomText(this.args.get("GEOMTEXT"));
                MgGeometry geometry = (MgGeometry) polygon.Transform(csInverseTransform);
                queryOptions.SetSpatialFilter(featureGeometry, geometry, MgFeatureSpatialOperations.Intersects);
            }

            int count = 0;
            MgAgfReaderWriter geometryReaderWriter = new MgAgfReaderWriter();
            MgFeatureReader featureReader = layer.SelectFeatures(queryOptions);
            String displayValue = null;
            while(featureReader.ReadNext() && (queryMax <= 0 || count < queryMax))
            {
                MgByteReader byteReader = featureReader.GetGeometry(featureGeometry);
                MgPoint centerPoint = null;
                try 
                {
                    MgGeometry geometry = geometryReaderWriter.Read(byteReader);
                    centerPoint = geometry.GetCentroid();
                    centerPoint = (MgPoint) centerPoint.Transform(csTransform);
                }
                catch (MgException ex) //Maybe because of bad geometry
                {
                    centerPoint = null;
                }

                Map idList = new HashMap(this.GetFeatureIdList(featureReader));
                
                int propertyType = featureReader.GetPropertyType(this.args.get("OUTPUTPROPERTY"));
                switch(propertyType)
                {
                    case MgPropertyType.Boolean :
                        displayValue = String.valueOf(featureReader.GetBoolean(this.args.get("OUTPUTPROPERTY")));
                        break;
                    case MgPropertyType.Byte :
                        displayValue = String.valueOf(featureReader.GetByte(this.args.get("OUTPUTPROPERTY")));
                        break;
                    case MgPropertyType.Single :
                        displayValue = String.valueOf(featureReader.GetSingle(this.args.get("OUTPUTPROPERTY")));
                        break;
                    case MgPropertyType.Double :
                        displayValue = String.valueOf(featureReader.GetDouble(this.args.get("OUTPUTPROPERTY")));
                        break;
                    case MgPropertyType.Int16 :
                        displayValue = String.valueOf(featureReader.GetInt16(this.args.get("OUTPUTPROPERTY")));
                        break;
                    case MgPropertyType.Int32 :
                        displayValue = String.valueOf(featureReader.GetInt32(this.args.get("OUTPUTPROPERTY")));
                        break;
                    case MgPropertyType.Int64 :
                        displayValue = String.valueOf(featureReader.GetInt64(this.args.get("OUTPUTPROPERTY")));
                        break;
                    case MgPropertyType.String :
                        displayValue = featureReader.GetString(this.args.get("OUTPUTPROPERTY"));
                        break;
                    case MgPropertyType.DateTime :
                    case MgPropertyType.Null :
                    case MgPropertyType.Blob :
                    case MgPropertyType.Clob :
                    case MgPropertyType.Feature :
                    case MgPropertyType.Geometry :
                    case MgPropertyType.Raster :
                        displayValue = "[unsupported data type]";
                        break;
                }
                result.add(new Feature(displayValue, centerPoint, idList));
                count++;
            }

            return result;
        }

        public String GetSelectionXML() throws MgException, JSONException
        {
            MgFeatureService featureService = (MgFeatureService)this.site.CreateService(MgServiceType.FeatureService);

            MgMap map = new MgMap(this.site);
            map.Open(this.args.get("MAPNAME"));
            MgLayer layer = (MgLayer) map.GetLayers().GetItem(this.args.get("LAYERNAME"));
            String featureClass = layer.GetFeatureClassName();
            MgClassDefinition classDef = layer.GetClassDefinition();

            MgPropertyCollection properties = new MgPropertyCollection();
            MgDataPropertyDefinition dataPropDef = null;
            JSONObject json = new JSONObject(this.args.get("IDLIST"));

            Iterator it = json.keys();
            while(it.hasNext())
            {
                String key = (String) it.next();
                Object value = json.get(key);
                dataPropDef = (MgDataPropertyDefinition) classDef.GetProperties().GetItem(key);
                switch(dataPropDef.GetDataType())
                {
                    case MgPropertyType.Boolean :
                        properties.Add(new MgBooleanProperty(key, Boolean.valueOf(value.toString())));
                        break;
                    case MgPropertyType.Byte :
                        properties.Add(new MgByteProperty(key, Byte.valueOf(value.toString())));
                        break;
                    case MgPropertyType.Single :
                        properties.Add(new MgSingleProperty(key, Float.valueOf(value.toString())));
                        break;
                    case MgPropertyType.Double :
                        properties.Add(new MgDoubleProperty(key, Double.valueOf(value.toString())));
                        break;
                    case MgPropertyType.Int16 :
                        properties.Add(new MgInt16Property(key, Short.valueOf(value.toString())));
                        break;
                    case MgPropertyType.Int32 :
                        properties.Add(new MgInt32Property(key, Integer.valueOf(value.toString())));
                        break;
                    case MgPropertyType.Int64 :
                        properties.Add(new MgInt64Property(key, Long.valueOf(value.toString())));
                        break;
                    case MgPropertyType.String :
                        properties.Add(new MgStringProperty(key, value.toString()));
                        break;
                    case MgPropertyType.DateTime :
                    case MgPropertyType.Null :
                    case MgPropertyType.Blob :
                    case MgPropertyType.Clob :
                    case MgPropertyType.Feature :
                    case MgPropertyType.Geometry :
                    case MgPropertyType.Raster :
                        break;
                }
            }

            MgSelection selection = new MgSelection(map);
            selection.AddFeatureIds(layer, featureClass, properties);

            return selection.ToXml();
        }
            
        private Map GetFeatureIdList(MgFeatureReader featureReader) throws MgException
        {
            MgClassDefinition classDef = featureReader.GetClassDefinition();
            MgPropertyDefinitionCollection idProps = classDef.GetIdentityProperties();
            Map idList = new HashMap();

            for(int i=0; i<idProps.GetCount(); i++)
            {
                MgDataPropertyDefinition idProp = (MgDataPropertyDefinition) idProps.GetItem(i);

                switch(idProp.GetDataType())
                {
                    case MgPropertyType.Boolean :
                        idList.put(idProp.GetName(), featureReader.GetBoolean(idProp.GetName()));
                        break;
                    case MgPropertyType.Byte :
                        idList.put(idProp.GetName(), featureReader.GetByte(idProp.GetName()));
                        break;
                    case MgPropertyType.Single :
                        idList.put(idProp.GetName(), featureReader.GetSingle(idProp.GetName()));
                        break;
                    case MgPropertyType.Double :
                        idList.put(idProp.GetName(), featureReader.GetDouble(idProp.GetName()));
                        break;
                    case MgPropertyType.Int16 :
                        idList.put(idProp.GetName(), featureReader.GetInt16(idProp.GetName()));
                        break;
                    case MgPropertyType.Int32 :
                        idList.put(idProp.GetName(), featureReader.GetInt32(idProp.GetName()));
                        break;
                    case MgPropertyType.Int64 :
                        idList.put(idProp.GetName(), featureReader.GetInt64(idProp.GetName()));
                        break;
                    case MgPropertyType.String :
                        idList.put(idProp.GetName(), featureReader.GetString(idProp.GetName()));
                        break;
                    case MgPropertyType.DateTime :
                        idList.put(idProp.GetName(), featureReader.GetDateTime(idProp.GetName()));
                        break;
                    case MgPropertyType.Null :
                    case MgPropertyType.Blob :
                    case MgPropertyType.Clob :
                    case MgPropertyType.Feature :
                    case MgPropertyType.Geometry :
                    case MgPropertyType.Raster :
                        break;
                }
            }

            return idList;
        }
            
        private boolean IsValidDataType(int type)
        {
            boolean valid = true;

            switch(type)
            {
            case MgPropertyType.Blob:
            case MgPropertyType.Clob:
            case MgPropertyType.Byte:
            case MgPropertyType.Feature:
            case MgPropertyType.Geometry:
            case MgPropertyType.Null:
                valid = false;
                break;
            }

            return valid;
        }
            
        private MgFeatureSchema CreateFilterSchema() throws MgException
        {
            MgFeatureSchema filterSchema = new MgFeatureSchema();
            filterSchema.SetName("FilterSchema");

            MgClassDefinition filterClass = new MgClassDefinition();
            filterClass.SetName("Filter");
            MgPropertyDefinitionCollection properties = filterClass.GetProperties();

            MgDataPropertyDefinition idProperty = new MgDataPropertyDefinition("ID");
            idProperty.SetDataType(MgPropertyType.Int32);
            idProperty.SetReadOnly(true);
            idProperty.SetNullable(false);
            idProperty.SetAutoGeneration(true);
            properties.Add(idProperty);

            MgGeometricPropertyDefinition geometryProperty = new MgGeometricPropertyDefinition("Geometry");
            geometryProperty.SetGeometryTypes(MgFeatureGeometricType.Surface);
            geometryProperty.SetHasElevation(false);
            geometryProperty.SetHasMeasure(false);
            geometryProperty.SetReadOnly(false);
            geometryProperty.SetSpatialContextAssociation("MAPCS");
            properties.Add(geometryProperty);

            filterClass.GetIdentityProperties().Add(idProperty);
            filterClass.SetDefaultGeometryPropertyName("Geometry");

            filterSchema.GetClasses().Add(filterClass);

            return filterSchema;
        }
        
        private MgPolygon CreatePolygonFromGeomText(String geomText) throws MgException
        {
            MgGeometryFactory geometryFactory = new MgGeometryFactory();

            String[] vertices = geomText.split(",");
            int count = Integer.valueOf(vertices[0]);

            MgCoordinateCollection coords = new MgCoordinateCollection();
            for(int i=0; i<count; i++)
            {
                MgCoordinate coord = geometryFactory.CreateCoordinateXY(Double.valueOf(vertices[(i*2)+1]), Double.valueOf(vertices[(i*2)+2]));
                coords.Add(coord);
            }

            MgLinearRing linearRing = geometryFactory.CreateLinearRing(coords);
            MgPolygon polygon = geometryFactory.CreatePolygon(linearRing, null);

            return polygon;
        }

    }

%>