

/*
  * *** ***************************************
  * *** Korem Spider Object
  * *** ***************************************
*/
function KoremSpider(map, arrMarkers) {

    this.map    = map;
    this.markers = arrMarkers;

    this.eventMove    = null;
    this.isInfoOpened = false;

    this.deltalngPerPx   = 0;
    this.deltalatPerPx   = 0;

    /*** *************************
     *** Animation parameters
     *** ************************/
    this.nbIteration     = 8;
    this.timeOut         = 25;
    this.polyLines       = new Array();
    this.pointsPerLevel  = 5;
    this.defaultDistance = 50;

    this.expanding       = false;
    this.shrinking       = false;
    this.expanded        = false;
    this.expandedMarker  = null;

    this.isInfoOpened       = false;
    this.timeOutRemovedPoly = null;
    this.centered=false;


    /*
     *** *************************
     *** Initialization
     *** ************************* */

    this.initSpiderDelta();
    GEvent.bind(this.map, "infowindowopen",  this,this.infoOpened);
    GEvent.bind(this.map, "infowindowclose", this,this.infoClosed);
    var that=this;
    GEvent.addListener(this.map, "moveend", function () {
        that.initSpiderDelta();
    });
   


}


KoremSpider.prototype.setMarkers=function(markers){
    this.markers=markers;
}

KoremSpider.prototype.addMarker=function(marker){
    this.markers.push(marker);
}

/* 
  * *** ***************************************
  * *** Calculate the distances between
  * *** points and pixels
  * *** We must call this function each time
  * *** we make zoom
  * *** ***************************************
*/

KoremSpider.prototype.initSpiderDelta= function () {
        
    var bounds    = this.map.getBounds();

    var southWest = bounds.getSouthWest();
    var northEast = bounds.getNorthEast();
    var deltalng  = northEast.lng() - southWest.lng();
    var deltalat  = northEast.lat() - southWest.lat();

    var width  = this.map.getSize().width;
    var height = this.map.getSize().height;

    this.deltalngPerPx = deltalng / width;
    this.deltalatPerPx = deltalat / height;
}


KoremSpider.prototype.infoOpened = function() {

    this.isInfoOpened = true;
}

KoremSpider.prototype.infoClosed = function() {

    // Si on fait ça pour regler le probleme
    // c'est le bordell
    // en fait, c'est l,evenemento de mouse move
    // qui ferme le spider
    // this.isInfoOpened=false; *** bordell

    if(this.expandedMarker){
        this.animShrink(this.expandedMarker, this.nbIteration)
    }
    this.isInfoOpened = false;
}

KoremSpider.prototype.showMarkerInfo = function(marker, zoom) {

    // On doit faire ça dans le cas special
    // ( this.isInfoOpened = false )
    // pour que le spider puisse se fermer
    // si on ne met pas ça le spider ne se ferme jamais
    this.isInfoOpened = false;

    this.map.closeInfoWindow();

    setTimeout ( function() {
        this.map.setCenter(marker.getLatLng(), zoom);
        marker.openInfoWindow(marker.textInfoWindow);
    }, 1000 );
//  ^ on doit donner temps sufiçant pour fermer le spider
//  si non va rester ouverte
}



/*
  * *** ***************************************
  * *** Verify if one marker is near other
  * *** for create un group of spider
  * *** ***************************************
*/


KoremSpider.prototype.isTouching =function (currentPoint, pointTesting) {
  
    var point1=this.getIconBounds(currentPoint);
    var point2=this.getIconBounds(pointTesting);

    var p1Icon=currentPoint.getIcon();
    var p2Icon=pointTesting.getIcon();
    
    var xOverlap = this.valueInRange(point1.x+p1Icon.correction.x, point2.x+p2Icon.correction.x, point2.x+p2Icon.correction.x + p2Icon.w)
    || this.valueInRange(point2.x+p2Icon.correction.x, point1.x+p1Icon.correction.x, point1.x+p1Icon.correction.x +  p1Icon.w);
    var yOverlap = this.valueInRange(point1.y-p1Icon.correction.y, point2.y-p2Icon.correction.y, point2.y-p2Icon.correction.y + p2Icon.h)
    || this.valueInRange(point2.y-p2Icon.correction.y, point1.y-p1Icon.correction.y, point1.y-p1Icon.correction.y + p1Icon.h);


    return xOverlap && yOverlap;

}

KoremSpider.prototype.valueInRange = function(value,min, max) {
    return (value >= min) && (value <= max);
}


