
//javascript to handle selections - spatial or query

function IMS2_Selection(){
    var self = this;
    
    this.env = null;
    this.query = null;
    this.ring = null;
    
    //gs
    this.buffer = 0;//null; // this is the buffer distance on the selection
    this.target = null;//null; // this is the target of the buffer
    this.units = "feet";//null;
    this.showBuffer = true;//false;
    
    function IsPoint(){
        if(self.env){
            return(self.env.IsPoint());
        }
        return(false);
    }
    this.IsPoint = IsPoint;
    
    function IsEnv(){
        if(self.env){
            return(!(self.env.IsPoint()));
        }
        return(false);
    }
    this.IsEnv = IsEnv
    
    function IsPoly(){
        return((self.ring && !self.env)?(true):(false));
    }
    this.IsPoly = IsPoly;
    
    function IsQuery(){
        if(self.query){
            return(true);
        }
        return(false);
    }
    this.IsQuery = IsQuery;
}

//polygon rings - normally polygons will only have one ring each
function IMS2_PolyRing(){
    var self = this;
    
    this.points = new Array();
    this.vertexSize = 4;
    this.vertexColor = null;
    this.distance = 0;
    this.perimeter = 0;
    this.area = 0;
    this.centroid = [0,0];
    this.closed = true;
    this.lastSegLen = 0;
    this.pointOrder = new Array();
    
    function CreateSinglePoint(x,y){
        self.Clear();
        self.AddPoint(x,y);
    }
    this.CreateSinglePoint = CreateSinglePoint;
    
    
    //add delete point (vertex) from polygon ring
    function AddPoint(x,y){
        self._AddPointAt(self.points.length,x,y);
    }
    this.AddPoint = AddPoint;
    
    function DeletePoint(pnum){
        if(self.points){
            var l = self.points.length;
            if(pnum >= 0 && pnum < l){
                self.points.splice(pnum,1);
            }
        }
        self.UpdateAttrbutes();
    }
    this.DeletePoint = DeletePoint;
    
    function DeleteLastPoint(){
		if(self.points.length > 0){
			self.points.splice(self.pointOrder[self.pointOrder.length - 1],1);
			self.pointOrder.length = self.pointOrder.length - 1;
		}
    }
    this.DeleteLastPoint = DeleteLastPoint;
    
    function UpdateAttrbutes(){
        self.distance = 0;
        self.perimeter = 0;
        self.area = 0;
        self.centroid = null;
        self.lastSegLen = 0;
        
        if(self.points){
            var l = self.points.length;
            var lm1 = l - 1;
            var lm2 = l - 2;
            var i2 = 1;
            var a = 0;
            var b = 0;
            var pta = [0,0];
            var ptb = [0,0];
            var ctrdJanx = 0;
            var cx = 0;
            var cy = 0;
            for(var i = 0;i < l;i++){
                i2 = (i < lm1)?(i+1):(i-lm1);
                pta = self.points[i];
                ptb = self.points[i2];
                a = pta[0] - ptb[0];
                b = pta[1] - ptb[1];
                self.perimeter += Math.sqrt((a*a)+(b*b));
                if(lm2 == i){
                    self.distance = self.perimeter;
                }
                self.area += ((pta[0]*ptb[1]) - (ptb[0]*pta[1]));
                ctrdJanx = ((pta[0]*ptb[1]) - (ptb[0]*pta[1]));
                cx += ((pta[0]+ptb[0]) * ctrdJanx);
                cy += ((pta[1]+ptb[1]) * ctrdJanx);
            }
            self.area = self.area * 0.5;
            if(l > 0){
            
				if(l > 1){
                    pta = self.points[lm2];
                    ptb = self.points[lm1];
                    a = pta[0] - ptb[0];
                    b = pta[1] - ptb[1];
                    self.lastSegLen = Math.sqrt((a*a)+(b*b));
                }
                if(0 != self.area){
                    var osa = 1/(self.area * 6);
                    cx *= osa;
                    cy *= osa;
                }else{
                    if(2 == l){
                        cx = (self.points[0][0] + self.points[1][0]) * 0.5;
                        cy = (self.points[0][1] + self.points[1][1]) * 0.5;
                    }else{
                        cx = self.points[0][0];
                        cy = self.points[0][1];
                    }
                }
                self.centroid = [cx,cy];
            }
            self.area = Math.abs(self.area);
            //window.status = "area: " + self.area + " distance: " + self.distance + " perimeter: " + self.perimeter + " cent: " + self.centroid;
        }
    }
    this.UpdateAttrbutes = UpdateAttrbutes; 
    
    
    //insert point between two others
    function InsertPoint(x,y){
        var l = self.points.length;
        if(l > 1){
            var nearpt = self.FindNearestPoint(x,y);
            var pt = null;
            if(nearpt >= 0){
                pt = self.points[nearpt];
            }
            var perpline = self._FindNearestPerpendicularLine(x,y);
            var addByPoint = false;
            var addByLine = false;
            
            if(perpline && pt){
                var perpdist = perpline[2];
                var a = pt[0] - x;
                var b = pt[1] - y;
                var ptdst = Math.sqrt((a*a)+(b*b));
                if(ptdst < perpdist){
                    addByPoint = true;
                }else{
                    addByLine = true;
                }
            }
            if(addByLine && !addByPoint && perpline){
                self._InsertBetween(x,y,perpline[0],perpline[1]);
            }else if(nearpt >= 0 || (addByPoint && !addByLine)){
                self._InsertPointUsingPoint(x,y,nearpt);
            }else{
                self.AddPoint(x,y);
            }
        }else{
            self.AddPoint(x,y);
        }
    }
    this.InsertPoint = InsertPoint;
    
    //find the closest line to insert a point on
    function _FindNearestPerpendicularLine(x,y){
        var bestline = null;
        var bestdist = -1;
        var l = self.points.length;
        for(var i = 0;i < l;i++){
            var i2 = i + 1;
            if(i2 >= l){
                i2 = 0;
            }
            var pta = self.points[i];
            var ptb = self.points[i2];
            var x1 = pta[0];
            var y1 = pta[1];
            var x2 = ptb[0];
            var y2 = ptb[1];
            var slx = x2 - x1;
            var sly = y2 - y1;
            var x3 = x;
            var y3 = y;
            var x4 = x3 + sly;
            var y4 = y3 + slx;
            var uden =  (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1);
            if(0 != uden){
                var uanum = (x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3);
                var ubnum = (x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3);
                var ua = uanum / uden;
                var ub = ubnum / uden;
                //alert(ua + " " + ub);
                // ua is seg
                // ub is line
                if(ua >= 0 && ua <= 1){
                    var isecx = x1 + (ua * (x2 - x1));
                    var isecy = y1 + (ua * (y2 - y1));
                    var a = isecx - x;
                    var b = isecy - y;
                    var dist = Math.sqrt((a*a)+(b*b));
                    if(!bestline){
                        bestline = [i,i2,dist];
                    }else if(bestline[2] > dist){
                        bestline = [i,i2,dist];
                    }
                }
            }
        }
        return(bestline);
    }
    this._FindNearestPerpendicularLine = _FindNearestPerpendicularLine;
    
    function _InsertPointUsingPoint(x,y,nearpt){
        var l = self.points.length;
        var pnum1 = nearpt - 1;
        var pnum2 = nearpt + 1;
        if(pnum1 < 0){
            pnum1 = l-1;
        }
        if(pnum2 >= l){
            pnum2 = 0;
        }
        var pt0 = self.points[nearpt];
        pt0 = [x-pt0[0],y-pt0[1]];
        var pt1 = self.points[pnum1];
        pt1 = [pt1[0]-x,pt1[1]-y];
        var pt2 = self.points[pnum2];
        pt2 = [pt2[0]-x,pt2[1]-y];
        var ptlen = Math.sqrt((pt0[0] * pt0[0]) + (pt0[1] * pt0[1]));
        pt0[0] = pt0[0] / ptlen;
        pt0[1] = pt0[1] / ptlen;
        ptlen = Math.sqrt((pt1[0] * pt1[0]) + (pt1[1] * pt1[1]));
        pt1[0] = pt1[0] / ptlen;
        pt1[1] = pt1[1] / ptlen;
        ptlen = Math.sqrt((pt2[0] * pt2[0]) + (pt2[1] * pt2[1]));
        pt2[0] = pt2[0] / ptlen;
        pt2[1] = pt2[1] / ptlen;
        var d1 = (pt0[0]*pt1[0])+(pt0[1]*pt1[1]);
        var d2 = (pt0[0]*pt2[0])+(pt0[1]*pt2[1]);
        window.status = "" + nearpt + "," + pnum1 + "," + pnum2 + " " + d1 + "," + d2;
        self._InsertBetween(x,y,nearpt,(d1 > d2)?(pnum1):(pnum2));
    }
    this._InsertPointUsingPoint = _InsertPointUsingPoint;
    
    function _InsertBetween(x,y,pnum1,pnum2){
        if(self.points){
            var l = self.points.length;
            var insloc = 0;
            if(0 == pnum1 || 0 == pnum2){
                var lm1 = l - 1;
                if((lm1) == pnum1){
                    insloc = l;
                }else if(0 == pnum2 || (lm1) == pnum2){
                    insloc = pnum1;
                }else{
                    insloc = pnum2;
                }
                window.status = "" + pnum1 + " " + pnum2 + " " + insloc;
            }else{
            
                insloc = (pnum1 < pnum2)?(pnum2):(pnum1);
            }
            if(insloc < 0){
                insloc = l-1;
            }
            if(insloc >= l){
                insloc = 0;
            }
            for(var i = l-1;i >= insloc;i--){
                self.points[i+1] = self.points[i];
            }
            self.points[insloc] = [x,y];
        }
        self.UpdateAttrbutes();
    }
    this._InsertBetween = _InsertBetween;
    
    function _AddPointAt(loc,x,y){
        if(self.points){
            var i = (loc >= self.points.length) ? (loc) : (
                (loc < 0) ? (0) : (loc)
            );
            if(i < self.points.length){
                // shift everything over
            }
            self.points[i] = [x,y];
            self.pointOrder[self.pointOrder.length] = self.points.length - 1;
        }
        self.UpdateAttrbutes();
    }
    this._AddPointAt = _AddPointAt;
    
    function FindNearestPoint(x,y){
        var besti = -1;
        var bestDist = -1;
        var l = self.points.length;
        var pt = null;
        for(var i =0;i<l;i++){
            pt = self.points[i];
            var a = x - pt[0];
            var b = y - pt[1];
            var d = Math.sqrt((a*a) + (b*b));
            if( d < bestDist || 0 == i ){
                bestDist = d;
                besti = i;
                if(0 == bestDist){
                    break;
                }
            }
        }
        return(besti);
    }
    this.FindNearestPoint = FindNearestPoint;
    
    function IsWithinPoint(x,y,pnum,prad){
        if(pnum >= self.points.length || pnum < 0){
            return(false);
        }
        var pt = self.points[pnum];
        var a = x - pt[0];
        var b = y - pt[1];
        var d = Math.sqrt((a*a) + (b*b));
        if(d < prad){
            return(true);
        }
        return(false);
    }
    this.IsWithinPoint = IsWithinPoint;
    
    function GetPointSelection(x,y,pxscale){
        var seli = self.FindNearestPoint(x,y);
        if(seli >= 0){
            var prad = pxscale * self.vertexSize;
            if(self.IsWithinPoint(x,y,seli,prad)){
                return(seli);
            }
        }
        return(-1);
    }
    this.GetPointSelection = GetPointSelection;
    
    function Clear(){
        self.points = new Array();
    }
    this.Clear = Clear;
}

