////////////////////////////////////////////////////////////////////////////////
// Code for displaying the static Ridge Trail data
////////////////////////////////////////////////////////////////////////////////

//
// JSLint (http://jslint.com/) linter directives. 
//
/*jslint onevar: true, browser: true, undef: true, nomen:true */
/*jslint eqeqeq: true, plusplus: true, bitwise: true */
/*jslint regexp: true, newcap: true, immed: true */
/*global jQuery, google, G_PHYSICAL_MAP */


// The global variables object. I use this object to avoid polluting the
// global namespace too much.
var ridgeTrail = {
    // This is the global GMap2 object.  Or will be, anyway
    map: 0,

    // The default bounds.  This can be overwritten once we've got the data, but
    // we have to have somewhere to start...
    default_bounds: 0,
    
    // Array of all of the passage structs.  I'd like to get rid of this
    passage_structs: [],
    
    // The info window DOM.  I hate having to keep this here.
    info_window: 0,

    // The coloration information
    color: [
        {
            // Book uses
            names: ["Full multi-use", "Hiking only", "Hiking & Equestrian", "Hiking & Biking"],
            match: ["hbe", "h", "he", "hb"],
            colors: ["#002A91", "#FFFF00", "#00912D", "#BD0C00"]
        },
        {
            // Uses
            names: ["Full multi-use", "Hiking only", "Hiking & Equestrian", "Hiking & Biking"],
            match: ["hbe", "h", "he", "hb"],
            colors: ["#002A91", "#FFFF00", "#00912D", "#BD0C00"]
            //colors: ["0000FF", "#FF8000", "#00FF00", "#FF0000"]
        }, 
        {
            // Status
            names: ["Dedicated", "Dedicated (but not full Multi-Use)", "Proposed"],
            match: ["d", "n", "p"],
            colors: ["#00007F", "#509BB2", "#E37A14"]
        },
        {
            // Type
            names: ["Primary", "Parallel", "Alternate", "Connector"],
            match: ["p", "l", "a", "c"],
            colors: ["#8DB500", "#005869", "#2C2233", "#5E0042"]
        },
        {
            // Interim (indexed input)
            names: ["Regular", "Interim"],
            match: ["r", "i"],
            colors: ["#00C40C", "#E300AF"]
        },
        {
            // Gauge
            names: ["Road", "Fire road", "Trail"],
            match: ["r", "f", "t"],
            colors: ["#363636", "#6B3300", "#DE8320"]
        },
        {
            // Surface
            names: ["Paved", "Natural"],
            match: ["p", "n"],
            colors: ["#575757", "#BD6F1B"]
        }
    ]

};

String.prototype.unSpace = function() {
    var rv="", s_ary, i;
    
    s_ary = this.split(" ");
    for (i=0; i<s_ary.length; i+=1) {
        if (i>0) {
            rv = rv.concat("_");
        }
        rv = rv.concat(s_ary[i]);
    }
    return rv;
};

// Pretty much everything useful in GMaps requires a GLatLngBounds object somewhere.  Unfortunately,
// the syntax for creating one is a bit.. verbose.  This method returns a proper new GLatLngBounds
// with a more compact signature
// @todo Consider making these args an object, to allow for unordered calling
var createBoundingBox = function createBoundingBox(min_lat, min_lon, max_lat, max_lon) {
    return new google.maps.LatLngBounds(new google.maps.LatLng(min_lat, min_lon), 
                                        new google.maps.LatLng(max_lat, max_lon));
};

// Zooms the map such that the given bounds are displayed at the maximum possible zoom to keep the
// whole box on the display.
var zoomBounds = function zoomBounds(bounds) {
    ridgeTrail.map.setCenter(bounds.getCenter());
    ridgeTrail.map.setZoom(ridgeTrail.map.getBoundsZoomLevel(bounds));
};

// Picks a color for a gap based on the ridgeTrail.color information.
var pickColor = function pickColor(gap_struct, cur_color) {
    var i, match_ary = ridgeTrail.color[cur_color].match;
    
    for (i=0; i<match_ary.length; i += 1) {
        if (match_ary[i] === gap_struct.color_matches[cur_color]) {
            return ridgeTrail.color[cur_color].colors[i];
        }
    }
    
    return "#000000";
};