KoremSpider.prototype.getIconBounds= function(marker)
{
    var iconSize, iconAnchorPointOffset;
    iconSize=marker.getIcon().iconSize;
    iconAnchorPointOffset=marker.getIcon().iconAnchor;
    var x=this.map.fromLatLngToDivPixel(marker.getLatLng()).x-iconAnchorPointOffset.x;
    var y=this.map.fromLatLngToDivPixel(marker.getLatLng()).y-iconAnchorPointOffset.y;
    return {
        "x":x,
        "y":y,
        "h":iconSize.height,
        "w":iconSize.width
        };

}

/*
  * *** ***************************************
  * *** Create spider group
  * *** with all the markers in the array
  * *** ***************************************
*/
KoremSpider.prototype.createSpiderGroups = function(currentPoint) {


    try{
        currentPoint.group=new Array();
    
            
        for(var i = 0; i < this.markers.length; i++){
            var pointTesting = this.markers[i];
                   
            if(this.isTouching(currentPoint, pointTesting)){
                currentPoint.group.push(pointTesting);
            }
        }

    }catch(err){
        console.debug(err)

    }
}


/*
  * *** ***************************************
  * *** Open one group of the spider
  * *** when a marker is clicked
  * ***
  * *** If there is no group for spider
  * *** the infowindow will be opened
  * *** ***************************************
*/
KoremSpider.prototype.openSpiderGroup = function (clickedMarker){

    //this.animCircleSpider(clickedMarker);
    this.animSpiderDispersion(clickedMarker);
    this.animCreateMovementSteps(clickedMarker);

    // **** Starts Expanding
    var that = this;
    var startIteration = 0;
    setTimeout(function(){
        if (this.eventMove) {
            GEvent.removeListener(that.eventMove);
        }
        that.animExpand(clickedMarker,startIteration)
    }, 1);
}

/**
  * *********************************************
  * *** Calculate the shape of the spider
  * ***
  * *********************************************
 */
KoremSpider.prototype.animCircleSpider = function (clickedMarker) {

    var size   = clickedMarker.group.length;
    var result = this.pointsPerLevel * Math.pow(2, 0);

    var cpt             = 0;
    var offset          = 0;

    var defaultDistance = 50;
    var distance        = 0;
    var point;

    for(var i = 0; i < size; i++)  {
        var diffAngle = result - cpt;
        diffAngle = 360 / diffAngle;
        distance = defaultDistance * (i+1);

        var j = 0;

        for(; cpt < result && cpt < size; cpt++){

            point = clickedMarker.group[cpt];

            var angle  = (diffAngle * j) + offset;
            var deltaX = this.animGetDeltaX(angle, distance);
            var deltaY = this.animGetDeltaY(angle, distance);

            point.newX = deltaX * this.deltalngPerPx;
            point.newY = deltaY * this.deltalatPerPx;

            j++;
            clickedMarker.group[cpt] = point;
        }

        if ( i > 0 ) {
            offset = diffAngle / 4;
        } else {
            offset = diffAngle / 2;
        }
        result = this.pointsPerLevel * Math.pow(2, i+1);
    }
}


KoremSpider.prototype.splitPoints=function(currentPoint, middleX, middleY){
    var pointsTakenX = new Array();
    var pointsTakenY = new Array();
    var pointsToMove = new Array();
    var sizeTaken = 0;
    var size = currentPoint.group.length;
    var cpt = 0;
    var distance = 0;

    pointsTakenX[0] = middleX;
    pointsTakenY[0] = middleY;
    for(var i = 0; i < size; i++){
        sizeTaken = pointsTakenX.length;
        for(var k = 0; k < sizeTaken; k++){
            if(currentPoint.group[i].getLatLng().lng() == pointsTakenX[k] && currentPoint.group[i].getLatLng().lat() == pointsTakenY[k]){
                pointsToMove[cpt] = currentPoint.group[i];
                pointsToMove[cpt].groupIndex = i;
                cpt++;
            }
        }
        pointsTakenX[i+1] = currentPoint.group[i].getLatLng().lng();
        pointsTakenY[i+1] = currentPoint.group[i].getLatLng().lat();
        currentPoint.group[i].offsetX = currentPoint.group[i].getLatLng().lng();
        currentPoint.group[i].offsetY = currentPoint.group[i].getLatLng().lat();

    }
    sizeTaken = pointsTakenX.length;
    if(pointsToMove.length > 0){
        var diffAngle = 360 / pointsToMove.length;
        for(i = 0; i < pointsToMove.length; i++){
            var angle = diffAngle * i;
            distance = 20;
            var invalidPosition = true;
            var newX = 0;
            var newY = 0;
            while(invalidPosition){
                var deltaX = getDeltaX(angle, distance);
                var deltaY = getDeltaY(angle, distance);
                var pointMoved = false;
                newX = deltaX * this.deltalngPerPx + pointsToMove[i].getLatLng().lng();
                newY = deltaY * this.deltalatPerPx + pointsToMove[i].getLatLng().lat();
                for(k = 0; k < sizeTaken; k++){
                    invalidPosition = false;
                    if(newX == pointsTakenX[k] && newY == pointsTakenY[k]){
                        invalidPosition = true;
                    }
                    if(pointsToMove[i].getLatLng().lng() == pointsTakenX[k] && pointsToMove[i].getLatLng().lat() == pointsTakenY[k] && !pointMoved){
                        pointsTakenX[k] = newX;
                        pointsTakenY[k] = newY;
                        pointMoved = true;
                    }
                }
                distance++;
            }
            currentPoint.group[pointsToMove[i].groupIndex].offsetX = newX;
            currentPoint.group[pointsToMove[i].groupIndex].offsetY = newY;
        }
    }
    return currentPoint;
}