//manage multiple selections
function IMS2_SelectionGroup(){
    var self = this;
    
    this.sels = new Array();
    this.layer = "";
    this.buffer = 0;
    this.order = null;
    
    function Select(sel,addit){
        if(addit){
            ;
        }else{
            self.sels = new Array();
            self.sels[0] = sel;
        }
    }
    this.Select = Select;
    
    function Clear(){
        self.sels = new Array();
        self.layer = "";
        self.order = null;
    }
    this.Clear = Clear;
    
    function GetSelection(i){
        if(!(i)){
            i = 0;
        }
        if(self.sels){
            if(i < self.sels.length){
                return(self.sels[i]);
            }
        }
        return(null);
    }
    this.GetSelection = GetSelection;
    
    function CreateQueryXML(join,buffer){
        if(self.sels && self.sels.length > 0){
            var xmlChunk = "<SelectionGroup operator=\"" + ((join)?(join):("AND")) + "\" order=\"" + ((self.order)?(self.order):("")) + "\">";
            for(var i = 0; i <self.sels.length; i++){
                var curSel = self.sels[i];
                
                //gs - add ability to buffer selection (e.g adjoiners)
                //showbuffer will actually show the buffer polygon - only available when buffering a feature
                var buf = "";
                if(curSel.buffer != null)
                {
					buf += " buffer=\"" + curSel.buffer + "\"";
					buf += (null == curSel.target)?(""):(" target=\"" + curSel.target + "\"");
					buf += (null == curSel.units)?(""):(" units=\"" + curSel.units + "\"");
					buf += " showbuffer=\"" + curSel.showBuffer + "\"";
                }
                
                
                if(curSel){
                    if(curSel.IsQuery()){
                        xmlChunk += "<Selection type=\"query\" query=\"" + curSel.query + "\"" + buf + " ></Selection>";
                    }else{
                        if(curSel.IsPoint()){
                            var env = curSel.env;
                            xmlChunk += "<Selection type=\"spatial\"" + buf + " ><Point x=\"" + env.x1 + "\" y=\"" + env.y1 + "\"" + ((buffer)?( " buffer=\"" + buffer + "\""):("")) + " /></Selection>";
                        }else if(curSel.IsEnv()){
                            var env = curSel.env;
                            xmlChunk += "<Selection type=\"spatial\"" + buf + " ><Envelope minx=\"" + env.x1 + "\" miny=\"" + env.y1 + "\" maxx=\"" + env.x2 + "\" maxy=\"" + env.y2 + "\" /></Selection>";
                        }else if(curSel.IsPoly()){
                            //coordstring
                            var coords = new Array();
                            for(var crdi = 0; crdi < curSel.ring.points.length; crdi++){
                                var pt = curSel.ring.points[crdi];
                                coords[crdi] = pt[0] + "," + pt[1];
                            }
                            if(coords && coords.length > 0){
                                xmlChunk += "<Selection type=\"spatial\"" + buf + " ><Polygon coordstring=\"" + coords.join(";") + "\" /></Selection>";
                            }
                        }
                    }
                }
            }
            xmlChunk += "</SelectionGroup>";
        }
        //alert(xmlChunk);
        return(xmlChunk);
    }
    this.CreateQueryXML = CreateQueryXML;
}
