function contains(zone, pos_x, pos_y)
{
    var zone_area = zone[3];
    return ((pos_x >= zone_area[0]) && (pos_x <= zone_area[0] + zone_area[2])
	    && (pos_y >= zone_area[1]) && (pos_y <= zone_area[1] + zone_area[3]));
}

function slugify(text, separator) {
    var replacements = [['ą', 'a'], ['ć', 'c'], ['ę', 'e'], ['ł', 'l'],
			['ń', 'n'], ['ó', 'o'], ['ś', 's'], ['ź', 'z'],
			['ż', 'z'], ['-', ' ']];
    text = text.toLowerCase();
    for (var i = 0; i < replacements.length; i++) {
	text = text.replace(new RegExp(replacements[i][0], 'g'), replacements[i][1]);
    }
    return text.replace(/\s+/g, separator || '-').replace(/[^a-z0-9\-_]/g,'');
}

function tiny_tooltip(x, y, text) {
    var tt = $("#tiny-tooltip");
    tt.text(text);
    tt.css("top", (y + 20) + 'px').css("left", (x + 10) + 'px').show();
}

function id_from_symptom_link(link) {
    return link.href.split("#")[1];
}

function dict_merge(a, b) {
    var ret = {};
    for (var property in a)
        ret[property] = a[property];
    for (var property in b)
        ret[property] = b[property];
    return ret;
}

function bounding_area(zone_info) {
    if (zone_info['area'])
	return zone_info['area'];
    var areas = zone_info['areas'];
    var ret = [areas[0][0], areas[0][1],
	       areas[0][0] + areas[0][2], areas[0][0] + areas[0][3]];
    for (var i = 1; i < areas.length; i++) {
	var area = areas[i];
	ret[0] = Math.min(ret[0], area[0]);
	ret[1] = Math.min(ret[1], area[1]);
	ret[2] = Math.max(ret[2], area[0] + area[2]);
	ret[3] = Math.max(ret[3], area[1] + area[3]);
    }
    ret[2] -= ret[0];
    ret[3] -= ret[1];
    return ret;
}

/** An image viewer with zoom and active areas (zones).
 */