KoremSpider.prototype.animSpiderDispersion =function (currentPoint){
    /*var size = currentPoint.group.length;
    var middleX = 0;
    var middleY = 0;
    var cpt = 0;
    var distance = 0;
    var deltaDistance = 0;
    var pointTest;
    var fallback = false;
    for(; cpt < size; cpt++){
        middleX = middleX + currentPoint.group[cpt].getLatLng().lng();
        middleY = middleY + currentPoint.group[cpt].getLatLng().lat();
    }
    middleX = middleX / cpt;
    middleY = middleY / cpt;

    currentPoint = this.splitPoints(currentPoint, middleX, middleY);
    cpt = 0;
    var result = this.pointsPerLevel * Math.pow(2, 0);
    for(var i = 0; cpt < size; i++){
        distance = this.defaultDistance * (i+1);
        for(; cpt < result && cpt < size; cpt++){
            var point = currentPoint.group[cpt];
            var deltaX = point.offsetX - middleX;
            var deltaY = point.offsetY - middleY;
            deltaDistance = Math.sqrt(Math.pow(deltaX/this.deltalngPerPx, 2) + Math.pow(deltaY/this.deltalatPerPx, 2));
            point.newX = distance / deltaDistance * deltaX;
            point.newY = distance / deltaDistance * deltaY;
            
            currentPoint.group[cpt] = point;
        }
        result = this.pointsPerLevel * Math.pow(2, i+1);
    }

    //We check if the points are correctly dispersed if not we fall back on the circle algorythm

    for(i = 0; i < size; i++){
        pointTest = currentPoint.group[i];
        for(var j = 0; j < size; j++){
            if(pointTest.indexPoint != currentPoint.group[j].indexPoint && isTouching(pointTest, currentPoint.group[j], true)){
                fallback = true;
            }
        }
    }
    if(fallback){*/
    this.animCircleSpider(currentPoint)
    //}
    return currentPoint;
}




// *** *******************
// *** Get Delta X
// *** *******************
KoremSpider.prototype.animGetDeltaX = function (angle, distance) {
    if(angle == 0) {
        return distance;
    }
    if(angle % 180 == 0) {
        return -distance;
    }
    if(angle % 90 == 0) {
        return 0;
    }
    var quadrant = Math.floor(angle / 90);
    var deltaX = 0.0;
    if(quadrant == 1 || quadrant == 3) {
        deltaX = Math.sin((angle % 90) * Math.PI/180) * distance;
    } else {
        deltaX = Math.cos((angle % 90) * Math.PI/180) * distance;
    }
    if(quadrant == 1 || quadrant == 2) {
        deltaX = -deltaX;
    }
    return deltaX;
}

// *** *******************
// *** Get Delta Y
// *** *******************
KoremSpider.prototype.animGetDeltaY = function  (angle, distance) {
    if(angle == 0 || angle == 180){
        return 0;
    }
    if(angle % 90 == 0 && angle % 180 != 0 && angle % 270 != 0) {
        return distance;
    }
    if(angle % 180 != 0 && angle % 270 == 0) {
        return -distance;
    }
    var quadrant = Math.floor(angle / 90);
    var deltaY = 0.0;
    if(quadrant == 1 || quadrant == 3) {
        deltaY = Math.cos((angle % 90) * Math.PI/180) * distance;
    } else {
        deltaY = Math.sin((angle % 90) * Math.PI/180) * distance;
    }
    if(quadrant == 2 || quadrant == 3) {
        deltaY = -deltaY;
    }
    return deltaY;
}


/* 
 * *********************************************
 * *** Calculate the coordinates for
 * *** the animation of each
 * *** marker for make the movements
 * *********************************************
 */
