/*
 * Filled Polygon Library for Google Maps
 *
 * This software is distributed on an "AS IS" basis, without warranties or
 * conditions of any kind, either express or implied.
 *
 */    

JPolygon.defaultColor = "#0000ff";
JPolygon.outlineThickness = 1;
JPolygon.fillThickness = 1;
JPolygon.defaultOutlineOpacity = 1.0;
JPolygon.defaultFillOpacity = 0.4;
JPolygon.maxLinePoints = 200;

function JPolygon(points, color, thick, fill, map, closePolygon) {
    this.points = [];
    this.sortedPoints = [];
    if (points) {
        this.points = points.slice();
        if (closePolygon) this.points.push(points[0]);

        // copy the points array into one that will be sorted to find the lowest latitude
        this.sortedPoints = points.slice();
        this.sortedPoints.sort();
    }

    this.color = JPolygon.defaultColor;
    if (color) {
        this.color = color;
    }
    
    this.outlineThickness = JPolygon.outlineThickness;
    if (thick) {
        this.outlineThickness = thick;
    }

    //this.outlineThickness = JPolygon.outlineThickness;
    this.fillThickness = JPolygon.fillThickness;
    this.outlineOpacity = JPolygon.defaultOutlineOpacity; 
    this.fillOpacity = JPolygon.defaultFillOpacity;


    this.polygonOutline = new GPolyline(this.points, this.color, this.outlineThickness, this.outlineOpacity);

    this.map = null;
    if (map) {
        this.map = map;
    }

    this.fillLines = null;
    if (fill) {
        this.pre1 = JPolygon.posInArray(this.sortedPoints[0], this.points);
        this.pre2 = JPolygon.backPos(this.pre1, this.points.length);
        this.post1 = this.pre1;
        this.post2 = JPolygon.forwardPos(this.post1, this.points.length);
        this.preSlope = 0;
        this.postSlope = 0;
        this.fillLines = [];

        this.fillPolygon();
    }


    this.minLat = this.sortedPoints[0].lat();
    this.maxLat = this.sortedPoints[this.sortedPoints.length-1].lat();
    this.minLng = 0;
    this.maxLng = 0;
    this.getMinMaxLng(this.points);
    this.latlngBounds = new GLatLngBounds(new GLatLng(this.minLat, this.minLng), new GLatLng(this.maxLat, this.maxLng));
}


JPolygon.prototype = new GOverlay();


JPolygon.prototype.initialize = function(map) {
    this.map = map;
    if (this.polygonOutline) { this.polygonOutline.initialize(map) }
    if (this.fillLines) {
        for (var i = 0; i < this.fillLines.length; i++) {
            this.fillLines[i].initialize(map);
        }
    }
}

JPolygon.prototype.remove = function() {
    if (this.polygonOutline) { this.polygonOutline.remove() }
    if (this.fillLines) {
        for (var i = 0; i < this.fillLines.length; i++) {
            this.fillLines[i].remove();
        }
    }
}

JPolygon.prototype.redraw = function(force) {
    if (this.polygonOutline) { this.polygonOutline.redraw(force) }
    if (this.fillLines) {
        for (var i = 0; i < this.fillLines.length; i++) {
            this.fillLines[i].redraw(force);
        }
    }
}


JPolygon.prototype.getBounds = function() {
    return this.latlngBounds;
}

// Gets the minimum and maximum longitudes in an array of points
JPolygon.prototype.getMinMaxLng = function(a) {
    this.minLng = a[0].lng();
    this.maxLng = a[0].lng();
    for (var i = 1; i < a.length; i++) {
        if (a[i].lng() < this.minLng) {
            this.minLng = a[i].lng();
        }
        if (a[i].lng() > this.maxLng) {
            this.maxLng = a[i].lng();
        }
    }
}

// Determines if a point is inside a polygon
JPolygon.prototype.inZone = function(pt) {
    var isInZone = 0;
    var j = this.points.length-1;
    for (var i = 0; i < this.points.length; j = i++) {
        if ((((this.points[i].lat() <= pt.lat()) && (pt.lat() < this.points[j].lat())) ||
             ((this.points[j].lat() <= pt.lat()) && (pt.lat() < this.points[i].lat()))) &&
            (pt.lng() < (this.points[j].lng() - this.points[i].lng()) *
             (pt.lat() - this.points[i].lat()) / (this.points[j].lat() - this.points[i].lat()) + this.points[i].lng())) {
            isInZone++;
        }
    }
    if ((isInZone % 2) == 0) {
        return false;
    }
    else {
        return true;
    }
} 

JPolygon.prototype.getNextPreSegment = function() {
    var pt1;
    var pt2;
    do {
        this.pre1 = JPolygon.backPos(this.pre1, this.points.length);
        this.pre2 = JPolygon.backPos(this.pre2, this.points.length);
        pt1 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.pre1],this.map.getZoom());
        pt2 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.pre2],this.map.getZoom());
    } while ((pt1.y == pt2.y) && (this.pre1 != this.post2) && (this.pre2 != this.post1));
}

