
EarthGL.BaseSection = EarthGL.Class({
	opacity: 1,
	// 3D model
	model: null, 
	map: null,

	initialize: function() {
	},

	setOpacity: function(op) {
	    this.opacity = op;
	}
    });


/**
 * @class EarthGL.Layer setups a layer (horizontal section) that can be added to a map
 * @param {String} name name of layer
 * @param {String} url URL of the WMS server
 * @param {Object} param WMS parameters
 * @param {Object} param.layers WMS layer parameter
 * @param {Object} param.styles WMS styles parameter
 * @param {Object} [param.elevation=0] WMS elevation parameter
 * @param {Object} options Optional properties for configuring the layer
 * @param {Object} [options.isBaseLayer=false] true if this given layer is a base-layer
 * @constructor
 */

EarthGL.Layer = EarthGL.Class(EarthGL.BaseSection,{
	CLASS_NAME: 'EarthGL.Layer',

	initialize: function(name,url,param,options) {
	    this.name = name;
	    this.url = url;
	    this.textureWidth = 1024;
	    this.textureHeight = 512;
	    this.isBaseLayer = false;

	    this.events = new EarthGL.Events(this);

	    // default WMS parameters
	    this.param = {
		layers: this.name,
		styles: '',	    
		format: 'image/png',
		service: 'WMS',
		version: '1.3.0',
		request: 'GetMap',
		exceptions: 'application/vnd.ogc.se_inimage',
		srs:'EPSG:4326',
		bbox:'-180,-90,180,90',
		width: this.textureWidth,
		height: this.textureHeight };

	    // overwrite default options in param
	    var p;
	    if (param) {
		for (p in param) {
		    this.param[p] = param[p];
		}
	    }
    
	    // overwrite default options
	    if (options) {
		for (p in options) {
		    this[p] = options[p];
		}
	    }

	    // do not request transparency for base layer
	    if (!this.isBaseLayer) {
		param.transparent = true;
	    }

	},

/** @memberOf EarthGL.Layer */
	needReload: function() {
	    // TODO
	    var lon = this.map.bbox.lon;
	    var lat = this.map.bbox.lat;
	    var mlon = this.model.lon;
	    var mlat = this.model.lat;

	    // check if zoomlevel is inadequate
	    this.maxratio = 2;
	    //this.maxratio = 1.2;
	    var ratio;

	    //return false;

	    // model / visible in map
	    ratio = (mlat[1]-mlat[0])/(lat[1]-lat[0]);
	    console.log('lat ratio',ratio);
	    
	    if (ratio > this.maxratio) {
		console.log('lat ref');
		return true;
	    }
			 
	    ratio = (mlon[1]-mlon[0])/(lon[1]-lon[0]);
	    console.log('lon ratio',ratio);

	    if (ratio > this.maxratio) {
		console.log('lon ref');
		return true;
	    }

	    // if (mlon[0] == -180 ||
	    // 	mlon[1] == 180 ||
	    // 	mlat[0] == -90 || 
	    // 	mlat[1] == 90) {

	    // 	return false;
	    // }

	    // check if outside

	    if (!EarthGL.Util.withininter(mlon[0],mlon[1],360,lon[0],lon[1]) ||
		mlat[0] > lat[0] || mlat[1] < lat[1]) {

		//console.log('outside');
		//console.log('we want',lon,lat);
		//console.log('we have',mlon,mlat);
		return true;
	    }

	    return false;

	},
	
	makeModel: function(lon,lat) {

	    // delete current model
	    // maybe this speeds-up carbage collection
	    //this.model = null;

	    //lon = [0, 90];
	    //lat = [0, 90];

	    lon = lon || [-180, 180];
	    lat = lat || [-90, 90];
	    
	    //console.log('make model',lon,lat);
	    // 0 is default elevation
	    var zhsec = 0;

	    if ('elevation' in this.param) {
		zhsec = this.param.elevation;
		if (typeof zhsec == 'string') {
		    zhsec = parseFloat(zhsec);
		}
	    }

	    //lon = [0, 90];
	    //lat = [-90, 90];
	    //lat = [20, 50];

	    var newParams = {bbox: lon[0] + ',' + lat[0] + ',' + lon[1] + ',' + lat[1]};
	    
	    var sphere = {};
	    var url = this.getFullRequestString(newParams);
	    //console.log('this.getFullRequestString ',url);
	    var that = this;
	    var map = this.map;

	    if (this.isBaseLayer) {
		//url = this.getFullRequestString(); // FIXME temporarily ignore lon and lat

		this.model = EarthGL.Util.makeEarthTex(map.gl, url,
						       map.topo,
						       function() {
							   // update scene
							   that.events.triggerEvent('loadend',{});
							   map.draw();
						       },
						       lon,lat
						       //[-180,180],[-90,90] // FIXME
						       );
	    }
	    else {
		sphere = EarthGL.Util.makeSphereEarth(map.gl, zhsec/map.EarthRadius, map.nLat-1, map.nLon-1, null, lon,lat);
		sphere.loaded = false;
		sphere.z = zhsec;
		this.model = sphere;

		var redraw = function(model) {
		    return function() {
			// update scene
			that.events.triggerEvent('loadend',{});
			model.loaded = true;
			map.draw();
		    };
		}(this.model);

		this.model.texture = EarthGL.Util.loadImageTexture(map.gl, url, redraw);
	    }

	    this.model.lon = lon;
	    this.model.lat = lat;
	},

	setMap: function(map) {
	    // add reference back the map
	    this.map = map;

	    this.makeModel();

	},

	mergeNewParams: function(param) {
	    for (var p in param) {
		this.param[p] = param[p];
	    }
	},

	getFullRequestString: function(newParams) {
	    var p = {};
	    p = EarthGL.Util.merge(p,this.param);
	    p = EarthGL.Util.merge(p,newParams);
    
	    return EarthGL.Util.append_param(this.url,p);
	},


	// dummy functions
	getZIndex: function() {
	    return 1000;
	},

	// dummy functions
	setZIndex : function(zindex) {
	},

	_draw:  function(program) {	    
	    if (!this.model || !this.model.loaded) {
		//console.log('not loaded ',this.model);
		if (this.model) {
		    //console.log('not loaded ',this.model.loaded);
		}
		return;
	    }

	    
	    if (this.needReload()) {
		// load new model first

		// get a slitghly larger domain
		var bbox = this.map.getBBOX(1.);
		this.makeModel(bbox.lon,bbox.lat);
		//return;
	    }
	    

	    var gl = this.map.gl;

	    var texture = this.model.texture;

	    //console.log('sphere.z ',this.model.z);

	    gl.useProgram(program);
	    gl.uniform1f(gl.getUniformLocation(program, "vscale"), this.map.vscale);
	    gl.uniform1f(gl.getUniformLocation(program, "opacity"), this.opacity);

	    // first enable all 3 atributes
	    gl.enableVertexAttribArray(0);
	    gl.enableVertexAttribArray(1);
	    gl.enableVertexAttribArray(2);

	    gl.bindBuffer(gl.ARRAY_BUFFER, this.model.texCoordObject);
	    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);

	    gl.bindBuffer(gl.ARRAY_BUFFER, this.model.elevationObject);
	    gl.vertexAttribPointer(1, 3, gl.FLOAT, false, 0, 0);

	    gl.bindBuffer(gl.ARRAY_BUFFER, this.model.gradhObject);
	    gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 0, 0);


	    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.model.indexObject);

	    this.map.mvpMatrix.setUniform(gl, gl.getUniformLocation(gl.program, "u_modelViewProjMatrix"), false);
	    this.map.normalMatrix.setUniform(gl, gl.getUniformLocation(gl.program, "u_normalMatrix"), false);


	    gl.bindTexture(gl.TEXTURE_2D, texture);
	    gl.drawElements(gl.TRIANGLES, this.model.numIndices, this.model.indexType, 0);
	},


	/**
	 * destroys a layer and removes it from the map
	 */

	destroy: function() {
	    if (this.map) {
		this.map.removeLayer(this);
	    }
	}
    });