var setColorationTable = function setColorationTable(new_color) {
    var i;
    
    for (i=0; i<4; i += 1) {
        if (i<ridgeTrail.color[new_color].names.length) {
            jQuery("#color_c"+i).attr("style", "background-color:"+ridgeTrail.color[new_color].colors[i]+";");
            jQuery("#color_i"+i).val(true);
            jQuery("#color_i"+i).hide();
            //jQuery("#color_i"+i).show();
            jQuery("#color_n"+i).text(ridgeTrail.color[new_color].names[i]);            
        } else {
            jQuery("#color_c"+i).attr("style", "background-color:#FFFFFF;");
            jQuery("#color_i"+i).hide();
            jQuery("#color_n"+i).text("");
        }
    }
};

var recolorAll = function recolorAll(new_color) {
    setColorationTable(new_color);
    
    // @todo What's the right way to do this? We can a) leave it alone; b) flip
    // the checkbox; c) turn on every segment; d) ??
    jQuery(".color_cb[checked!=true]").attr("checked", true);

    jQuery.each(ridgeTrail.passage_structs, function (i, p) {
            jQuery.each(p.gaps, function(j, g) {
                    var color = pickColor(g, new_color);
                    g.line.setStrokeStyle({color:color});
                    g.line.show();
                });
        });
};

var assignColorationCheckboxMethods = function assignColorationCheckboxMethods() {
    jQuery(".color_cb").click(function() {
            var id, val, n, color;

            id = jQuery(this).attr("id");
            n = id.charAt(id.length-1);
            val = jQuery(this).attr("checked");
            color = jQuery("#coloration_select").val();
            jQuery.each(ridgeTrail.passage_structs, function(i,p) {
                    jQuery.each(p.gaps, function(j,g) {
                            if (g.color_matches[color] === ridgeTrail.color[color].match[n]) {
                                if (val) {
                                    g.line.show();
                                } else {
                                    g.line.hide();
                                }
                            }
                        });
                });
        });
};

var assignTrailFilterMethod = function assignTrailFilterMethod() {
    jQuery("#trailfilter_select").change(function(node) {
            trailtype = jQuery(this).val();
            //alert("Trailtype is " + trailtype);
            jQuery.each(ridgeTrail.passage_structs, function(i,p) {
                    jQuery.each(p.gaps, function(j,g) {
                        //alert("color_matches is " + g.color_matches + "(" + g.color_matches.indexOf(trailtype) + ")");
                            if (g.color_matches[1].indexOf(trailtype) > -1) {
                                g.line.show();
                            } else {
                                g.line.hide();
                            }
                        });
                });
            
        });
}

var flipPaneVisibility = function flipPaneVisibility() {
    jQuery("#left_pane_close").toggle();
    jQuery("#left_pane_open").toggle();
    jQuery("#left_pane").toggle();
    jQuery("#left_pane").toggleClass("vis");
    
    if (jQuery("#left_pane").hasClass("vis")) {
        jQuery("#map_canvas").attr("style", "left:310px;");
    } else {
        jQuery("#map_canvas").attr("style", "left:0px;");     
    }
};

var assignPaneVisibilityMethods = function assignPaneVisibilityMethods() {
    jQuery("#left_pane_close").click(flipPaneVisibility);
    jQuery("#left_pane_open").click(flipPaneVisibility);
};

