var request;
var wfsoverlays = new Array();
function trim(str) {
   return str.replace(/^\s*|\s*$/g,"");
}

function GMapWFS(map, baseURL, typename, buffer, proxy){
	if(!proxy || proxy == ""){
		this.useProxy = false;
	}else{
		this.useProxy = true;
	}
	this.localProxy = proxy?proxy:"wfs_proxy.php";
	this.map = map;
	this.overlays = new Array();
	this.baseURL = baseURL;
	this.typename = typename;
	this.buffer = buffer;
	this.type = 'centroid';
}

//In order to use a different proxy for WFS, you need to override this function to return the correct URL.
//In the new function you should probably use this.baseURL, this.typename, and the bounds.
GMapWFS.prototype.getWFSURL = function (bounds){
	var query;
	if(this.useProxy){	
		query = this.localProxy + '?';
		query = query + "baseURL=" + encodeURI(this.baseURL);
	}else{
		query = this.baseURL;
		query = query + "&version=1.0.0&request=GetFeature&service=WFS";
	}
	query = query + "&typename=" + encodeURI(this.typename);
	if (query.indexOf('&filter=') >= 0) {
      var bbox =  bounds.getSouthWest().lng() + "," + 
                  bounds.getSouthWest().lat() + " " + 
                  bounds.getNorthEast().lng() + "," +
                  bounds.getNorthEast().lat();
      var urlstart = query.substring(0,query.indexOf("&filter=" + 8));
      var urlfilter = query.substring(query.indexOf("&filter=" + 8),query.indexOf("&typename="));
      var urlend = query.substring(query.indexOf("&typename="));
      var ftemp = urlfilter.indexOf("<ogc:")+1;
      var filterstart = urlfilter.substring(0,urlfilter.indexOf("<ogc:",ftemp));
      var propertyfilter = urlfilter.substring(urlfilter.indexOf("<ogc:",ftemp),urlfilter.indexOf("</ogc:Filter>"));
      var filterend = urlfilter.substring(urlfilter.indexOf("</ogc:Filter>"));
      
      var boxfilter ='<ogc:BBOX>';
      boxfilter +=      '<ogc:PropertyName>box</ogc:PropertyName>';
      boxfilter +=      '<gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml">';
      boxfilter +=         "<gml:coordinates>" + bbox + "</gml:coordinates>";
      boxfilter +=      '</gml:Box>';
      boxfilter +=   '</ogc:BBOX>';
      var filter = filterstart + "<ogc:And>" + propertyfilter + boxfilter + "</ogc:And>" + filterend;
      query = urlstart + filter + urlend;
   } else {
      var bbox =  bounds.getSouthWest().lng() + "," + 
                  bounds.getSouthWest().lat() + "," + 
                  bounds.getNorthEast().lng() + "," +
                  bounds.getNorthEast().lat();
      query = query + "&bbox=" + encodeURI(bbox);
	}
	return query;
}

GMapWFS.prototype.setBaseURL = function (baseURL){
	this.baseURL = baseURL;
}

GMapWFS.prototype.setTypename = function (typename){
	this.typename = typename;
}

GMapWFS.prototype.addOverlay = function (a){
	this.map.addOverlay(a);
}
/*
GMapWFS.prototype.clear = function (){
	this.map.clearOverlays();
}
*/
GMapWFS.prototype.clear = function (){
	this.map.clearOverlays();
	return true;
}