KoremSpider.prototype.animCreateMovementSteps = function (clickedMarker){
    // *** For create mouse area
    var bounds = new GLatLngBounds ();
    var groupBounds = new GLatLngBounds ();


    // **** Helpers for the steps
    var spiderMember;
    var lng = 0;
    var lat = 0;
    for(var i = 0; i < clickedMarker.group.length; i++){

        spiderMember = clickedMarker.group[i];
  
        spiderMember.latLngArr = new Array();
        spiderMember.initialLatLng=spiderMember.getLatLng() ;
        
        bounds.extend(spiderMember.initialLatLng);

        var j = 0;
        for(; j <= this.nbIteration; j++) {
            lng = spiderMember.getLatLng().lng() + (spiderMember.newX * j / this.nbIteration);
            lat = spiderMember.getLatLng().lat() + (spiderMember.newY * j / this.nbIteration);
        
            // *** Add the steps coordinates
            spiderMember.latLngArr[j] = new GLatLng(lat, lng);
        }

        bounds.extend(spiderMember.latLngArr[spiderMember.latLngArr.length-1]);
        if(this.centered){
            groupBounds.extend(spiderMember.latLngArr[0]);
            spiderMember.groupBounds=groupBounds;
        }

        clickedMarker.group[i] = spiderMember;
    }

    this.createMouseArea(clickedMarker, bounds);
}


/*
 * *********************************************
 * *** Starts the open animation for a
 * *** clicked marker
 * *** It uses a timeout for make the animation
 * *** and it calls itself for continue the
 * *** expansion
 * *********************************************
 */
KoremSpider.prototype.animExpand = function(clickedMarker,iteration){
 
    var that = this;

    if(iteration == 1 ) {

        this.expanded      = true;
        this.expandedMarker= clickedMarker;

        clearTimeout(this.timeOutRemovedPoly);
    }


    if( iteration >= this.nbIteration){

        this.eventMove = GEvent.addListener(this.map, 'mousemove', function(latlng){
            that.isMouseInSpider(clickedMarker, latlng);
        });
        this.expanding=false;

        return;
    }

    var currentPoint;
    for(var i = 0; i < clickedMarker.group.length; i++ ){
        currentPoint = clickedMarker.group[i];

        if(iteration==1){
            clickedMarker.expanded=true;
            currentPoint.expanded=true;
        }

        if(iteration==this.nbIteration-1){
            var polyLine;

            if(this.centered){
                polyLine = new GPolyline([currentPoint.groupBounds.getCenter(),
                    currentPoint.latLngArr[this.nbIteration]],
                    "#2763a2", 1, 0.8);

                this.polyLines.push(polyLine);
           
                this.map.addOverlay(polyLine);


            }else{

                polyLine = new GPolyline([currentPoint.initialLatLng,
                    currentPoint.latLngArr[this.nbIteration]],
                    "#2763a2", 1, 0.8);
                this.polyLines.push(polyLine);
                this.map.addOverlay(polyLine);

            }
    

        }

        currentPoint.setLatLng(currentPoint.latLngArr[iteration + 1]);

    }


    // After first expand iteration hide other martkers
    if(iteration==1){
        this.map.closeInfoWindow();
        try {

            for(var j=0; j < this.markers.length;j++) {
                if(!this.markers[j].expanded) {
                    this.markers[j].hide();
                }
            }

        } catch(err) {
            console.debug(err);
        }

    }

    setTimeout(function() {
        that.animExpand(clickedMarker, iteration + 1 )
    }, that.timeOut);

}


KoremSpider.prototype.clearSpidered = function(){
    // try{
    if(!this.expanded ){
        return;
    }

    var currentPoint;
    for (var j=0; j < this.markers.length;j++) {
        if(! this.markers[j].expanded) {
            try{
                this.markers[j].show();
            }catch(e){
            }
        }
    }
    

    for(var i = this.expandedMarker.group.length - 1; i >= 0; i--){
        try{
            currentPoint = this.expandedMarker.group[i];
            currentPoint.setLatLng(currentPoint.latLngArr[0]);
            currentPoint.expanded=false;
        }catch(e){
            
        }

    }
    for (var k = 0; k < this.polyLines.length; k ++) {
        this.map.removeOverlay(this.polyLines[k]);
    }
    this.polyLines = new Array();

    this.expandedMarker=false;
    if (this.eventMove) {
        GEvent.removeListener(this.eventMove);
    }

    this.shrinking         = false;
    this.expanded          = false;
/* }catch(e){
       console.debug(e);
    }*/
}