function ViewPort(div, img, highlight_img) {
    this.div = div;
    this.img = img;
    this.highlight_img = highlight_img;
    this.overview_zones = [];
    this.zones = [];
    this.first_frame = true;
    this.maximum_zoom_width = 100;
    this.handlers_installed = false;

    /** Loads image and sets the initial viewport.
     *
     *  - img_url: image name prefix
     *  - img_dimensions: a 2-element list with image size,
     *    to avoid waiting until it's loaded
     *  - area: a 4-element list [x, y, w, h] to be initially
     *    visible (it will be expanded, if necessary, to keep
     *    the original aspect ratio).
     */
    this.set_content = function(img_url, img_dimensions, area,
				overview_zones,
				maximum_zoom_width,
				overview_zone_callback,
				subzone_callback)
    {
        this.remove_zones();
	this.img_url = img_url;
        $(this.img).attr("src", img_url + ".gif");
        this.img_dimensions = img_dimensions;
        this.body_area = area;
	this.first_frame = true;
        this.show_area(area);
	this.maximum_zoom_width = maximum_zoom_width;
	this.overview_zones = overview_zones;
	this.overview_zone_callback = overview_zone_callback;
	this.subzone_callback = subzone_callback;

	viewer = this;

	if (!this.handlers_installed) {
	    $(this.div).mousemove(function (evt) {
		viewer.mousemove(viewer, evt);
	    }).bind('mouseout', function() {
		viewer.leave_zones();
	    }).click(function() {
		var zone = viewer.current_zone;
		if (zone) {
		    if (zone[4]) {
			zone[4](zone[0], zone[5]);
		    }
		} else {
		    Diagnoser.show_general_symptoms();
		}
	    });
	    this.handlers_installed = true;
	};
    };

    this.zoom_out = function(done_callback) {
	$("#diag-zoom-out").fadeOut();
	this.remove_zones();
	var self = this;
	this.add_zones(this.overview_zones, function(zone_name, zone_info) {
	    if (zone_info['subzones'])
		self.overview_zone_callback(zone_name, zone_info);
	    else
		self.subzone_callback(zone_name, zone_info);
        });
	this.show_area(this.body_area, done_callback);
    };

    this.zoom_in = function(zone_name, zone_info, done_callback) {
	viewer.leave_zones();
	$("#diag-zoom-out").fadeIn();
	this.remove_zones();
	this.add_zones(zone_info['subzones'], this.subzone_callback);
	this.show_area(bounding_area(zone_info), done_callback);
    };

    /** Changes the visible area.
     *
     *  - area: 4-element list [x, y, w, h] to be visible
     *  - keep_aspect: if true, area will be expanded if 
     *    necessary to keep the correct aspect ratio
     */
    this.show_area = function(area, done_callback)
    {
	var x = area[0], y = area[1], w = area[2], h = area[3];

	if (w < this.maximum_zoom_width) {
	    x -= (this.maximum_zoom_width - w) / 2;
	    w = this.maximum_zoom_width;
	}

        var xscale = $(this.div).width() / w;
        var yscale = $(this.div).height() / h;
	this.scale = Math.min(xscale, yscale);
	this.xdiff = -x * this.scale;
	this.ydiff = -y * this.scale;

	// center the visible area
	var actual_viewport = this.map_rect_screen2img([0, 0, $(this.div).width(), $(this.div).height()]);
	this.xdiff = -this.scale * (x - (actual_viewport[2] - w) / 2);
	this.ydiff = -this.scale * (y - (actual_viewport[3] - h) / 2);

        $(this.img).css("position", "absolute");
        $(this.highlight_img).css("position", "absolute");

	var animation_time = (this.first_frame ? 0 : 400);
	this.first_frame = false;

	$(this.img).animate({left: this.xdiff + "px",
                             top: this.ydiff + "px",
                             width: Math.floor(this.scale * this.img_dimensions.width),
                             height: Math.floor(this.scale * this.img_dimensions.height)},
	    animation_time,
	    "swing",
	    done_callback);
	$(this.highlight_img).animate({left: this.xdiff + "px",
				       top: this.ydiff + "px",
				       width: Math.floor(this.scale * this.img_dimensions.width),
				       height: Math.floor(this.scale * this.img_dimensions.height)},
				      animation_time,
				      "swing");
        this.update_zones();
    };

    this.mousemove = function(viewer, evt) {
        for (var i = viewer.zones.length - 1; i >= 0; i--) {
	    var pot_zone = viewer.zones[i];
	    if (contains(pot_zone, evt.pageX, evt.pageY)) {
		if (viewer.current_zone != pot_zone) {
        	    viewer.current_zone = pot_zone;
        	    var url = this.img_url + "_" + slugify(viewer.current_zone[0], '_') + ".gif";
        	    $(this.highlight_img).attr("src", url);
		}
		tiny_tooltip(evt.pageX, evt.pageY, viewer.current_zone[0]);
		return;
	    }
	}
	viewer.leave_zones();
    },

    this.leave_zones = function() {
	if (this.current_zone) {
	    $(this.highlight_img).attr("src", this.img_url + '/../../empty_pixel.gif');
	    this.current_zone = null;
	}
	$("#tiny-tooltip").hide();
    },

    /** Adds a new active zone to the viewer.
     */
    this.add_zone = function(zone_name, zone_rect, zone_info, callback) {
	var pos = this.map_rect_img2screen(zone_rect);
        this.zones = this.zones.concat([[zone_name, zone_rect, null, [], callback, zone_info]])
    };

    /** Adds a list of new zones to the viewer.
     *  Each zone has a name and one or more rectangular areas.
     *
     *  - zones: a list of lists with zone information:
     *    [['zone1_name', {'area': [x, y, w, h]}],
     *     ['zone2_name', {'areas': [[x, y, w, h], [x, y, w, h]]}],
     *     ...]
     *  - callback: a function to call when a zone gets clicked.
     *    Signature: function(zone_name, zone_rectangle).
     */
    this.add_zones = function(zones, callback) {
	if (!zones)
	    return;
        for (var i = 0; i < zones.length; i++) {
            var zone = zones[i];
            var zone_name = zone[0];
            var zone_info = zone[1];
	    var areas;
	    if (zone_info['area'])
		areas = [zone_info['area']];
	    else
		areas = zone_info['areas'];
            for (var zi = 0; zi < areas.length; zi++) {
                this.add_zone(zone_name, areas[zi], zone_info, callback);
	    }
        }
        this.update_zones();
    };

    this.remove_zones = function() {
        this.zones = [];
    };

    this.update_zones = function() {
	var offset = $(this.div).offset();
        for (var i = 0; i < this.zones.length; i++) {
	    var zone = this.zones[i];
            var zone_rect = zone[1];
            var zone_el = zone[2];
	    var new_area = this.map_rect_img2screen(zone_rect);
            this.set_position(zone_el, new_area);
	    zone[3] = [new_area[0] + offset.left, new_area[1] + offset.top,
		       new_area[2], new_area[3]];
        }
    };

    this.map_point_screen2img = function(point) {
        return [(point[0] - this.xdiff) / this.scale,
                (point[1] - this.ydiff) / this.scale];
    };

    this.map_point_img2screen = function(point) {
        return [point[0] * this.scale + this.xdiff,
                point[1] * this.scale + this.ydiff];
    };

    this.map_rect_img2screen = function(rect) {
        var lt = this.map_point_img2screen([rect[0], rect[1]]);
        var rb = this.map_point_img2screen([rect[0] + rect[2], rect[1] + rect[3]]);

        return [lt[0], lt[1], rb[0] - lt[0], rb[1] - lt[1]];
    };

    this.map_rect_screen2img = function(rect) {
        var lt = this.map_point_screen2img([rect[0], rect[1]]);
        var rb = this.map_point_screen2img([rect[0] + rect[2], rect[1] + rect[3]]);

        return [lt[0], lt[1], rb[0] - lt[0], rb[1] - lt[1]];
    };

    this.set_position = function(el, scr_rect) {
        $(el).css("left", Math.floor(scr_rect[0]) + "px");
        $(el).css("top", Math.floor(scr_rect[1]) + "px");
        $(el).css("width", Math.floor(scr_rect[2]) + "px");
        $(el).css("height", Math.floor(scr_rect[3]) + "px");
    };

}