GMapWFS.prototype.refreshInBounds = function (bounds) {
   if (request) request.abort();
   else request = GXmlHttp.create();
   if(!bounds) bounds = this.map.getBounds();
	var markers = new Array();
	var href;
   var coordinates = new Array();
   var polygons = new Array();
   var cstring,etemp;
   var url = this.getWFSURL(bounds);
	request.open("GET", url, true);
   var wfs = this; //copies the reference because we can't use this to refer to this in the onreadystatechange function below
   wfs.clear();
   request.onreadystatechange = function() {
      if (request.readyState == 4) {
         var response = request.responseText;
         var xml = GXml.parse(response);
         /*          
            var texml = response;
            texml = texml.replace(/</g,"&lt;");
            texml = texml.replace(/>\n/g,"&gt;");
            document.getElementById('response').innerHTML = '<pre>' + texml + '</pre>';
         */
         var pxml,element,headers,data,name,value,marker,polygon;
         while (xml.nodeName != 'wfs:FeatureCollection') {
            if (xml.childNodes.length > 0)
               xml = xml.childNodes[0];
            else xml = xml.nextSibling;
         }
         var features = xml.childNodes;
         for (var i=1; i < features.length; i++) {
            /* WFS response looks like 
               root - <FeatureCollection>
               array of - <featureMember>
               layer with feature id - <layer fid='stuff'>
               point attributes
            */
            xy = new Array();
	         pxml = features[i].childNodes[0];
			   headers = "<tr>";
			   data = "<tr>";
			   for (var j=0;j<pxml.childNodes.length;j++) {
			      element = pxml.childNodes[j];
			      /* 
                  get rid of the namespace to avoid people using different 
			         namespaces being a problem 
               */
			      name = element.nodeName.substring(element.nodeName.indexOf(':')+1);
               if (element.childNodes.length > 0) value = element.childNodes[0].nodeValue;
               else value = "unknown";
               /*
                  if the element has a value then it's one of the attributes
                  of the feature
                  if it doesn't have a value then it's the element containing
                  the feature's geometry
               */
               if (value && value != 'undefined') {
                  if (!columns || columns[name]) {
                     headers += "<th class='lined'>" + name + "</th>";
                     if (value.indexOf('.jpg') >= 0 ||
                         value.indexOf('.gif') >= 0 ||
                         value.indexOf('.png') >= 0 ||
                         value.indexOf('?image=') >= 0) {
                        data += "<td class='lined'><a class='thumbnail' target='maplink' href='" + value + "'><img class='thumbnail' src='" + value + "' alt='Species Photo'/></a></td>"; 
                     } else if (  value.indexOf('http') >= 0    || 
                           value.indexOf('ftp') >= 0     ||
                           value.indexOf('mailto') >= 0  ) {
                        data += "<td class='lined'><a target='maplink' href='" + value + "'>" + value + "</a></td>"; 
                     } else if ( value.indexOf('.co.uk') >= 0  ||
                                 value.indexOf('.org.uk') >= 0 ||
                                 value.indexOf('.net') >= 0    ||
                                 value.indexOf('.org') >= 0    ||
                                 value.indexOf('.com') >= 0    ) {
                        href = 'http://' + value;
                        data += "<td class='lined'><a target='maplink' href='" + href + "'>" + value + "</a></td>"; 
                     } else {
                        var style = '';
                        if (value.length > 20) style = "style='font-size: smaller;'";
                        data += "<td class='lined' " + style + ">" + value + "</td>"; 
                     }
                  }
               } else {
                  xy = getCentroid(element);
                  if (wfspolygons) polygon = getPolygon(element);
               }
            }
            headers += "</tr>";
            data += "</tr>";
            cstring = xy[0] + "," + xy[1];
            if (coordinates[cstring]) {
               if (wfsmarkers) {
                  marker = markers[coordinates[cstring]];
                  marker.id += "," + pxml.attributes['fid'];
                  marker.html += headers + data;
               }
               if (wfspolygons) {
                  polygon = polygons[coordinates[cstring]];
                  if (!polygon.colors) polygon.colors = new Array();
                  if (data.indexOf('nbn') >= 0) polygon.colors.push('00ff00');
                  else if (data.indexOf('yhedn') >= 0) polygon.colors.push('ff0000');
                  else polygon.colors.push('ff9900');
               }
            } else {
               if (wfsmarkers) {
                  marker = new GMarker(new GPoint(xy[0],xy[1]));
                  /* not using fid at the moment but thought it might be useful */
                  marker.id = pxml.attributes['fid'];
                  marker.cstring = cstring;
                  /* marker.html is what gets dispayed in the feature's speech 
                     bubble */
                  marker.html = headers + data;
                  GEvent.addListener(marker, "click", function () {
                     wfssuppress = 1;
                     this.openInfoWindowHtml(this.html);
                  });
                  markers.push(marker);
                  wfsoverlays.push(marker);
                  coordinates[cstring] = markers.length -1;
               }
               if (wfspolygons) {
                  polygon.cstring = cstring;
                  polygon.colors = new Array();
                  if (data.indexOf('nbn') >= 0) polygon.colors.push('00ff00');
                  else if (data.indexOf('yhedn') >= 0) polygon.colors.push('ff0000');
                  else polygon.colors.push('ff9900');
                  polygons.push(polygon);
                  wfsoverlays.push(polygon);
                  coordinates[cstring] = polygons.length -1;
               }
            }
         }
         if (wfspolygons) {
            polygons = updatePolygonColors(polygons);
            addPolygons(map,polygons);
            //wfsoverlays.push(polygons);
         }
         if (wfsmarkers) {
            addMarkers(map,markers);
            //wfsoverlays.push(markers);
         }
         if (wfsprogress) wfsprogress = 0;
      } 
	} 
	request.send(null);
	return true;
}