/*
 * *********************************************
 * *** Starts the closing animation when the
 * *** mouse is out the area of the spider
 * *** It uses a timeout for make the animation
 * *** and it calls itself for continue the
 * *** closing
 * *********************************************
 */
KoremSpider.prototype.animShrink = function(clickedMarker, iteration){

    if(this.isInfoOpened){
        return;
    }

    if (this.eventMove) {
        GEvent.removeListener(this.eventMove);
    }

    this.shrinking = true;
    if( iteration < 0 ) {
        clickedMarker.expanded = false
        this.shrinking         = false;
        this.expanded          = false;
        return;
    }

    // *** In the last iteration, show
    // *** the marakers that are hidden
    if(iteration == 0) {

        this.expandedMarker=false;
        try{
            for(var j=0; j < this.markers.length;j++){
                if(! this.markers[j].expanded){

                    this.markers[j].show();
                }
            }
        }catch(err){
            console.debug(err);
        }
    }

    var currentPoint;

    for(var i = clickedMarker.group.length - 1; i >= 0; i--){
        currentPoint = clickedMarker.group[i];
        currentPoint.setLatLng(currentPoint.latLngArr[iteration]);

        if(iteration==1){
            currentPoint.expanded=false;
        }

    }

    if(iteration == this.nbIteration && (this.polyLines.length > 1) ) {

        for (var k = 0; k < this.polyLines.length; k ++) {
            this.map.removeOverlay(this.polyLines[k]);
        }

        this.polyLines = new Array();
    }

    var that = this;
    setTimeout(function() {
        that.animShrink(clickedMarker, iteration-1)
    }, this.timeOut);
}


/*
 * *********************************************
 * *** Calculate the area of the open spider
 * *** where the mouse will can move in
 * *** If the mouse leave this area the spider
 * *** will close itself
 * ***
 * *** Uses the Google Bounds object for
 * *** make the area
 * *********************************************
 */
KoremSpider.prototype.createMouseArea = function (clickedMarker,theBounds){

    var bounds;
    var polygonBuffer   = 10;
    bounds = theBounds;

    var height = clickedMarker.getIcon().iconSize.height;
    var width  = clickedMarker.getIcon().iconSize.width;
    var sw  = bounds.getSouthWest();
    var ne  = bounds.getNorthEast();

    var maxLat = (ne.lat() +   height * this.deltalatPerPx +  polygonBuffer
        * this.deltalatPerPx);

    var minLat = sw.lat()  -   polygonBuffer * this.deltalatPerPx;

    var maxLng = (ne.lng() +   width * this.deltalngPerPx / 2 + polygonBuffer
        * this.deltalatPerPx );

    var minLng = sw.lng()  - ( width * this.deltalngPerPx / 2 + polygonBuffer
        * this.deltalatPerPx);


    var polygon = new GPolygon([
        new GLatLng(minLat, minLng),
        new GLatLng(minLat, maxLng),
        new GLatLng(maxLat, maxLng),
        new GLatLng(maxLat, minLng),
        new GLatLng(minLat, minLng)
        ], "#FFFFFF", 1, 1, "#FFFFFF", 1);

    clickedMarker.spiderMouseArea = polygon;

// *** Add this only for debbuging
// map_.addOverlay(clickedMarker.spiderMouseArea);
}


KoremSpider.prototype.isMouseInSpider = function (clickedMarker, latlng){

    //todo:if info window opened(rename)
    if (this.shrinking || this.expanding) {
        return;
    }

    var maxLat = clickedMarker.spiderMouseArea.getBounds().getNorthEast().lat();
    var maxLng = clickedMarker.spiderMouseArea.getBounds().getNorthEast().lng();
    var minLat = clickedMarker.spiderMouseArea.getBounds().getSouthWest().lat();
    var minLng = clickedMarker.spiderMouseArea.getBounds().getSouthWest().lng();

    if(latlng.lat() < minLat || latlng.lat() > maxLat || latlng.lng() < minLng || latlng.lng() > maxLng)
    {
        var that = this;
        setTimeout(function(){
            that.animShrink(clickedMarker, that.nbIteration);
        }, 1);

    // for debug purpose to check
    // spider mouse area
    // map_.removeOverlay ( clickedMarker.spiderMouseArea );

    }



}

GMarker.prototype.startSpider = function(spider){

    var spider=spider;

    if(!spider.expandedMarker  ){

        spider.createSpiderGroups(this);
    }


    if(this.group && this.group.length>1 && !spider.expanded  ){
        spider.openSpiderGroup(this);

    }else{
        this.parent.showMiniFiche()
    //this.openInfoWindow (this.textInfoWindow);
    }



} 

