// $Id: srs.js,v 1.34 2009/09/22 14:24:31 goncalves Exp $

/**
 * Array of known contexts (from most specific to less specific)
 */
var gInfoterreContexts =
	new Array(
		new InfoterreContext(
			"Metropole",
			"EPSG:27582",
			new Array(
				new Array(-11, 54),
				new Array(13, 38)
			),
			new Array(
				new Array(-5.3, 49.8),
				new Array(8.7, 42.5)
			),
			[40428,1600797,1216896,2698833.8],
			"customapbuilder/contexts/locator_metropole.xml",
			new Array(
				new Array("EPSG:27582","Lambert 2 étendu"),
				new Array("IGNF:LAMB93","Lambert 93")
			)
		),
		new InfoterreContext(
			"Martinique",
			"EPSG:32620",
			new Array(
				new Array(-62, 15.5), 
				new Array(-60, 13.5)
			),
			new Array(
				new Array(-61.7, 15.4),
				new Array(-60.3, 14)
			),
			[625000, 1590000, 745000, 1830000],
			"customapbuilder/contexts/locator_martinique.xml",
			new Array(
				new Array("EPSG:32620","UTM Zone 20 Nord"),
				new Array("IGNF:MART38UTM20","Antilles 84")
			)
		),
		new InfoterreContext(
			"Guadeloupe",
			"EPSG:32620",
			new Array(
				new Array(-62.5, 17), 
				new Array(-60, 15.5)
			),
			new Array(
				new Array(-62.1, 16.66),
				new Array(-60.7, 15.63)
			),
			[625000, 1590000, 745000, 1830000],
			"customapbuilder/contexts/locator_guadeloupe.xml",
			new Array(
				new Array("EPSG:32620","UTM Zone 20 Nord"),
				new Array("IGNF:GUAD48UTM20","Antilles 84")
			)
		),
		new InfoterreContext(
			"Guyane",
			"EPSG:32622",
			new Array(
				new Array(-57, 8),
				new Array(-49, 0)
			),
			new Array(
				new Array(-55, 6),
				new Array(-51, 2)
			),
			[100000, 225000, 440000, 640000],
			"customapbuilder/contexts/locator_guyane.xml",
			new Array(
				new Array("EPSG:32622","UTM Zone 22 Nord"),
				new Array("IGNF:RGFG95","Guyane 95")
			)
		),
		new InfoterreContext(
			"Mayotte",
			"EPSG:32738",
			new Array(
				new Array(44.6, -12.3),
				new Array(45.7, -13.3)
			),
			new Array(
				new Array(44.7, -12.4),
				new Array(45.6, -13.2)
			),
			[500000, 8560000, 545000, 8605000],
			"customapbuilder/contexts/locator_mayotte.xml",
			new Array(
				new Array("EPSG:32738","UTM Zone 38 Sud"),
				new Array("IGNF:RGM04","Mayotte 2004")
			)
		),
		new InfoterreContext(
			"Reunion",
			"EPSG:32740",
			new Array(
				new Array(54, -20),
				new Array(57, -22)
			),
			new Array(
				new Array(54.93, -20.77),
				new Array(56.15, -21.51)
			),
			[310000, 7630000, 385000, 7695000],
			"customapbuilder/contexts/locator_reunion.xml",
			new Array(
				new Array("EPSG:32740","UTM Zone 40 Sud"),
				new Array("IGNF:RGR92","Réunion 92")
			)
		),
		new InfoterreContext(
			"Nouvelle-Caledonie",
			"EPSG:32758",
			new Array(
				new Array(161, -18),
				new Array(171, -25)
			),
			new Array(
				new Array(163.6, -19),
				new Array(168.5, -23)
			),
			[340000, 7485000, 840000, 7850000],
			"customapbuilder/contexts/locator_nouvelle_caledonie.xml",
			new Array(
				new Array("EPSG:32758","UTM Zone 58 Sud"),
				new Array("IGNF:RGNC","RG Nouvelle-Calédonie")
			)
		),
		new InfoterreContext(
			"Polynesie (5 Sud)",
			"EPSG:32705",
			new Array(
				new Array(-152.5, -16),
				new Array(-150.2, -17.3)
			),
			new Array(
				new Array(-151.9, -16.27),
				new Array(-150.7, -17.12)
			),
			[625000, 8125000, 723000, 8185000],
			"customapbuilder/contexts/locator_polynesie5Sud.xml",
			new Array(
				new Array("EPSG:32705","UTM Zone 05 Sud"),
				new Array("IGNF:RGPF","RG Polynésie")
			)
		),
		new InfoterreContext(
			"Polynesie (6 Sud)",
			"EPSG:32706",
			new Array(
				new Array(-150.2, -17.2),
				new Array(-148.5, -18.3)
			),
			new Array(
				new Array(-150, -17.3),
				new Array(-149, -18.1)
			),
			[190000, 8020000, 278000, 8067000],
			"customapbuilder/contexts/locator_polynesie6Sud.xml",
			new Array(
				new Array("EPSG:32706","UTM Zone 06 Sud"),
				new Array("IGNF:RGPF","RG Polynésie")
			)
		),
		new InfoterreContext(
			"Monde",
			"EPSG:4326",
			new Array(
				new Array(-180, 90),
				new Array(180, -90)
			),
			new Array(
				new Array(-180, 90),
				new Array(180, -90)
			),
			[-180,-90,180,90],
			"customapbuilder/contexts/locator_monde.xml",
			new Array(
				new Array("EPSG:4326","Latitude/Longitude")
			)
		)
	);