function the_other(current, va, vb) {
    if (current == va)
        return vb;
    else
        return va;
}

function ListViewer(ul_selector, click_callback, operation_block, update_complete_callback) {
    this.ul = $(ul_selector);
    this.item_list = [];
    this.click_callback = click_callback;
    this.ul_selector = ul_selector;
    this.operation_block = operation_block;
    this.update_complete_callback = update_complete_callback;

    this.set = function(item_list) {
        this.item_list = item_list;
        this._update();
    };

    this.add = function(item) {
        if (this.contains(item))
            return;

        this.item_list.push(item);
        this._update();
    };

    this.clear = function() {
        this.set([]);
    };

    this.remove = function(item) {
        var i = this._get_index(item);
        if (i === null)
            return;
        this.item_list.splice(i, 1);
        this._update();
    };

    this._get_index = function(item) {
        for (var i = 0; i < this.item_list.length; i++) {
            if (this.item_list[i].id == item.id)
                return i;
        }
        return null;
    };

    this.contains = function(item) {
        return this._get_index(item) !== null;
    };

    this.show_additional_info = function(li, item) {
        var jq_info = $("#additional-info");
        var pos = $(li).offset();
        jq_info.css("top", (pos.top + $(li).height() + 10) + "px");
        jq_info.css("left", (pos.left + 20) + "px");
        jq_info.html("<p>Tu będzie podpowiedź na temat objawu</p>"
                     + "<p>" + item.name + "</p>");
        jq_info.show();
    };

    this.hide_additional_info = function() {
        var jq_info = $("#additional-info");
        jq_info.hide();
    };

    this._update = function() {
        var ul = this.ul;
        var callback = this.click_callback;
        var self = this;

        this._clear_list();
        $.each(this.item_list, function(i, item) {
            var new_node = document.createElement("li");
            var el_new_a = document.createElement("a");
            new_node.appendChild(el_new_a);
            var new_a = $(el_new_a);
            new_a.attr('href', "#" + item.id);
            new_a.attr('name', encodeURIComponent(item.name));
	    new_a.html(self.operation_block
		       + item.name
		       + '<span class="zone-name"> (' + item.zone_name + ")</span>");
	    new_a.attr('class', 'showTip symptom');
/*            $(new_a).hover(function() {
                self.show_additional_info(new_node, item);
            }, function(obj) {
                self.hide_additional_info();
            });*/

            if (callback)
                new_a.click(callback);
            ul.append(new_node);
        });
	if (this.item_list.length)
	    $(this.ul.find("li.empty-marker")).hide();
	else
	    $(this.ul.find("li.empty-marker")).show();
	if (this.update_complete_callback)
	    this.update_complete_callback();
    };

    this._clear_list = function() {
        this.ul.find("li").map(function(index, el) {
	    if (!$(el).hasClass('empty-marker'))
		el.parentNode.removeChild(el);
        });
    };

    this.get_id_list = function() {
        return $.map(this.item_list,
                     function(item) { return item.id; });
    };

}