var setAllowedIcon = function setAllowedIcon(selector, trail_struct, match_ch, url_ary) {
    var found_count = 0, idx;

    if (trail_struct.gaps) {
        for (idx=0; idx<trail_struct.gaps.length; idx += 1) {
            // @todo Fix this hack -- uses might need to be separate, perhaps, from the color_matches array
            if (trail_struct.gaps[idx].color_matches[1]) {
                if (trail_struct.gaps[idx].color_matches[1].indexOf(match_ch) > -1) {
                    found_count += 1;
                }
            }
        }
        if (found_count === trail_struct.gaps.length) {
            selector
                .show()
                .attr("src", url_ary[0])
                .attr("alt", "Allowed")
                .attr("title", "Allowed");
        } else if (found_count === 0) {
            selector
                // .attr("src", url_ary[2])
                // .attr("src", "images/icons/blank.gif")
                .hide()
                .attr("alt", "Not allowed")
                .attr("title", "Not allowed");
        } else {
            selector
                .show()
                .attr("src", url_ary[1])
                .attr("alt", "Partially allowed")
                .attr("title", "Partially allowed");            
        }
    } else if (trail_struct.uses) {
        if (trail_struct.uses.indexOf(match_ch) === -1) {
            selector
                .hide()
                // .attr("src", url_ary[2])
                .attr("alt", "Not allowed")
                .attr("title", "Not allowed");            
        } else {
            selector
                .show()
                .attr("src", url_ary[0])
                .attr("alt", "Allowed")
                .attr("title", "Allowed");            
        }
    }
};