/**
 * Object that represents predefined contexts.
 */
function InfoterreContext(iName, iProjection, iBBox_max, iBBox_init, iBBox_wedge, iLocatorContextUrl, allowProjList) {

    // Properties
    this.name = iName; // Name of the context
    this.projection = iProjection; // Projection to use
    this.bbox_max = iBBox_max; // Maximum bounding box corresponding to that context (array of two points (ul, lr) in EPSG4326)
    this.bbox_init = iBBox_init; // Initial bounding box corresponding to that context (array of two points (ul, lr) in EPSG4326)
    this.bbox_wedge = iBBox_wedge; // Maximum bounding box corresponding to the TileCache configuration and OpenLayers max extent (array of four coordinates (minX, minY, maxX, maxY) in context SRS)
    this.locator_url = iLocatorContextUrl; // URL toward the locator context
    this.allow_proj_list = allowProjList // Array of local Projections (may be null)
    // Methods
    this.contains = InfoterreContext_contains;
}

/*
*	return tru if value is in the interval 'min, max)
*/
function between(min, max, value){
	return (value <= max && value>=min);
}
/**
 * Return true if the bbox intersects in the context bbox and if center fits in the context bbox , false otherwise.
 */
function InfoterreContext_contains(iBBox) {
	return (
			  (iBBox[0][0] <= iBBox[1][0])
			&&
			  (iBBox[1][1] <= iBBox[0][1])
			&&
			(
				between(this.bbox_max[0][0], this.bbox_max[1][0], iBBox[0][0])
				||
			  	between(this.bbox_max[0][0], this.bbox_max[1][0], iBBox[1][0])
			)
			&&
			(
				between(this.bbox_max[1][1], this.bbox_max[0][1], iBBox[1][1])
				||
			  	between(this.bbox_max[1][1], this.bbox_max[0][1], iBBox[0][1])
			)
			&&
			(
				between(this.bbox_max[0][0], this.bbox_max[1][0], (iBBox[1][0]+iBBox[0][0])/2)
				&&
			  	between(this.bbox_max[1][1], this.bbox_max[0][1], (iBBox[0][1]+iBBox[1][1])/2)
			)
		   );
}

/**
 * Return a InfoterreContext from a Bbox.
 */
function getContextFromBox(iBBox) {

	// Iterate on every context
	for(var lIndex = 0; lIndex < gInfoterreContexts.length; lIndex++) {
		var lContext = gInfoterreContexts[lIndex];
	    if (lContext.contains(iBBox)) {
	    	return lContext;
	    }
	}
	// Otherwise, return the last one (the more general).
	return gInfoterreContexts[gInfoterreContexts.length - 1];
}

/**
 * Return a InfoterreContext from a name.
 */
function getContextFromName(iName) {

	// Iterate on every context
	for(var lIndex = 0; lIndex < gInfoterreContexts.length; lIndex++) {
		var lContext = gInfoterreContexts[lIndex];
	    if (lContext.name == iName) {
	    	return lContext;
	    }
	}

	// Otherwise, return the first one (the Metropole one).
	return gInfoterreContexts[0];
}

/**
 * Return InfoterreContexts from a SRS.
 */
function getContextsFromSrs(iSrs) {

	// Iterate on every context
	var iContexts = [];
	for(var lIndex = 0; lIndex < gInfoterreContexts.length; lIndex++) {
		var lContext = gInfoterreContexts[lIndex];
	    if (lContext.projection.toUpperCase() == iSrs.toUpperCase() ) {
	    	iContexts.push(lContext);
	    }
	}
	return iContexts;
}

/**
 * Get the current InfoterreContext
 */