JPolygon.prototype.getNextPostSegment = function() {
    var pt1;
    var pt2;
    do {
        this.post1 = JPolygon.forwardPos(this.post1, this.points.length);
        this.post2 = JPolygon.forwardPos(this.post2, this.points.length);
        pt1 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.post1],this.map.getZoom());
        pt2 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.post2],this.map.getZoom());
    } while ((pt1.y <= pt2.y) && (this.pre1 != this.post2) && (this.pre2 != this.post1));
// NOTE: Larger y value means lower latitude
}

JPolygon.prototype.fillPolygon = function() {
    this.pre1 = JPolygon.posInArray(this.sortedPoints[0], this.points);
    this.pre2 = JPolygon.backPos(this.pre1, this.points.length);
    this.post1 = this.pre1;
    this.post2 = JPolygon.forwardPos(this.post1, this.points.length);

    var tpt1;
    var tpt2;
    tpt1 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.pre1],this.map.getZoom());
    tpt2 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.pre2],this.map.getZoom());
    if (tpt1.y == tpt2.y) {
        this.getNextPreSegment();
    }
    tpt1 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.post1],this.map.getZoom());
    tpt2 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.post2],this.map.getZoom());
    if (tpt1.y == tpt2.y) {
        this.getNextPostSegment();
    }
//GLog.write("pre1 = "+this.pre1);
//GLog.write("pre2 = "+this.pre2);
//GLog.write("post1 = "+this.post1);
//GLog.write("post2 = "+this.post2);
    this.preSlope = this.getLineSlope(this.pre1, this.pre2);
    this.postSlope = this.getLineSlope(this.post1, this.post2);


    // safetycount is here in case there is a bug in the code. Don't want to loop forever.
    var safetycount = 0;
    while ((this.pre1 != this.post2) && (this.pre2 != this.post1) && (safetycount < this.points.length)) {
        // fill a portion of the polygon
        safetycount++;
//GLog.write("pre1="+this.pre1+" pre2="+this.pre2+" post1="+this.post1+" post2="+this.post2);
        this.fillBetweenSegments();
    }
}



JPolygon.prototype.fillBetweenSegments = function() {
    var prePt1 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.pre1],this.map.getZoom());
    var prePt2 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.pre2],this.map.getZoom());
    var postPt1 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.post1],this.map.getZoom());
    var postPt2 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.post2],this.map.getZoom());
    var preLatDif = prePt2.y - prePt1.y;
    var preLngDif = prePt2.x - prePt1.x;
    var postLatDif = postPt2.y - postPt1.y;
    var postLngDif = postPt2.x - postPt1.x;
    var fillArray = [];
//GLog.write("pre1="+this.pre1+" pre2="+this.pre2+" post1="+this.post1+" post2="+this.post2);
//GLog.write("preLatDif = "+preLatDif);
////GLog.write("preLngDif = "+preLngDif);
//GLog.write("postLatDif = "+postLatDif);
////GLog.write("postLngDif = "+postLngDif);
//    var preLatInc = preLatDif == 0 ? 0 : preLatDif / Math.abs(preLatDif);
    var preLatInc = preLatDif == 0 ? 0 : (this.fillThickness * preLatDif) / Math.abs(preLatDif);
    var preLngOffset = 0;
//    var postLatInc = postLatDif == 0 ? 0 : postLatDif / Math.abs(postLatDif);
    var postLatInc = postLatDif == 0 ? 0 : (this.fillThickness * postLatDif) / Math.abs(postLatDif);
    var postLngOffset = 0;
    var preLatOffset = 0;
    var postLatOffset = 0;
