/******************************************************************************
 *                                                                            *
 *  Copyright (C) 2008-2011 Alexander Barth <barth.alexander@gmail.com>.      *
 *                                                                            *
 *  This program is free software: you can redistribute it and/or modify      *
 *  it under the terms of the GNU Affero General Public License as published  *
 *  by the Free Software Foundation, either version 3 of the License, or      *
 *  (at your option) any later version.                                       *
 *                                                                            *
 *  This program is distributed in the hope that it will be useful,           *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *  GNU Affero General Public License for more details.                       *
 *                                                                            *
 *  You should have received a copy of the GNU Affero General Public License  *
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.     *
 *                                                                            *
 ******************************************************************************/

// decode intervales such as 
// 2007-12-01/2008-03-20/P1D


function wms_range(s,units) {
    function decode_dim(s){
	if (units == "ISO8601") {
	    return Util.mjd(s);
	}
	else {
	    return parseFloat(s);
	}
    }

    function encode_dim(s){
	if (units == "ISO8601") {
	    return Util.gregd(s);
	}
	else {
	    return "" + s;
	}
    }


    function decode_step(s){

	if (units == "ISO8601") {
	    if (s == 'P1D') {
		return 1;
	    }
	}

	return parseFloat(s);
    }


    var list = s.split(',');
    var range = [];

    for (var i=0; i<list.length; i++) {

	// remove leading and trailing whitespaces
	list[i] = list[i].replace(/^\s+|\s+$/g,"");
	
	if (list[i].indexOf('/') == -1) {
	    range.push(list[i]);
	}
	else {
	    r = list[i].split('/');
	    var min = decode_dim(r[0]);
	    var max = decode_dim(r[1]);
	    var step = decode_step(r[2]);
	    var x = min;

	    while (x <= max) {
		range.push(encode_dim(x));
		x += step;
	    }
	}
    }

    return range;
}

// representation of a dataset
// description come from the XML section of the GetCapability request
// loadStat() performs additional request
// options:
//   wms_url ('')
//   use_proxy (null)