GMapWFS.prototype.refreshInBoundsWithBuffer = function (buffer, bounds){
	this.refreshInBounds(this.bufferBounds(buffer, bounds));
}

GMapWFS.prototype.bufferBounds = function (buffer, bounds){
	if(!buffer){
		buffer = this.buffer;
	}
	if(!bounds){
		bounds = this.map.getBounds();
	}
	var width = bounds.getNorthEast().lng() - bounds.getSouthWest().lng();
	var height = bounds.getNorthEast().lat() - bounds.getSouthWest().lat();
	bounds = new GLatLngBounds(
      new GLatLng(
         bounds.getSouthWest().lat() - height*buffer,
	      bounds.getSouthWest().lng() - width*buffer
	   ),
	   new GLatLng(
	      bounds.getNorthEast().lat() + height*buffer,
	      bounds.getNorthEast().lng() + width*buffer
	   ));
	return bounds;
}

function getCentroid(element) {
/*
   the coordinates element is quite deep into the xml and 
   at a different depth depending on whether it's a point 
   or a polygon etc so .. just keep going deeper into the 
   xml until you find coordinates
*/
   var xy,name,poly,verts,v,n,minx,miny,maxx,maxy;
   while (name != 'coordinates') {
      element = element.childNodes[0];
      name = element.nodeName.substring(element.nodeName.indexOf(':')+1);
   }
   /*
      all geometries eventually have a gml:coordinates element
      i'm only interested in plotting centroids so i get all 
      the points and turn them into a centroid.   
   */
//alert(element.nodeName + " " + element.nodeValue);
   if (element.childNodes[0].nodeValue.length == 4096) {
      poly = element.childNodes[0].nodeValue;
      for (n=1;n<element.childNodes.length;n++) {
         poly += element.childNodes[n].nodeValue;
      }
   } else  poly = element.childNodes[0].nodeValue;
   
   verts = trim(poly).split(" ");
   if (verts.length > 1) {
   /*
      simple rough centroid from the bounding box of 4  
      of the vertices with greatest separation
   */   
      var gap = Math.floor(verts.length / 4);
      if (gap > 1) gap = 1;
      for (v=0;v<verts.length;v+=gap) {
         xy = verts[v].split(",");
         if (xy.length == 2) {
            if (v==0) {
               minx = xy[0];
               miny = xy[1];
               maxx = xy[0];
               maxy = xy[1];
            } else {
               minx = Math.min(minx,xy[0]);
               miny = Math.min(miny,xy[1]);
               maxx = Math.max(maxx,xy[0]);
               maxy = Math.max(maxy,xy[1]);
            }
         } 
      }
      xy[0] = minx + ((maxx - minx)/2);
      xy[1] = miny + ((maxy - miny)/2);
   } else {
      xy = verts[0].split(",");
   }
   return xy;
}