function getCurrentContext(){
    var bbox=config.objects.mainMap.getBoundingBox();
    var curcontext = getPreferedContext([bbox[0],bbox[3]],[bbox[2],bbox[1]]);
    if (curcontext.projection != config.objects.mainMap.getSRS()) {
    	curcontext = getContextsFromSrs(config.objects.mainMap.getSRS())[0];
    }
    return curcontext;
}

/**
 * Get the InfoterreContext that matches the current coordinates
 */
function getPreferedContext(new_ul, new_lr, epsg){
	if (!epsg) {
		epsg = config.objects.mainMap.getSRS();
	}
	var ullr = proj4jsTransform(epsg, "EPSG:4326", {x:new_ul[0],y:new_ul[1]}, {x:new_lr[0],y:new_lr[1]} )
	return getContextFromBox([[ullr.ul.x,ullr.ul.y],[ullr.lr.x,ullr.lr.y]]);
}

/**
 * Return maxExtent of InfoterreContex from a bbox , only used in CustoMapPaneOL
 */
function getMaxExtentFromContextFromBbox(context,bbox) {
	if(bbox){
	    var tmp_ullr = proj4jsTransform(context.proj.srsCode,"EPSG:4326", {x:bbox[0],y:bbox[3]}, {x:bbox[2],y:bbox[1]});
		var currentContext = getContextFromBox([[tmp_ullr.ul.x,tmp_ullr.ul.y], [tmp_ullr.lr.x,tmp_ullr.lr.y]]);
		return currentContext.bbox_wedge;
	}else{
		return null;
	}
}

/**
*	Transform coordinates of 2 points(ul and lr) from a projetcion (srcEpsg) to another (destEpsg)
*
*/
function proj4jsTransform(srcEpsg, destEpsg, ul, lr ){
	var srcProj = new Proj4js.Proj(srcEpsg);
	var destProj = new Proj4js.Proj(destEpsg);
	if(srcProj.srsCode.toUpperCase()!= destProj.srsCode.toUpperCase()){
		if(ul){
		Proj4js.transform(srcProj, destProj, ul);
		}
		if(lr){
		Proj4js.transform(srcProj, destProj, lr);
		}
	}
	return {ul:ul,lr:lr};
}

/**
* Transform the resolution from a projection unit to another
*/
function resolutiontransform(srcEpsg, destEpsg, resolution) {
	var meterPerDegree = 111118.69;
	var result = resolution;
	
	var srcProj = new Proj4js.Proj(srcEpsg);
	var destProj = new Proj4js.Proj(destEpsg);
	if (srcProj.units != destProj.units) {
		if (srcProj.units == 'm') {
			result = result / meterPerDegree;
		} else {
			result = result * meterPerDegree;
		}
	}
	return result;
}

/**
 * Zoom to the specified center with the specified zoom level
 * @param center New center
 * @param zoom New zoom level
 * @param epsg Optional EPSG code corresponding to the projection of center
 */
function itZoomToLevel(center, zoom, epsg) {
	var mbContext = config.objects.mainMap;
	if (!epsg) {
		epsg = mbContext.proj.srsCode;
	}

	// Convert the scale into resolution
	var resolution = mbContext.map.resolutions[zoom];

	// Call the real zoom method
	itZoom(center, resolution, epsg);
}

/**
 * Zoom to the specified center with the specified scale
 * @param center New center
 * @param scale New scale
 * @param epsg Optional EPSG code corresponding to the projection of center
 */
function itZoomToScale(center, scale, epsg) {
	var mbContext = config.objects.mainMap;
	if (!epsg) {
		epsg = mbContext.proj.srsCode;
	}
	
	// Get the units of the epsg
	var proj = new Proj4js.Proj(epsg);
	var units = proj.units == 'meters' ? 'm' : proj.units;
	// Convert the scale into resolution
	var baseResolution = OpenLayers.Util.getResolutionFromScale(scale,units);
	
	var finalResolution = mbContext.map.resolutions[mbContext.map.getZoomForResolution(baseResolution)];
	// Get the resolution in the good epsg if different
	if (epsg != mbContext.proj.srsCode) {
		baseResolution = resolutiontransform(epsg, mbContext.proj.srsCode, baseResolution);
		finalResolution = mbContext.map.resolutions[mbContext.map.getZoomForResolution(baseResolution)];
		finalResolution = resolutiontransform(mbContext.proj.srsCode, epsg, finalResolution);
	}	

	// Call the real zoom method
	itZoom(center, finalResolution, epsg);
}