function LayerInfo(layer_xml,options) {
    var fmt, url;

    this.layer_xml = layer_xml;
    this.name = '<no name>';
    this.title = '<no title>';
    this.abstract_info = null;
    this.wms_url = '';
    this.use_proxy = null;
    this.cap = null;
    this.WMS_extensions = null;
    this.stat = {};
    this.figprops = null;

    var item = layer_xml.firstChild;
    
    while (item !== null) {

	// ignore empty elements
	if (item.firstChild) {
	    if (item.tagName == "Name") {
		this.name = item.firstChild.data;
            }
            else if (item.tagName == "Title") {
		this.title = item.firstChild.data;
            }
            else if (item.tagName == "Abstract") {
		this.abstract_info = item.firstChild.data;
            }
            else if (item.tagName == "MetadataURL") {
		fmt = item.getElementsByTagName("Format")[0].firstChild.data;
		url = item.getElementsByTagName("OnlineResource")[0].getAttribute("xlink:href");

		if (fmt == 'text/html') {
		    this.metadata_url = url;
		}
            }
            else if (item.tagName == "DataURL") {
		fmt = item.getElementsByTagName("Format")[0].firstChild.data;
		url = item.getElementsByTagName("OnlineResource")[0].getAttribute("xlink:href");

		if (fmt == 'application/x-netcdf') {
		    this.http_url = url;
		}
		else {
		    this.opendap_url = url;
		}
	    }		
        }
	
  	item = item.nextSibling;         
    }
    

    this.id = 'layer' + this.name;

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

LayerInfo.prototype.get_bounding_box = function() {	
    // default boundary box
    var b = [-180,-90,180,90];

    // for version 1.3.0
    var bb = Util.get_tags(this.layer_xml,'EX_GeographicBoundingBox')[0];

    if (bb) {
	b = [
	     parseFloat(bb.getElementsByTagName('westBoundLongitude')[0].firstChild.data),
	     parseFloat(bb.getElementsByTagName('southBoundLatitude')[0].firstChild.data),
	     parseFloat(bb.getElementsByTagName('eastBoundLongitude')[0].firstChild.data),
	     parseFloat(bb.getElementsByTagName('northBoundLatitude')[0].firstChild.data)];

    }
    else {
	// for version 1.1.1
	bb = Util.get_tags(this.layer_xml,'LatLonBoundingBox')[0];

	if (bb) {
	    b = [ 
		 parseFloat(bb.getAttribute("minx")),
		 parseFloat(bb.getAttribute("miny")),
		 parseFloat(bb.getAttribute("maxx")),
		 parseFloat(bb.getAttribute("maxy"))];
	}
    }

    //console.log('bb', b);
    return b;
};


LayerInfo.prototype.get_bounds = function() {
    b = this.get_bounding_box();
    return new OpenLayers.Bounds(b[0],b[1],b[2],b[3]);
};

/*

      <Dimension name="time" units="ISO8601"/>
      <Extent name="time">2007-12-01/2010-03-20/P1D</Extent>
*/
LayerInfo.prototype.get_dimension = function(name) {
    var dimensions = Util.get_tags(this.layer_xml,"Dimension");
    var units = '';
    var range = [];
    var default_value = null;
    var i, extent;

    if (this.cap.version == '1.3.0') {
	for (i=0; i < dimensions.length; i++) {
	    if (dimensions[i].getAttribute("name") == name) {
		units = dimensions[i].getAttribute("units");
		range = dimensions[i].firstChild.data;	
		//range = range.split(',');
		range = wms_range(range,units);

		default_value = dimensions[i].getAttribute('default');
		break;
	    }
	}
    }
    else {
	// version 1.1.1
	for (i=0; i < dimensions.length; i++) {
	    if (dimensions[i].getAttribute("name") == name) {
		units = dimensions[i].getAttribute("units");
		break;
	    }
	}

	extent = Util.get_tags(this.layer_xml,"Extent");
	for (i=0; i < extent.length; i++) {
	    if (extent[i].getAttribute("name") == name) {
		if (extent[i].firstChild) {
		    range = extent[i].firstChild.data;
		    //range = range.split(',');
		    range = wms_range(range,units);
		    default_value = extent[i].getAttribute('default');

		}
		break;
	    }
	}
	
    }
    return {range: range, units: units, default_value: default_value};
};

LayerInfo.prototype.get_attribution = function() {
    var attr = Util.get_tags(this.layer_xml,"Attribution")[0];
    var a = {};

    if (!attr)
	return a;

    var tmp = attr.getElementsByTagName("Title");
    if (tmp.length > 0)
	a.title = tmp[0].firstChild.data;

    tmp = attr.getElementsByTagName("OnlineResource");
    if (tmp.length > 0)
	a.url = tmp[0].getAttribute("xlink:href");

    tmp = attr.getElementsByTagName("LogoURL");
    if (tmp.length > 0) {
	tmp = tmp[0].getElementsByTagName("OnlineResource");
	if (tmp.length > 0)	   
	    a.logo = tmp[0].getAttribute("xlink:href");
    }

    return a;
};

LayerInfo.prototype.get_styles = function(){
    // TODO inherit style
    var styles = this.layer_xml.getElementsByTagName('Style');
    var s = [];
    var legend_url;

    for (j=0; j < styles.length; j++) {
	try {
	    legend_url = styles[j].getElementsByTagName("OnlineResource")[0].getAttribute("xlink:href");
	}
	catch (err) {
	    legend_url = null;
	}
	

	s[j] = {name: styles[j].getElementsByTagName("Name")[0].firstChild.data,
		title: styles[j].getElementsByTagName("Title")[0].firstChild.data,
		legend_url: legend_url
	};
    }

    return s;
};


/**
 * make a getStat request and call onload when finished
 */
// depreciated
// LayerInfo should not have a figprop method, because it prevent to use
// multiple sections (horizontal and vertical on the same map)
// loadStat is now a method of FigureProperties

LayerInfo.prototype.loadStat = function(param){
    var that = this;
    var params = {}, p;

    if (this.figprops) {
	p = this.figprops.get_layer_param();
	params = p.param;
    }

    params.layer = decodeURIComponent(this.name);
    params.request = 'GetStats';

    // add parameters
    if (param) {
	for (p in param) {
	    params[p] = param[p];
	}
    }

    var fun = function(xmldoc) {
	if (xmldoc) {
	    that.stat.xmldoc = xmldoc;
	    that.stat.xmin = parseFloat(xmldoc.getElementsByTagName("xmin")[0].firstChild.nodeValue);
	    that.stat.xmax = parseFloat(xmldoc.getElementsByTagName("xmax")[0].firstChild.nodeValue);
	    that.stat.ymin = parseFloat(xmldoc.getElementsByTagName("ymin")[0].firstChild.nodeValue);
	    that.stat.ymax = parseFloat(xmldoc.getElementsByTagName("ymax")[0].firstChild.nodeValue);

	    
	    if (that.figprops) {
		console.log('set stat ',that.title,that.name);
		that.figprops.set_stat(xmldoc);
	    }
	}

	if (that.onload) {
	    that.onload(xmldoc);
	}
    };

    Util.proxy_ajax(this.wms_url,params,null,fun,{proxy: this.use_proxy});
};


// negociate info_formats
LayerInfo.prototype.infoFormat = function(preferred_info_formats) {
    var available_info_formats = this.cap.requests.GetFeatureInfo.formats;

    for (var i in preferred_info_formats) {
	for (var j in available_info_formats) {
	    if (preferred_info_formats[i] == available_info_formats[j]) {
		return preferred_info_formats[i];
	    } 
	}
    }

    return null;
};


// performs the GetCapabilities request and creates a layer tree
// options:

//   version: 1.3.0 (default) or 1.1.1
//   WMS_extensions: null (no WMS extensions), OceanBrowser, ncWMS (not jet implemented)

function LayerTree(id,url,onclick,options) {
    this.id = id;
    this.url = url;
    this.params = '';
    this.onclick = onclick;
    this.namespace = 'layer_list_';
    this.elem = null;
    this.choose_multiple = true;
    //this.choose_multiple = false;

    this.LEAVE_CURRENT = "leave_current ui-state-active";
    this.LEAVE = "leave";
    this.TREE_HIDDEN = "tree_hidden";
    this.TREE_SHOWN = "tree_shown";
    this.PARENT_SHOWN = "parent_shown";
    this.PARENT_HIDDEN = "parent_hidden";
    this.version = '1.3.0';
    this.extra_param = {};
    this.use_proxy = null;
    this.requests = [];
    this.WMS_extensions = 'OceanBrowser';


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

    // linear list of all layers
    this.layer_infos = [];
    this.current_leave = null;
}

// methods
LayerTree.prototype.load = function() {
    var p,
        cap = document.getElementById(this.id);

    if (!this.elem) {
	this.elem = document.createElement("div");
	cap.appendChild(this.elem);
    }

    Util.removeChildNodes(this.elem);
    
    var img = document.createElement("img");
    img.src = "img/ajax-loader.gif";
    img.alt = "loading";
    this.elem.appendChild(img);
    this.elem.appendChild(document.createTextNode("Loading..."));

    // default parameters for request
    var params = {
	request: "GetCapabilities",
	service: "WMS",
	version: this.version
    };

    // over-write with extra parameter for request
    for (p in this.extra_param) {
	params[p] = this.extra_param[p];
    }

    // use proxy instead of real WMS server

    Util.proxy_ajax(this.url, params, this, this.make_tree, {proxy: this.use_proxy});
};

LayerTree.prototype.make_tree = function(xmldoc) {
    var item;
    this.xmldoc = xmldoc;

    // remove 'Loading...' message
    Util.removeChildNodes(this.elem);

    // service
    var service = xmldoc.getElementsByTagName('Service')[0];
    item = service.firstChild;
    
    while (item !== null) {
	if (item.tagName == "Title" && item.firstChild) {
	    this.title = item.firstChild.data;
        }
        else if (item.tagName == "Abstract" && item.firstChild) {
	    this.abstract_info = item.firstChild.data;
        }
  	item = item.nextSibling;         
    }

    // all requests
    var req = xmldoc.getElementsByTagName('Request')[0];
    item = req.firstChild;    
    while (item !== null) {
	if (item.tagName) {
	    var requestInfo = {formats: []};

	    var item2 = item.firstChild;
	    while (item2 !== null) {
		if (item2.tagName == "Format" && item2.firstChild) {
		    requestInfo.formats.push(item2.firstChild.data);
		}

		item2 = item2.nextSibling;
	    }

	    this.requests[item.tagName] = requestInfo;
	}
  	item = item.nextSibling;             
    }

    // get the url for GetMap requests
    var GetMap = xmldoc.getElementsByTagName('GetMap');

    if (GetMap.length > 0) {
	var get = GetMap[0].getElementsByTagName('Get');
	if (get.length > 0) {	
	    var url = get[0].getElementsByTagName("OnlineResource")[0].getAttribute("xlink:href");
	    console.log("url ",url);
	    this.url = url;	    
	}
    }

    // show title

    var title_elem = document.createElement("h2");
    title_elem.appendChild(document.createTextNode(this.title));
    this.elem.appendChild(title_elem);
    

    // layer inventory
    var root = xmldoc.getElementsByTagName('Capability')[0];
    list = this.layer_list(root);
    list.className = this.TREE_SHOWN;

    this.elem.appendChild(list);

    // simulate click
    //this.onclick(this.layers[0]);
	
};    

LayerTree.prototype.layer_list = function(root) {
    var obj = this;
    var item = root.firstChild;
    var list = document.createElement("ul");

    var i = 0;

    var vert_wms_url = null;
    if (this.WMS_extensions == 'OceanBrowser') {
	// replace wms by wms_vert for the WMS server 
	// providing vertical sections
	vert_wms_url = this.url.replace(/\/wms\b/,'/wms_vert');
    }

    while (item !== null) {
	if (item.tagName == "Layer") {
	    var Layer = new LayerInfo(item, 
				      {wms_url: this.url, 
				       vert_wms_url: vert_wms_url,
				       use_proxy: this.use_proxy,
				       WMS_extensions: this.WMS_extensions,
				       cap: this
				      }
				      );

	    var layer_num = this.layer_infos.length;
	    var list_item = document.createElement("li");

	    var sub = this.layer_list(item);
	    if (sub.childNodes.length === 0) {
		if (this.choose_multiple) {
		    var input=document.createElement("input");
		    input.id = this.namespace + "checkbox_" + layer_num;
		    input.type="checkbox";
		    list_item.appendChild(input);
		    Layer.checkbox = input;
		

		    oLabel=list_item.appendChild(document.createElement("label"));
		    oLabel.htmlFor = this.namespace + "checkbox_" + layer_num;
		    oLabel.appendChild (document.createTextNode(Layer.title));
		    input.onclick = function(event){obj.click_layer(event);} ;
		}
		else {
		    oLabel=list_item.appendChild(document.createElement("a"));
		    oLabel.appendChild (document.createTextNode(Layer.title));
		    oLabel.onclick = function(event){obj.click_layer(event);};
		}
	    }
	    else {
		var elem = document.createElement("a");
		elem.appendChild(document.createTextNode(Layer.title));
		elem.onclick = function(event){ obj.click_node(event); } ;
		list_item.appendChild(elem);
	    }

	    var span;
	    span = document.createElement("span");
	    span.appendChild(document.createTextNode(layer_num));
	    span.className = "hidden";
	    list_item.appendChild(span);

	    this.layer_infos.push(Layer);
	    
	    list.appendChild(list_item);

	    //list_item.onclick = function(event){obj.click_node(event)} ;
	    //list_item.addEventListener("click",click_node,false);

	    sub.className = this.TREE_HIDDEN;

	    if (sub.childNodes.length === 0) {
		list_item.className = this.LEAVE;
	    }
	    else {
		list_item.className = this.PARENT_HIDDEN;
		list.appendChild(sub);
	    }

	    i++;	    
	}

	item = item.nextSibling; 
    }


    //    list.addEventListener("click",click_node,false);
    return list;

};


LayerTree.prototype.click_node = function(event) {
    var tree;

    if (!event) event = window.event; // for IE
    
    elem = (event.target) ? event.target : event.srcElement;

    if (elem.tagName == 'A') {
	elem = elem.parentNode;
    }


    if (elem.className == this.PARENT_HIDDEN) {
	elem.className = this.PARENT_SHOWN;
	tree = elem.nextSibling;
	tree.className = this.TREE_SHOWN;
    }
    else if (elem.className == this.PARENT_SHOWN) {
	elem.className = this.PARENT_HIDDEN;
	tree = elem.nextSibling;
	tree.className = this.TREE_HIDDEN;
    }

    console.log("tg cN " + elem.className);
    console.log("tg " + event.target);
};

LayerTree.prototype.click_layer = function(event) { 
    if (!event) event = window.event; // for IE
    //alert("tg " + elem.className);
    //alert("tg " + event.target);

    elem = (event.target) ? event.target : event.srcElement; 

    var index = elem.parentNode.lastChild.firstChild.data;
    var Layer = this.layer_infos[index];


    if (this.choose_multiple) {
	if (elem.checked) {
	    add_layer(Layer);
	}
	else {
	    remove_layer(Layer);
	}
    }
    else {
	add_layer(Layer);
    }

};