var Diagnoser = {
    init: function(media_url, imageset, gender) {
        this.symptoms = {};
        this.available_symptoms = new ListViewer("#diag-symptoms ul",
                                                 Diagnoser.symptom_clicked,
						 '<span class="op plus">+</span>');
        this.chosen_general_symptoms = new ListViewer("ul.general-symptoms",
						      Diagnoser.chosen_symptom_clicked,
						      '<span class="op minus">x</span>',
						      this.symptom_list_updated);
        this.chosen_local_symptoms = new ListViewer("ul.local-symptoms",
						    Diagnoser.chosen_symptom_clicked,
						    '<span class="op minus">x</span>',
						    this.symptom_list_updated);

        this.MEDIA_URL = media_url;
        this.imageset = imageset;

        this.gender = gender;
        this.side = "front";

        this.init_controls();
        
        this.show_body();
    },

    symptom_list_updated: function() {
	var total_count = (Diagnoser.chosen_general_symptoms.item_list.length
			   + Diagnoser.chosen_local_symptoms.item_list.length);
	if (total_count) {
	    $("#diag-clear-symptoms").show();
	    $("#diag-diagnose").show();
	} else {
	    $("#diag-clear-symptoms").hide();
	    $("#diag-diagnose").hide();
	}
    },

    clear_diagnosis: function() {
	var result_pane = $("#diag-results .result-pane");
	result_pane.slideUp("fast", function() {
            result_pane.html('');
	});
	Diagnoser.diagnosed = false;
    },

    new_diagnose: function() {
        this.chosen_general_symptoms.clear();
        this.chosen_local_symptoms.clear();
	this.clear_diagnosis();
    },

    open_colorbox: function(data) {
	var colorbox_data;
	colorbox_data = {inline: true,
			     opacity: 0.7};
	colorbox_data = dict_merge(colorbox_data, data);
	$.fn.colorbox(colorbox_data);

	// from colorbox
	var isIE = !$.support.opacity;
	var isIE6 = isIE && !window.XMLHttpRequest;
	if (isIE6) {
            var $window = $(window);
            $window.unbind('resize.cboxie6');
            $window.unbind('scroll.cboxie6');

            var $overlay = $("#cboxOverlay");
            $window.bind('resize.cboxie6 scroll.cboxie6', function () {
		$overlay.css({width: $("#diag-pane").width() + 50, height: $("#diag-pane").height() + 20,
                              top: 0, left: 0});
            }).trigger("scroll.cboxie6");
	};
    },

    diagnose: function(with_answers) {
        var id_list = Diagnoser.chosen_general_symptoms.get_id_list();
	var symptom_count = id_list.length;
	id_list = id_list.concat(Diagnoser.chosen_local_symptoms.get_id_list());

	try {
	    pageTracker._trackEvent('Diagnostics', 'Diagnose', Diagnoser.gender, symptom_count);
	} catch(err) {};

	var answer_list = '';
	$("input.diag-answer-yes:checked").each(function () {
	    answer_list += $(this).attr("name") + ",";
	});
	
	$("#diag-diagnose").html("Diagnozuję...");
	var data = {symptoms: id_list, gender: Diagnoser.gender};
	if (with_answers) {
	    data['answers'] = answer_list;
	    data['with_answers'] = true;
	}
        $.getJSON("/diag/_js/diagnose/", data,
              function(data, status) {
		  $("#diag-diagnose").fadeOut("fast", function() {$("#diag-diagnose").html("Diagnozuj")});

                  $("#diag-results .result-pane").html(data.html).slideDown("fast");
		  Diagnoser.diagnosed = true;

 		  if (data.questions) {
		      $("#diag-question-list").html(data.questions);
		      Diagnoser.open_colorbox({href: '#colorbox-questions',
					       opacity: 0.4});
		  }
		  else {
		  	  Diagnoser.show_caution_colorbox();
		  }
              });
        
        return false;
    },

    set_gender: function(gender) {
	if (this.gender == gender) {
	    this.large_view.zoom_out();
	    return;
	}
        this.gender = gender;
        this.new_diagnose();
        this.show_body();
	if (gender == "female") {
	    $("#male-icon").removeClass("current");
	    $("#female-icon").addClass("current");
	} else {
	    $("#female-icon").removeClass("current");
	    $("#male-icon").addClass("current");
	}
    },

    switch_side: function() {
        this.side = the_other(this.side, "front", "back");
        this.show_body();
    },

    get_image: function() {
        return this.imageset[this.gender + "_" + this.side];
    },

    show_symptoms_for: function(body_zone, done_callback) {
	var replacements = {'całe ciało': 'objawy ogólne'};

        $.getJSON("/diag/_js/symptoms/" + slugify(body_zone) + "/",
		  {gender: this.gender}, function(data, status) {
            $("#diag-body-part-name").text(replacements[data.body_zone_name] || data.body_zone_name);
            Diagnoser.store_symptom_information(data.symptoms);
            Diagnoser.available_symptoms.set(data.symptoms);
	    if (done_callback) {
		done_callback();
	    }
        });
    },

    show_symptom_pane: function(done_callback) {
//	$("#diag-symptoms").animate({left: '280px'}, 500, "swing", done_callback);
	if (done_callback)
	    done_callback();
    },

    hide_symptom_pane: function(done_callback) {
//	$("#diag-symptoms").animate({left: '20px'}, 100, "linear", done_callback);
	if (done_callback)
	    done_callback();
    },

    store_symptom_information: function(symptom_list) {
        $.each(symptom_list, function(i, symptom) {
            Diagnoser.symptoms[symptom.id] = symptom;
        });
    },

    get_symptom: function(symptom_id) {
        // TODO: add lazy loading for symptoms that are not in cache
        return Diagnoser.symptoms[symptom_id];
    },

    symptom_clicked: function(obj) {
        var symptom_id = id_from_symptom_link(obj.target);
        Diagnoser.current_symptom_list.add(Diagnoser.get_symptom(symptom_id));
	Diagnoser.clear_diagnosis();
        return false;
    },

    chosen_symptom_clicked: function(obj) {
	var href = ($(obj.target).attr("href") || $(obj.target).parent("a").attr("href"));
        var symptom_id = href.substr(1);
	var symptom = Diagnoser.get_symptom(symptom_id);
        Diagnoser.chosen_general_symptoms.remove(symptom);
        Diagnoser.chosen_local_symptoms.remove(symptom);
	Diagnoser.clear_diagnosis();
        return false;
    },

    show_general_symptoms: function() {
	Diagnoser.current_symptom_list = Diagnoser.chosen_general_symptoms;
	Diagnoser.hide_symptom_pane(function() {
	    var image = Diagnoser.get_image();
	    Diagnoser.hide_symptom_pane(function() {
		Diagnoser.show_symptoms_for("całe ciało", function() {
		    Diagnoser.large_view.zoom_out(Diagnoser.show_symptom_pane);
		});
	    });
	});
    },

    init_controls: function() {
        Diagnoser.large_view = new ViewPort($("#diag-body")[0],
                                            $("#diag-body-img")[0],
                                            $("#diag-body-img-highlight")[0]);
        $("#female-icon").click(function() {
            Diagnoser.set_gender("female");
            return false;
        });
        $("#male-icon").click(function() {
            Diagnoser.set_gender("male");
            return false;
        });
        $("#diag-rotate").click(function() {
            Diagnoser.switch_side();
            return false;
        });
        $("#diag-general").click(function() {
            Diagnoser.show_general_symptoms();
            return false;
        });
        $("#diag-start").click(function() {
            Diagnoser.chosen_general_symptoms.clear();
            Diagnoser.chosen_local_symptoms.clear();
            return false;
        });
        $("#diag-zoom-out").click(function() {
            Diagnoser.large_view.zoom_out();
            return false;
        });
        $("#diag-clear-symptoms").click(function() {
            Diagnoser.new_diagnose();
            return false;
        });
        $("#diag-diagnose").click(function() {
	    Diagnoser.diagnose();
            return false;
        });

        $("#diag-answer-questions").click(function() {
	    Diagnoser.diagnose(true);
            return false;
        });
        $("#diag-cancel-questions").click(function() {
  	    Diagnoser.show_caution_colorbox();
            return false;
        });
        $("#caution-ok").click(function() {
	    $.fn.colorbox.close();
            return false;
        });

        // nicer hover effects
        $('a.ui-state-default').hover(
            function() { $(this).addClass('ui-state-hover'); },
            function() { $(this).removeClass('ui-state-hover'); }
        );

        $('#ajax-spinner').ajaxStart(function() {
            $(this).show();
        });

        $('#ajax-spinner').ajaxStop(function() {
            $(this).hide();
        });
    },

    show_body: function() {
        var image = this.get_image();
        Diagnoser.large_view.set_content(Diagnoser.MEDIA_URL + image.url,
                                         image.full_size,
                                         image.body_area,
					 image.overview_zones,
					 image.maximum_zoom_width,
					 function(zone_name, zone_info) {
					     Diagnoser.hide_symptom_pane(function() {
                                                 Diagnoser.large_view.zoom_in(zone_name, zone_info, function() {
						     Diagnoser.current_symptom_list = Diagnoser.chosen_local_symptoms;
					             Diagnoser.show_symptoms_for(zone_name, Diagnoser.show_symptom_pane);
						 });
					     });
					 },
					 function(zone_name, zone_info) {
					     Diagnoser.hide_symptom_pane(function() {
						 Diagnoser.current_symptom_list = Diagnoser.chosen_local_symptoms;
					         Diagnoser.show_symptoms_for(zone_name, Diagnoser.show_symptom_pane);
					     });
					 });
	Diagnoser.show_general_symptoms();
    },
    
    show_caution_colorbox: function() {
		Diagnoser.open_colorbox({href: "#caution",
             overlayClose: false,
             onOpen: function(){$().unbind("keydown.cbox_close");}});
    }
};

var install_fckeditor = function(media_url, div_name) {
    var oFCKeditor = new FCKeditor( div_name ) ;
    oFCKeditor.Config["CustomConfigurationsPath"] = media_url + "js/fckconfig.js";
    oFCKeditor.Width = "700px";
    oFCKeditor.Height = "300px";
    oFCKeditor.BasePath = media_url + "js/fckeditor/" ;
    oFCKeditor.ReplaceTextarea() ;
};