var openInfoWindow = function openInfoWindow(trail_struct) {
    if (trail_struct.type === "passage") {
        if (trail_struct.div.hasClass("has_desc") || true) {
            if (typeof ridgeTrail.info_window === "number") {
                ridgeTrail.info_window = document.createElement("div");

                jQuery(ridgeTrail.info_window)
                    .attr("id", "info_window")
                    .attr("style", "width:500px;font-family:verdana,tahoma,arial,sans-serif;font-size:10px;");
                
                jQuery('' +
                   '<div style="padding:10px;align:center">'+
                   '  <span id="iw_gap_name" style="font-size:150%"></span>'+
                   '</div>'+
                   '<table border="0" width="100%">'+
                   '  <tr>'+
                   '    <td style="vertical-align:top" width="320px">'+
                   '      <table width="100%">'+
                   '        <tr id="iw_page_row">'+
                   '          <td style="vertical-align:top" align="right" width="120px"><b><i><a target="_blank" href="http://www.wildernesspress.com/product.php?productid=16685&cat=0&page=1">Official&nbsp;Guide</a>&nbsp;page:</i></b></td>'+
                   '          <td style="vertical-align:top" align="left">'+
                   '            <i><span id="iw_page"></span></i>'+
                   '            <span id="iw_page_map_div"><a id="iw_page_map_link" target="_blank" href="maps/25.pdf">(map)</a></span>'+
                   '          </td>'+
                   '        </tr>'+
                   '        <tr id="iw_length_row">'+
                   '          <td style="vertical-align:top" align="right"><b><i>Length:</i></b></td>'+
                   '          <td style="vertical-align:top" align="left"><i><span id="iw_length"></span></i></td>'+
                   '        </tr>'+
                   '        <tr id="iw_allowed_row">'+
                   '          <td style="vertical-align:top" align="right"><b><i>Allowed:</i></b></td>'+
                   '          <td style="vertical-align:top" align="left">'+
                   '            <img id="iw_allowed_h" src="images/icons/1_walker.gif"/>'+
                   '            <img id="iw_allowed_e" src="images/icons/2_horse.gif"/>'+
                   '            <img id="iw_allowed_b" src="images/icons/3_bicycle.gif"/>'+
                   '          </td>'+
                   '        </tr>'+
                   '      </table>'+
                   '    </td>'+
                   '    <td id="iw_image_cell" align="right" width="180px">'+
                   '      <table border="0" width="100%">'+
                   '        <tr>'+
                   '          <td>'+
                   '            <img id="iw_image" src="" width="160px" height="120px" alt="Picture"></img>'+
                   '          </td>'+
                   '        </tr>'+
                   '        <tr>'+                   
                   '          <td align="right">'+
                   '            <span id="iw_photo_credit"></span>'+
                   '          </td>'+
                   '        </tr>'+
                   '      </table>'+
                   '    </td>'+
                   '  </tr>'+
                   '</table>'+
                   '<div id="iw_desc_div">'+
                   '  <hr />'+
                   '  <span id="iw_desc"></span>'+
                   '</div>').appendTo(ridgeTrail.info_window);
            }

            jQuery("#iw_gap_name", ridgeTrail.info_window).text(trail_struct.name);
            
            if (trail_struct.page) {
                jQuery("#iw_page_row", ridgeTrail.info_window).show();
                jQuery("#iw_page", ridgeTrail.info_window).text(trail_struct.page);
            } else {
                jQuery("#iw_page_row", ridgeTrail.info_window).show();
                jQuery("#iw_page", ridgeTrail.info_window).text("Not in 3rd Edition");                
            }
            if (trail_struct.map_url) {
                jQuery("#iw_page_map_div", ridgeTrail.info_window).show();
                jQuery("#iw_page_map_link", ridgeTrail.info_window).attr("href", trail_struct.map_url);
            } else {
                jQuery("#iw_page_map_div", ridgeTrail.info_window).hide();                    
            }
            
            if (trail_struct.book_len) {
                jQuery("#iw_length_row", ridgeTrail.info_window).show();
                jQuery("#iw_length", ridgeTrail.info_window).text(trail_struct.book_len);
            } else {
                jQuery("#iw_length_row", ridgeTrail.info_window).hide();                
            }
            
            if (trail_struct.uses || trail_struct.gaps) {
                jQuery("#iw_allowed_row", ridgeTrail.info_window).show();
                setAllowedIcon(jQuery("#iw_allowed_h", ridgeTrail.info_window), trail_struct, "h", 
                    ["images/icons/1_walker.gif", "images/icons/1_walker_some.gif", "images/icons/1_walker_no.gif"]);
                setAllowedIcon(jQuery("#iw_allowed_e", ridgeTrail.info_window), trail_struct, "e", 
                    ["images/icons/2_horse.gif", "images/icons/2_horse_some.gif", "images/icons/2_horse_no.gif"]);
                setAllowedIcon(jQuery("#iw_allowed_b", ridgeTrail.info_window), trail_struct, "b", 
                    ["images/icons/3_bicycle.gif", "images/icons/3_bicycle_some.gif", "images/icons/3_bicycle_no.gif"]);
            } else {
                jQuery("#iw_allowed_row", ridgeTrail.info_window).hide();
            }
            
            if (trail_struct.image_url) {
                jQuery("#iw_image_cell", ridgeTrail.info_window).show();
                jQuery("#iw_image", ridgeTrail.info_window)
                    .attr("src", trail_struct.image_url)
                    .attr("alt", trail_struct.caption);
                    
                if (trail_struct.image_credit) {
                    jQuery("#iw_photo_credit", ridgeTrail.info_window)
                        .replaceWith("<span id='iw_photo_credit'><b>Photo</b>: " + trail_struct.image_credit + "</span>");
                }
            } else {
                jQuery("#iw_image_cell", ridgeTrail.info_window).hide();
            }
            
            if (trail_struct.desc) {
                jQuery("#iw_desc_div", ridgeTrail.info_window).show();
                jQuery("#iw_desc", ridgeTrail.info_window).replaceWith("<span id='iw_desc'>" + trail_struct.desc + "</span>");
            } else {
                jQuery("#iw_desc_div", ridgeTrail.info_window).hide();                
            }
                        
            ridgeTrail.map.openInfoWindow(trail_struct.midpoint, ridgeTrail.info_window);
        } else {
            ridgeTrail.map.openInfoWindowHtml(trail_struct.midpoint, "<p/>"+trail_struct.name);
        }
    }
};

// Event handler when line text is clicked on in the list on the left side
var clickSegment = function clickSegment(trail_struct) {

    ridgeTrail.map.closeInfoWindow();
    zoomBounds(trail_struct.bounds);
    
    jQuery(".selected_trail").removeClass("selected_trail");
    if (trail_struct.type === "passage") {
        trail_struct.div.addClass("selected_trail");
        openInfoWindow(trail_struct);
    }
};