/**
 * Zoom to the specified BBOX (expressed in the specified projection)
 * @param ul Upper left point
 * @param lr Lower right point
 * @param epsg Optional EPSG code corresponding to the projection of ul and lr
 */
function itZoomToBox(ul, lr, epsg) {
	var mbContext = config.objects.mainMap;
	if (!epsg) {
		epsg = mbContext.proj.srsCode;
	}

	var input_bounds = new OpenLayers.Bounds(ul[0], ul[1], lr[0], lr[1]);
	var queryResolution = input_bounds.getWidth() / mbContext.map.getSize().w;

	if(epsg != mbContext.proj.srsCode) {
		//passe la resolution dans le systeme de la carte pour avoir la resolution r�elle
		//puis la remets dans le syst�me demand�e afin de pouvoir zoomer dessus.
		queryResolution=  resolutiontransform(epsg,mbContext.proj.srsCode,queryResolution);
		var finalResolution = mbContext.map.resolutions[mbContext.map.getZoomForResolution(queryResolution)];
		finalResolution = resolutiontransform(mbContext.proj.srsCode,epsg,finalResolution);
	} else {
		finalResolution = queryResolution;
	}

    itZoom(input_bounds.getCenterLonLat(), finalResolution, epsg);

}

/**
 * Zoom to the specified BBOX (expressed in the specified projection)
 * @param center New center
 * @param resolution New resolution
 * @param epsg Optional EPSG code corresponding to the projection of center
 */
function itZoom(center, resolution, epsg) {
	var mbContext = config.objects.mainMap;
	if (!epsg) {
		epsg = mbContext.proj.srsCode;
	}

	// Get the real destination BBOX (because of fixed scales)
	var real_bounds = mbContext.map.calculateBounds(center, resolution);
    var real_ullr = {ul:new Array(real_bounds.left,real_bounds.top),lr:new Array(real_bounds.right,real_bounds.bottom)};

    // Get the target InfoTerre context
	var targetContext = getPreferedContext(real_ullr.ul, real_ullr.lr, epsg);

    // Get the current InfoTerre context
    var curContext = getCurrentContext();

    // No Context switching or no SRS switching
    if (curContext.name == targetContext.name || curContext.projection == targetContext.projection) {
    	if (epsg != curContext.projection) {
    		var centerTransform = {x:center.lon,y:center.lat};
    		Proj4js.transform(new Proj4js.Proj(epsg), new Proj4js.Proj(targetContext.projection), centerTransform);
    		resolution = resolutiontransform(epsg,targetContext.projection,resolution);
    		real_bounds = mbContext.map.calculateBounds(new OpenLayers.LonLat(centerTransform.x,centerTransform.y), resolution);
    	}
        mbContext.map.zoomToExtent(real_bounds);
    }
    // Context switching
    else
    {
    	//if SRS'context are different
        var target_ullr = proj4jsTransform(epsg, targetContext.projection, {x:real_ullr.ul[0],y:real_ullr.ul[1]}, {x:real_ullr.lr[0],y:real_ullr.lr[1]});
        var bbox=mbContext.doc.selectSingleNode("/wmc:ViewContext/wmc:General/wmc:BoundingBox");
        bbox.setAttribute("minx", target_ullr.ul.x);
        bbox.setAttribute("miny", target_ullr.lr.y);
        bbox.setAttribute("maxx", target_ullr.lr.x);
        bbox.setAttribute("maxy", target_ullr.ul.y);
        mbContext.setSRS(targetContext.projection);
    }
	//reload the locator if onglet is selected
	var tabCont = dijit.byId("localisationTabContainer");
	if ((isIE && tabCont.selectedChildWidget.id.indexOf('locator') > -1) || isFF) {
    	updateLocator();
    }
}

/**
 * Force the right InfoTerreContext to get loaded
 */
function firstLoadMapAndLocator(){
	if (forcedArea != null && !authenticated) {
		if (forcedArea.area != null) {
			zoomOnArea(forcedArea.area);
		} else if (forcedArea.region != null) {
			zoomOnRegion(forcedArea.region);
		} else if (forcedArea.departement != null) {
			zoomOnDepartement(forcedArea.departement);
		}
		forcedArea = null;
	} else {
		var bbox = config.objects.mainMap.getBoundingBox();
		var ul = new Array(2);
		ul[0] = bbox[0];
		ul[1] = bbox[1];
		var lr = new Array(2);
		lr[0] = bbox[2];
		lr[1] = bbox[3];
		itZoomToBox(ul, lr);
		if (forcedArea != null && authenticated) {
			displayErrorMessage(gErrors["error.zoom.authenticated"]);
		}
	}
}
