﻿/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
* license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
* full text of the license. */

/**
* @requires OpenLayers/Format/KML.js
* @requires OpenLayers/Format/XML.js
* @requires OpenLayers/Feature/Vector.js
* @requires OpenLayers/Geometry/Point.js
* @requires OpenLayers/Geometry/LineString.js
* @requires OpenLayers/Geometry/Polygon.js
* @requires OpenLayers/Geometry/Collection.js
*/

/**
* Class: OpenLayers.Format.AdvancedKML
* Read/Wite KML. Create a new instance with the <OpenLayers.Format.AdvancedKML>
*     constructor. 
* 
* Inherits from:
*  - <OpenLayers.Format.KML>
*/
OpenLayers.Format.AdvancedKML = OpenLayers.Class(OpenLayers.Format.KML, {

    /**
    * APIProperty: kmlns
    * {String} KML Namespace to use. Defaults to 2.2 namespace.
    */
    kmlns: "http://earth.google.com/kml/2.2",

    folderList: [],

    kmlDocumentName: null,

    /**
    * Method: parseData
    * Read data from a string, and return a list of features. 
    * 
    * Parameters: 
    * data    - {String} or {DOMElement} data to read/parse.
    * options - {Object} Hash of options
    *
    * Returns:
    * {Array(<OpenLayers.Feature.Vector>)} List of features.
    */
    parseData: function(data, options) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }

        var nameNode = this.getFirstChildNode(this.getFirstChildNode(this.getFirstChildNode(data, "kml"), "Document"), "name");
        if (nameNode) {
            this.kmlDocumentName = OpenLayers.Util.getXmlNodeValue(nameNode);
        }

        // Loop throught the following node types in this order and
        // process the nodes found                       
        var types = ["Link", "NetworkLink", "Style", "StyleMap", "Folder", "Placemark"];
        for (var i = 0; i < types.length; ++i) {
            var type = types[i];

            var nodes = this.getElementsByTagNameNS(data, "*", type);

            // skip to next type if no nodes are found
            if (nodes.length == 0) {
                continue;
            }

            switch (type.toLowerCase()) {

                // Fetch external links                                                     
                case "link":
                case "networklink":
                    this.parseLinks(nodes, options);
                    break;

                // parse style information                                                    
                case "style":
                    if (this.extractStyles) {
                        this.parseStyles(nodes, options);
                    }
                    break;
                case "stylemap":
                    if (this.extractStyles) {
                        this.parseStyleMaps(nodes, options);
                    }
                    break;

                // parse features                                                    
                case "placemark":
                    this.parseFeatures(nodes, options);
                    break;

                // parse folders                                                   
                case "folder":
                    this.parseFolders(nodes)
            }
        }

        return this.features;
    },

    /**
    * Method: parseAttributes
    *
    * Parameters:
    * node - {DOMElement}
    *
    * Returns:
    * {Object} An attributes object.
    */
    parseAttributes: function(node) {
        var attributes = {};
        // assume attribute nodes are type 1 children with a type 3 or 4 child
        var child, grandchildren, grandchild;
        var children = node.childNodes;
        for (var i = 0; i < children.length; ++i) {
            child = children[i];
            if (child.nodeType == 1) {
                grandchildren = child.childNodes;
                if ((grandchildren.length == 1 || grandchildren.length == 3) && child.nodeName != "ExtendedData") {
                    var grandchild;
                    switch (grandchildren.length) {
                        case 1:
                            grandchild = grandchildren[0];
                            break;
                        case 3:
                        default:
                            grandchild = grandchildren[1];
                            break;
                    }
                    if (grandchild.nodeType == 3 || grandchild.nodeType == 4) {
                        var name = (child.prefix) ?
                                child.nodeName.split(":")[1] :
                                child.nodeName;
                        var value = OpenLayers.Util.getXmlNodeValue(grandchild);
                        if (value) {
                            value = value.replace(this.regExes.trimSpace, "");
                            attributes[name] = value;
                        }
                    }
                }
                else if (child.nodeName == "ExtendedData") {
                    for (var j = 0; j < grandchildren.length; j++) {
                        if (grandchildren[j].nodeName == "Data") {
                            if (grandchildren[j].attributes) {
                                var nameAttr = grandchildren[j].attributes.getNamedItem("name");
                                if (nameAttr) {
                                    var nameAttrValue = nameAttr.nodeValue;
                                    var dataChildren = grandchildren[j].childNodes;
                                    for (var k = 0; k < dataChildren.length; k++) {
                                        if (dataChildren[k].nodeType == 1 && dataChildren[k].nodeName == "value") {
                                            attributes[nameAttrValue] = OpenLayers.Util.getXmlNodeValue(dataChildren[k]);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return attributes;
    },

    /**
    * Method: parseFolders
    * Add folders to folderList
    * 
    * Parameters: 
    * nodes     - List of {DOMElement} data to read/parse.
    */
    parseFolders: function(nodes) {
        for (var i = 0; i < nodes.length; i++) {
            var folderNode = nodes[i];
            this.parseFolder(folderNode);
        }
    },

    /**
    * Method: parseFolder
    * Add folder to folderList
    * 
    * Parameters: 
    * node     - {DOMElement} of data to read/parse.
    */
    parseFolder: function(node) {
        var newFolder = {};

        var folderName = this.getElementsByTagNameNS(node, "*", "name")[0];
        var folderDescription = this.getElementsByTagNameNS(node, "*", "description")[0];
        var styleUrl = this.getElementsByTagNameNS(node, "*", "styleUrl")[0];

        newFolder.name = OpenLayers.Util.getXmlNodeValue(folderName);
        newFolder.description = OpenLayers.Util.getXmlNodeValue(folderDescription);

        if (this.extractStyles && styleUrl) {
            if (styleUrl) {
                var styleNodeValue = OpenLayers.Util.getXmlNodeValue(styleUrl);
                newFolder.style = this.getStyle(styleNodeValue);
            }
        }

        this.folderList.push(newFolder);
    },

    getFirstChildNode: function(node, name) {
        if (node) {
            var child;
            var children = node.childNodes;
            for (var j = 0; j < children.length; ++j) {
                child = children[j];
                if (child.nodeType == 1) {
                    if (child.nodeName == name) {
                        return child;
                    }
                }
            }
        }
        return null;
    },

    CLASS_NAME: "OpenLayers.Format.AdvancedKML"
}); 