// Parses the main received trail-structure tree at the Gap (depth 4) level,
// taking a reference to a gap node
var parseGap = function parseGap(node, passage_struct) {
    var line, color, gap_struct;

    if (node && node.name && typeof(node.name) === 'string') {

        gap_struct = { gap_id: node.gapId,
                        line: 0,
                        passage: passage_struct
                        };
        
        gap_struct.color_matches = [
            passage_struct.uses,
            node.uses,
            node.status,
            node.type,
            node.interim ? "i" : "r",
            node.gauge,
            node.surface
        ];

        color = pickColor(gap_struct, jQuery("#coloration_select").val());

        line = new google.maps.Polyline.fromEncoded({
                    points: node.points,
                    levels: node.levels,
                    zoomFactor: node.zoomFactor,
                    numLevels: node.numLevels,
                    color: color,
                    weight: 3,
                    opacity: 0.90
                    });
        gap_struct.line = line;
        
        google.maps.Event.addListener(line, 'click', function(latlng) {
                clickSegment(passage_struct);
            });
        ridgeTrail.map.addOverlay(line);
        
        passage_struct.gaps.push(gap_struct);

    } else {
        alert('Invalid node passed to parseGap');
    }
};

var parsePassage = function parsePassage(node, parent) {
    var my_div, passage_struct;

    if (node && node.name && typeof(node.name) === 'string') {

        passage_struct = { name: node.name,
                            div: 0,
                            gaps: [],
                            bounds: createBoundingBox(node.min_lat, node.min_lon, node.max_lat, node.max_lon),
                            midpoint: new google.maps.LatLng(node.mid_lat, node.mid_lon),
                            type: "passage",
                            uses: node.book_uses,
                            bid: 0,
                            page: node.page,
                            book_len: node.book_length,
                            image_url: node.image_url,
                            image_caption: node.caption,
                            image_credit: node.image_credit,
                            map_url: node.map_url,
                            desc: node.desc
                            };

        node.visible = true;

        my_div = jQuery(document.createElement("div"))
                    .addClass("left_pane_line")
                    .addClass("yellow_hilite")
                    .addClass("line_vis")
                    /*
                    .click(function() {
                            jQuery(this)
                                .toggleClass("line_vis")
                                .toggleClass("gray_font");
                            if (jQuery(this).hasClass("line_vis")) {
                                jQuery.each(passage_struct.gaps, function(i,n) { n.line.show(); });
                            } else {
                                jQuery.each(passage_struct.gaps, function(i,n) { n.line.hide(); });                                
                            }                            
                        })
                    */
                    .appendTo(parent);
        
        passage_struct.div = my_div;
        
        if (passage_struct.desc && passage_struct.desc.length > 0) {
            my_div.addClass("has_desc");
        }
        if (passage_struct.image_url && passage_struct.image_url.length > 0) {
            my_div.addClass("has_photo");
        }
        if (passage_struct.map_url && passage_struct.map_url.length > 0) {
            my_div.addClass("has_map");
        }
        
        jQuery(document.createElement("span"))
            .addClass("left_pane_line_label")
            .text(node.name)
            .attr("style", "cursor:pointer;")
            .hover(
                    function() { jQuery(this).toggleClass("blue_text"); },
                    function() { jQuery(this).toggleClass("blue_text"); } 
                )
            .click(function() { 
                    clickSegment(passage_struct); 
                    return false; 
                })
            .appendTo(my_div);

        if (node.Gap) {
                 jQuery.each(node.Gap, function(i,n) {
                                            parseGap(n, passage_struct);
                                        });
        }
        
        ridgeTrail.passage_structs.push(passage_struct);

    } else {
        alert('Invalid node passed to parsePassage');
    }
};