//GLog.write("preLatInc = "+preLatInc);
//GLog.write("postLatInc = "+postLatInc);
//    while ((preLatOffset < Math.abs(preLatDif)) && (postLatOffset < Math.abs(postLatDif))) {
//        if (preLatOffset != preLatDif) {
//            if (preLatInc > 0) preLatOffset++;
//            else preLatOffset--;
    while ((preLatOffset < Math.abs(preLatDif)) && (postLatOffset < Math.abs(postLatDif))) {
        if (preLatOffset < Math.abs(preLatDif)) {
            if (preLatInc > 0) preLatOffset += this.fillThickness;
            else preLatOffset -= this.fillThickness;
        }

        if (postLatOffset < Math.abs(postLatDif)) { 
            postLatOffset = (prePt1.y + preLatOffset) - postPt1.y;
        }

//GLog.write("preLatInc= "+preLatInc+"  preLatOffset= "+preLatOffset);
//GLog.write("postLatInc= "+postLatInc+"  postLatOffset= "+postLatOffset+" postLatDif= "+postLatDif);
        if (preLngOffset < Math.abs(preLngDif)) {
            if (this.preSlope != 0) {
                preLngOffset = Math.round(preLatOffset / this.preSlope);
                if (Math.abs(preLngOffset) > Math.abs(preLngDif)) {
                    preLngOffset = preLngDif+1;
                }
            }
        }
        if (postLngOffset < Math.abs(postLngDif)) {
            if (this.postSlope != 0) {
                postLngOffset = Math.round(postLatOffset / this.postSlope);
                if (Math.abs(postLngOffset) > Math.abs(postLngDif)) {
                    postLngOffset = postLngDif;
                }
            }
        }
//GLog.write("preLngOffset= "+preLngOffset+"  preLngDif= "+preLngDif);
//GLog.write("postLngOffset= "+postLngOffset+"  postLngDif= "+postLngDif);

        tmpPrePt = new GPoint(prePt1.x + preLngOffset, prePt1.y + preLatOffset);
        tmpPostPt = new GPoint(postPt1.x + postLngOffset, tmpPrePt.y);
//GLog.write("preLatOffset = "+preLatOffset);
//GLog.write("postLatOffset = "+postLatOffset);
//GLog.write("tmpPrePt = "+tmpPrePt.toString());
//GLog.write("tmpPostPt = "+tmpPostPt.toString());
        pt1 = this.map.getCurrentMapType().getProjection().fromPixelToLatLng(tmpPrePt, this.map.getZoom(), true);
        pt2 = this.map.getCurrentMapType().getProjection().fromPixelToLatLng(tmpPostPt, this.map.getZoom(), true);

        if (((fillArray.length / 2) % 2) == 0) {
            fillArray.push(pt1);
            fillArray.push(pt2);
        }
        else {
            fillArray.push(pt2);
            fillArray.push(pt1);
        }

        if (fillArray.length >= JPolygon.maxLinePoints) {
//GLog.write("fillArray size = "+fillArray.length);
//pointcount += fillArray.length;
            var fillLine = new GPolyline(fillArray,this.color,this.fillThickness,this.fillOpacity);
            this.fillLines.push(fillLine);
            fillArray = [];
        }

        if (((preLatInc > 0) && (preLatOffset >= preLatDif)) || 
            ((preLatInc < 0) && (preLatOffset <= preLatDif))) {
            // get new point 
            this.getNextPreSegment();
            if ((this.pre1 == this.post2) || (this.pre2 == this.post1)) break;

            prePt1 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.pre1],this.map.getZoom());
            prePt2 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.pre2],this.map.getZoom());
            this.preSlope = this.getLineSlope(this.pre1, this.pre2);
            preLatDif = prePt2.y - prePt1.y;
            preLngDif = prePt2.x - prePt1.x;
            preLatInc = preLatDif == 0 ? 0 : (this.fillThickness * preLatDif) / Math.abs(preLatDif);
            preLngOffset = 0;
            preLatOffset = 0;
        }

        if (((postLatInc > 0) && (postLatOffset >= postLatDif)) || 
            ((postLatInc < 0) && (postLatOffset <= postLatDif))) {
            if ((this.points[this.pre1].lat() < this.points[this.pre2].lat()) &&
                 ((this.points[this.post1].lat() < this.points[this.pre2].lat()) || 
                  (this.points[this.post2].lat() < this.points[this.pre2].lat()))) {

            // get new point 
            this.getNextPostSegment();
            if ((this.pre1 == this.post2) || (this.pre2 == this.post1)) break;

            postPt1 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.post1],this.map.getZoom());
            postPt2 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[this.post2],this.map.getZoom());
            this.postSlope = this.getLineSlope(this.post1, this.post2);
            postLatDif = postPt2.y - postPt1.y;
            postLngDif = postPt2.x - postPt1.x;
            postLatInc = postLatDif == 0 ? 0 : (this.fillThickness *postLatDif) / Math.abs(postLatDif);
            postLngOffset = 0;
            postLatOffset = 0;
            }
        }

    }

    if (fillArray.length > 0) {
        var fillLine = new GPolyline(fillArray,this.color,this.fillThickness,this.fillOpacity);
        this.fillLines.push(fillLine);
    }

}


JPolygon.prototype.getLineSlope = function(p1, p2) {
    var pt1 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[p1],this.map.getZoom());
    var pt2 = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(this.points[p2],this.map.getZoom());
    var slope = 0;
    if (pt1.x != pt2.x) {
        slope = (pt2.y - pt1.y) / (pt2.x - pt1.x);
    }
    return slope;
}

JPolygon.printArray = function(a) {
    for (var i = 0; i < a.length; i++) {
        GLog.write("a["+i+"] = "+a[i].toString());
    }
}
   
JPolygon.posInArray = function(pt, arr) {
    var pos = 0;
 
    while (pos < arr.length) {
        if (pt == arr[pos]) {
            break;
        }
        else {
            pos++;
            if (pos == arr.length) alert("array position not found");
        }
    }
    return pos;
}

JPolygon.backPos = function(pos, len) {
    pos--;
    if (pos < 0) {
        pos = len -1;
    }
    return pos;
}

JPolygon.forwardPos = function(pos, len) {
    pos++;
    if (pos >= len) {
        pos = 0;
    }
    return pos;
}