/**
 * EarthGL.Section defines a vertical section that can be added to a map
 * @param {String} name name of layer
 * @param {String} url URL of the WMS server
 * @param {Number[]} slon array of values representing the longitudes the points define the section
 * @param {Number[]} slat array of values representing the latitudes the points define the section
 * @param {Number[]} h depth range
 * @param {Object} param WMS parameters
 * @param {Object} param.layers WMS layer parameter
 * @param {Object} param.styles WMS styles parameter
 * @param {Object} options Optional properties for configuring the layer
 * @constructor
 */

EarthGL.Section = EarthGL.Class(EarthGL.BaseSection,{
	CLASS_NAME: 'EarthGL.Section',

	initialize: function(name,url,slon,slat,h,param,options) {
	    this.name = name;
	    this.url = url;
	    this.slon = slon;
	    this.slat = slat;
	    this.h = h;

	    var sd = EarthGL.Util.secdistance(slon,slat);
	    //console.log('maxsd 1 ' + sd[sd.length-1]);

	    var ratio = 0.01;
	    var bbox = [0,h[0],sd[sd.length-1]/ratio,h[1]];

	    var section = EarthGL.Util.encodesec(slon,slat);

	    // default WMS parameters
	    this.param = {
		layers: this.name,
		ratio: ratio,
		section: section,
		format: 'image/png',
		service: 'WMS',
		version: '1.3.0',
		request: 'GetMap',
		exceptions: 'application/vnd.ogc.se_inimage',
		srs:'EPSG:4326',
		bbox: bbox,
		width: 512,
		height: 256};

	    // overwrite default options in param
	    if (param) {
		for (var p in param) {
		    this.param[p] = param[p];
		}
	    }
    
	    // overwrite default options
	    if (options) {
		for (var p in options) {
		    this[p] = options[p];
		}
	    }
    
	},


	getFullRequestString: function() {
	    var p = this.param;
	    //console.log('sec url ',Util.append_param(this.url,p));
	    //console.log('sec bbox',p.bbox);
	    return EarthGL.Util.append_param(this.url,p);
	},


	setMap: function(map) {
	    // add reference back the map
	    this.map = map;

	    var url_sec = this.getFullRequestString();

	    //console.log('url_sec ' + url_sec);

	    var h0 = this.h[0]/map.EarthRadius;
	    var h1 = this.h[1]/map.EarthRadius;

	    var sec = EarthGL.Util.makeSection(map.gl,this.slon,this.slat,[h0,h1]);
    
	    sec.texture = EarthGL.Util.loadImageTexture(map.gl, url_sec);
	    this.model = sec;
	},

	_draw: function(program) {
	    var gl = this.map.gl;
    
	    gl.useProgram(program);
	    gl.uniform1f(gl.getUniformLocation(program, "vscale"), this.map.vscale);
	    gl.uniform1f(gl.getUniformLocation(program, "opacity"), this.opacity);

	    // Enable all of the vertex attribute arrays.
	    gl.enableVertexAttribArray(0);
	    gl.enableVertexAttribArray(1);
	    gl.enableVertexAttribArray(2);
 
	    // Set up all the vertex attributes for vertices, normals and texCoords
	    gl.bindBuffer(gl.ARRAY_BUFFER, this.model.normalObject);
	    gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
 
	    gl.bindBuffer(gl.ARRAY_BUFFER, this.model.texCoordObject);
	    gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0);
 
	    gl.bindBuffer(gl.ARRAY_BUFFER, this.model.vertexObject);
	    gl.vertexAttribPointer(2, 3, gl.FLOAT, false, 0, 0);
 
	    // Bind the index array
	    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.model.indexObject);

	    this.map.mvpMatrix.setUniform(gl, gl.getUniformLocation(program, "u_modelViewProjMatrix"), false);
	    this.map.normalMatrix.setUniform(gl, gl.getUniformLocation(program, "u_normalMatrix"), false);

	    // Bind the texture to use
	    gl.bindTexture(gl.TEXTURE_2D, this.model.texture);

	    // Draw the section
	    gl.drawElements(gl.TRIANGLES, this.model.numIndices, gl.UNSIGNED_SHORT, 0);

	}

    });