// Parses the main received trail-structure tree at the Region (depth 2) level, taking a reference
// to a region node as an input
var parseRegion = function parseRegion(node) {
    var region_div, region_list_div, region_struct = {};
  
    if (node && node.name && typeof(node.name) === 'string') {
    
        region_div = jQuery(document.createElement("div"))
                        .addClass("region_list")
                        .attr("id", "region_" + node.name.unSpace())
                        .appendTo("#trail_list");

        region_struct = {
            name: node.name,
            bounds: createBoundingBox(node.min_lat, node.min_lon, node.max_lat, node.max_lon),
            type: "region"
        };
        
        jQuery(document.createElement("div"))
            .addClass("left_pane_group_hdr")
            .addClass("left_pane_group_hdr_collapsed")
            .addClass("yellow_hilite")
            .attr("id", "region_hdr_"+node.name.unSpace())
            .appendTo(region_div);

        jQuery(document.createElement("img"))
            .attr("src", "images/icons/tri_r_g.png")
            .addClass("triangle_right")
            .attr("style", "cursor:pointer;")
            .click(function() {
                    jQuery(this)
                        .toggleClass("triangle_down")
                        .toggleClass("triangle_right");
                    if (jQuery(this).hasClass("triangle_down")) {
                        jQuery(this).attr("src", "images/icons/tri_d_g.png");
                    } else {
                        jQuery(this).attr("src", "images/icons/tri_r_g.png");                        
                    }
                    jQuery("#region_hdr_"+node.name.unSpace())
                        .toggleClass("left_pane_group_hdr_expanded")
                        .toggleClass("left_pane_group_hdr_collapsed");
                    jQuery("#region_list_"+node.name.unSpace()).slideToggle(/*"slow"*/);                         
                })
            .appendTo("#region_hdr_"+node.name.unSpace());

        jQuery(document.createElement("span"))
            .text(node.name)
            .attr("style", "cursor:pointer;padding-left:2px;")
            .hover(
                    function() { jQuery(this).toggleClass("blue_text"); },
                    function() { jQuery(this).toggleClass("blue_text"); }
                )
            .click(function() { clickSegment(region_struct); return false; })
            .appendTo("#region_hdr_"+node.name.unSpace());

        region_list_div = jQuery(document.createElement("div"))
            .addClass("passage_list")
            .attr("id", "region_list_"+node.name.unSpace())
            .attr("style", "display:none;")
            .appendTo(region_div);
                    
        if (node.Passage) {
                 jQuery.each(node.Passage, function(i,n) {
                                            parsePassage(n, jQuery(region_list_div));
                                        });
        }
    
    } else {
        alert('Invalid node passed to parseRegion');
    }
};

// Entry point to the server-generated JSON data for the raw tree.
var retrieveBookPassages = function retrieveBookPassages() {
    google.maps.DownloadUrl("passages.json", function(data, responseCode) {
            var passages;
            
            if (responseCode !== 200) {
                alert("Got a " + responseCode + " from the server when requesting trail data!");
                return;
            }

            if (typeof JSON === 'object') {
                passages = JSON.parse(data);
            } else {
                passages = eval( '(' + data + ')' );
            }

            ridgeTrail.default_bounds = createBoundingBox(passages.min_lat, 
                                                         passages.min_lon, 
                                                         passages.max_lat, 
                                                         passages.max_lon);

            if (passages.Region) {
                jQuery.each(passages.Region, function(i,node) {
                        parseRegion(node);
                    });
            }
        });
};

// Initialization method, called when the document's ready
jQuery(document).ready(function() {
    if (google.maps.BrowserIsCompatible()) {
        ridgeTrail.default_bounds = createBoundingBox(37.0038039542664, -122.760171537037, 38.4646589254181, -121.520603645815);

        ridgeTrail.map = new google.maps.Map2(document.getElementById("map_canvas"));
        ridgeTrail.map.setCenter(ridgeTrail.default_bounds.getCenter());
        ridgeTrail.map.setUIToDefault();
        ridgeTrail.map.setMapType(G_PHYSICAL_MAP);

        ridgeTrail.map.setZoom(ridgeTrail.map.getBoundsZoomLevel(ridgeTrail.default_bounds));
        
        jQuery("#left_pane_title_span")
            .click(function() {
                    clickSegment({ name: "Bay Area Ridge Trail",
                                    bounds: ridgeTrail.default_bounds,
                                    type: "overview" });
                    return false;
                });
        
        assignColorationCheckboxMethods();
        assignTrailFilterMethod();
        assignPaneVisibilityMethods();
        setColorationTable(jQuery("#coloration_select").val());
        jQuery("#coloration_select").change(function(node) {
                recolorAll(jQuery(this).val());
            });
        retrieveBookPassages();
    }
});