function getPolygon(element) {
/*
   the coordinates element is quite deep into the xml and 
   at a different depth depending on whether it's a point 
   or a polygon etc so .. just keep going deeper into the 
   xml until you find coordinates
*/
   var polygon;
   var xy,name,poly,verts,v,n,minx,miny,maxx,maxy;
   while (name != 'coordinates') {
      element = element.childNodes[0];
      name = element.nodeName.substring(element.nodeName.indexOf(':')+1);
   }
   /*
      all geometries eventually have a gml:coordinates element
      i'm only interested in plotting centroids so i get all 
      the points and turn them into a centroid.   
   */
   if (element.childNodes[0].nodeValue.length == 4096) {
      poly = element.childNodes[0].nodeValue;
      for (n=1;n<element.childNodes.length;n++) {
         poly += element.childNodes[n].nodeValue;
      }
   } else  poly = element.childNodes[0].nodeValue;
   
   verts = trim(poly).split(" ");
   if (verts.length > 1) {
   /*
      simple rough centroid from the bounding box of all 
      the vertices
   */   
      var points = new Array();
      for (v=0;v<verts.length;v++) {
         xy = verts[v].split(",");
         if (xy.length == 2) {
            points.push(new GPoint(xy[0],xy[1]));
         } 
      }
   } 
   /* 
      alter colours for different resolution polygons
   */
   //polygon = new GPolygon(points,'#003300',1,1,'#00ff00',0.5);
   polygon = new Shape(points);
   return polygon;
}
function Shape(points) {
   this.points = points;
   this.cstring = null;
   this.colors = null;
   this.fill = null;
   this.outline = null;
}
function addMarkers(map,markers) {
   var marker;
   for (var i=0;i<markers.length;i++) {
      marker = markers[i];
      marker.html = "<div style='width: 320px; height: 120px; overflow: auto;'><table>" +
                     marker.html + "</table></div>";
      map.addOverlay(marker);
   }
   return true;
}

function addPolygons(map,polygons) {
   var shape,polygon;
   for (var i=0;i<polygons.length;i++) {
      shape = polygons[i];
      polygon = new GPolygon(shape.points,shape.outline,1,1,shape.fill,0.5);
      map.addOverlay(polygon);
   }
   return true;
}
function d2h(d) {return d.toString(16);}
function h2d(h) {return parseInt(h,16);}
function averageColors(colors,dim) {
   dim = (dim>0)?dim:1;
   var color,c1,c2,r,g,b,component,clength,s,e;
   r = 0;
   g = 0;
   b = 0;
   for (var i=0;i<colors.length;i++) {
      color = colors[i];
      clength = Math.floor(color.length /3);
      r += h2d(color.substring(0,clength));
      g += h2d(color.substring(clength,(clength*2)));
      b += h2d(color.substring((clength*2),(clength*3)));
   }
   r = Math.floor(r/(colors.length * dim));
   g = Math.floor(g/(colors.length * dim));
   b = Math.floor(b/(colors.length * dim));
   r = d2h(r);
   while (r.length < 2) r = new String('0' + r); 
   g = d2h(g);
   while (g.length < 2) g = new String('0' + g); 
   b = d2h(b);
   while (b.length < 2) b = new String('0' + b); 
   return r+g+b;
}
function updatePolygonColors(polygons) {
   var c;
   for (var i=0;i<polygons.length;i++) {
      c = averageColors(polygons[i].colors);
      polygons[i].fill = '#' + averageColors(polygons[i].colors);
      polygons[i].outline = '#' + averageColors(polygons[i].colors,3);
   }
   return polygons;
}
