/**
 * Copyright (C) 2006-2008 Atlas Ltd.
 * required: Google Maps API library.
 */
var MapOrganizer = function(map_id, opts) {
	this.map_elm_ = $(map_id);
    var map = new GMap2(this.map_elm_);
    this.map_ = map;
    this.manipulator_ = new MarkersManipulator(map);
    this.in_query_ = false;
    this.num_request_ = 0;
    this.reast_query_ = false;
    this.elm_now_loading_ = 'nowloading';
    this.moveendEvents_ = [];
	if (opts) {
		this.opts_ = opts;
	}
	else {
		this.opts_ = {};
	}
	
};

MapOrganizer.prototype = {

    acts_as_receiver: true,
    acts_as_sender: true,

    showInitialMarks: function() {
        this.manipulator_.remove_marks_();
        this.dispatcher.send('removeall');
    	this.mapChangedAction_(this.map_);
    },

    register_dispatchers: function(dispatcher) {
        dispatcher.register(this.manipulator_);
    },

    getMap: function() {
        return this.map_;
    },

    get_manipulator: function() {
        return this.manipulator_;
    },

    registerEventAction_: function() {
		this.moveendEvents_.push(this.mapChangedAction_.bind(this));
        GEvent.addListener(this.map_, "moveend", this.runMoveEndEvents_.bind(this));
    },

    mapClickedAction: function(overlay, point) {
    },
	
	addMoveEndEvent: function(event) {
		this.moveendEvents_.push(event);
	},
	
	runMoveEndEvents_: function() {
		var map = this.map_;
		this.moveendEvents_.each(function(event) {
			event(map);
		});
	},

    mapChangedAction_: function(map) {
        var m = map;
        var pt = m.getCenter();
        var ii = this.num_request_++;
        if (this.in_query_) {
            this.least_query_ = true;
            return;
        } else {
            var zoom = m.getZoom();
            if (this.pointByClick_) {
            	this.pointByClick_ = false;
            	if (zoom < 15) {
            		m.setZoom(15);
            	}
            } else if (zoom <= 10) {
		        this.manipulator_.remove_marks_();
	        	this.dispatcher.send('removeall');
               	return;
           	}

            this.in_query_ = true;
            this.least_query_ = false;
            var bounds = m.getBounds();
            var center = m.getCenter();
            var sw = bounds.getSouthWest();
            var ne = bounds.getNorthEast();
			zoom = m.getZoom();
			this.now_loading_(true);
            new Ajax.Request('/qbb', {
                method: 'get',
                parameters: '&ii=' + ii + '&sw_lat=' + sw.lat() + '&sw_lng=' + sw.lng() + '&ne_lat=' + ne.lat() + '&ne_lng=' + ne.lng() + "&ce=" + center.lat() + "x" + center.lng() + "&s=" + zoom,
                onComplete: this.show_results.bind(this)
            });
        }
    },

    now_loading_: function(flag) {
		var elm = $(this.elm_now_loading_);
		if (!elm) return;
    	if (flag) {
    		elm.show();
    	} else {
    		elm.hide();
    	}
    },

   //////////////////////////////
    show_results: function(transport) {
	var responseText = transport.responseText;
	if (this.result_text_ != responseText) {
		this.result_text_ = responseText;
 		var obj = eval('(' + responseText + ')');
		this.show_results_from_obj(obj);
	}
        this.in_query_ = false;
	this.now_loading_(false);
    },
    show_results_from_obj: function(obj) {
        this.manipulator_.set_source_objs(obj);
        this.manipulator_.replace_marks_();
    },
    //////////////////////////////
    start: function(lat, lng, scale, marks, center_mark) {
        var m = this.map_;
        var pt = new GLatLng(lat, lng)
        m.setCenter(pt, scale);
		m.addControl(this.controller_by_height());
        m.addControl(new GMapTypeControl());
        m.addControl(new GScaleControl());
		if (!this.opts_.init_only) {
			this.registerEventAction_();
		}
		  if (marks && center_mark) {
			this.show_results_from_obj(marks);
		  }
    },
	
	controller_by_height: function() {
		var height = this.map_elm_.getHeight();
		var controller = null;
		if (height < 130) {
			controller = new GSmallZoomControl();
		} else if (height < 310) {
			controller = new GSmallMapControl();
		} else {
			controller = new GLargeMapControl();
		}
		return controller;
	},
	
	on_pan_to_center: function(lat, lng) {
		this.pointByClick_ = true;
	    var map = this.getMap();
	    map.panTo(new GLatLng(lat, lng));		
	}
}

function createCommonIcon() {
    var ci = new GIcon();
    ci.image = '/images/nonumber.png';
    ci.shadow = '/images/number_shadow.png';
    ci.iconSize = new GSize(23, 40);
    ci.shadowSize = new GSize(50, 39);
    ci.iconAnchor = new GPoint(12, 37);
    return ci;
}

var COMMON_ICON = createCommonIcon();
var ICON_PREF_BY_CC = {
    iconSize: new GSize(23, 35),
    iconAnchor: new GPoint(12, 31)
};

var MarkerWrapper = function(map, attrs, id, receiver) {
        this.receiver_ = receiver;
        this.map_ = map;
        this.mark_ = null;
        this.revmark_ = null;
        this.id_ = id;
        this.uniqId_ = attrs.id;
        this.latlng_ = new GLatLng(attrs.location.y, attrs.location.x);

        this.name_ = attrs.name;
//        this.genre_code_ = attrs.genre_code;
        this.genre_code_ = '50080';
        this.attrs_ = attrs;
        this.handles_ = new Array();
        this.clipped_ = false;
        this.unclip();
};

MarkerWrapper.prototype = {

    unclip: function() {
        this.setup_mark(false);
        this.clipped_ = false;
    },

    clip: function() {
    	this.setup_mark(true);
    	this.clipped_ = true;
    },

    setup_mark: function(clipped) {
        var mark = this.create_mark(this.latlng_, this.id_, this.genre_code_, this.name_, clipped);
        this.map_.addOverlay(mark);
        if (this.mark_ != null) {
            this.map_.removeOverlay(this.mark_);
        }
        this.mark_ = mark;
    },

    create_mark: function(latlng, id, class_code, tiptext, clipped) {
       var mark = new GMarker(latlng, {
            icon: this.createIcon(id, class_code, clipped),
            title: tiptext
        });
        this.replace_listener(mark, 'mouseover', this.mark_mouseover_action_.bind(this));
        this.replace_listener(mark, 'mouseout', this.mark_mouseout_action_.bind(this));
        this.replace_listener(mark, 'click', this.mark_click_action_.bind(this));
        return mark;
    },

    replace_listener: function(mark, event, func) {
        var handle = GEvent.addListener(mark, event, func);
        if (this.handles_[event]) {
            GEvent.removeListener(this.handles_[event]);
        }
        this.handles_[event] = handle;
    },

    createIcon: function(id, cc, clipped) {
        var gi = new GIcon(COMMON_ICON);
        if (clipped) {
            gi.image = this.build_rev_image_path_(id, cc);
        } else {
            gi.image = this.build_image_path_(id, cc);
        }
        this.setsize_by_cc_(gi, cc);
        return gi;
    },

    setsize_by_cc_: function(gi, cc) {
        gi.iconSize = ICON_PREF_BY_CC.iconSize;
        gi.iconAnchor = ICON_PREF_BY_CC.iconAnchor;
    },

    uniqId: function() {
        return this.uniqId_;
    },

    build_image_path_: function(number, type) {
        var ii = number + 1;
		var suffix = 'c';
		if (this.attrs_.is_open) {
			suffix = '';
		}
        var n = '/images/marker/b_' + type + '_' + ii + suffix + '.png';
        return n;
    },

    build_rev_image_path_: function(number, type) {
        var ii = number + 1;
		var suffix = 'c';
		if (this.attrs_.is_open) {
			suffix = '';
		}
        var n = '/images/marker/b_' + type + '_' + ii + 'r' + suffix + '.png';
        return n;
    },

    mark_mouseover_action_: function() {
       this.receiver_.send('mouseover', this.id_);
    },

    mark_mouseout_action_: function() {
   		if (!this.clipped_) {
	        this.receiver_.send('hide', this.id_);
	    }
    },

    mark_click_action_: function() {
        this.clipped_ = this.toggle_(this.clipped_);
        this.setup_mark();
        this.receiver_.send('click', this.id_);
    },

    toggle_: function(flag) {
        return !flag;
    },

    dig: function(flag) {
        var map = this.map_;
		if (flag) {
	        var hid = null;
        	var latlng = this.mark_.getPoint();
        	map.removeOverlay(this.mark_);
        	this.revmark_ = this.create_revmark(latlng, this.id_, this.genre_code_, this.attrs_.name);
        	map.addOverlay(this.revmark_);
		} else {
	        var latlng = this.revmark_.getPoint();
        	map.removeOverlay(this.revmark_);
        	this.mark_ = this.create_mark(latlng, this.id_, this.genre_code_, this.attrs_.name);
        	map.addOverlay(this.mark_);
		}
    },

    center: function() {
        var map = this.map_;
        map.panTo(this.mark_.getPoint());
    },

    disappear: function() {
        var map = this.map_;
        if (this.mark_) {
            map.removeOverlay(this.mark_);
        }
        if (this.revmark_) {
            map.removeOverlay(this.revmark_);
        }
        this.receiver_.send('remove', this.id_);
    }
}

var MarkersManipulator = function(map) {
    this.map_ = map;
    this.marks_ = null;
    this.objs_ = [];
    this.clipped_ = false;
    this.activeMarker_ = null;
};

MarkersManipulator.prototype = {

    acts_as_receiver: true,

    set_depts_scope: function(ids) {
		this.ids_ = null;
    },

    set_source_objs: function(objs) {
        this.objs_ = objs;
        this.objs_.each(function(value) {
            value.location.x_org = value.location.x;
            value.location.y_org = value.location.y;
        });
    },

    replace_marks_: function() {
        var fobjs = this.objs_;
        var newmarks = this.create_marks_(fobjs);
        this.remove_marks_();
        this.marks_ =  newmarks;
    },

    filter_by_depts: function(objs) {
        var ids = this.ids_;
        if (!this.ids_) return objs;
        var fobjs = new Array();
        objs.each(function(value) {
            var ok = false;
            value.departments.each(function(dept) {
                if (0 <= ids.indexOf(dept)) {
                    ok = true;
                    return;
                }
            });
            if (ok) {
                fobjs.push(value);
            }
        });
        return fobjs;
    },

    create_marks_: function(obj) {
        var scale = 19 - this.map_.getZoom();
        var matchOffset = 0.00004 * Math.pow(2, scale);
        var marks = new Array;
        var prev_x = null; var prev_y = null;
        var cur_x; var cur_y;
        for (var i = 0; i < obj.length; i++) {
            var cobj = obj[i];
            cobj.location.y = cobj.location.y_org;
            cobj.location.x = cobj.location.x_org;
            if (prev_y == cobj.location.y && prev_x == cobj.location.x) {
                cur_x = cur_x + matchOffset;
                cobj.location.x = cur_x;
            } else {
                prev_y = cobj.location.y;
                prev_x = cobj.location.x;
                cur_y = prev_y;
                cur_x = prev_x;
            }
            marks[i] = new MarkerWrapper(this.map_, cobj, i, this.dispatcher);
        }

        this.dispatcher.send('createall', obj);
        return marks;
    },

    remove_marks_: function () {
        var marks = this.marks_;
        if (marks == null) return;
        var map = this.map_;
        for (var i = 0; i < marks.length; i++) {
            map.removeOverlay(marks[i].mark_);
        }
        this.marks_ = null;
    },

    on_show: function(id) {
        var cur_id = this.cur_id_;
        if (cur_id != null && cur_id != id) {
            this.unclip_mark(this.marks_[cur_id]);
            this.clipped_ = false;
        }
        this.cur_id_ = id;
    },
	
    on_mouseover: function(id) {
            if (this.clipped_) {
                    return;
            }
    this.dispatcher.send('show', id);
    },

    on_click: function(id) {
        var cur_id = this.cur_id_;
			if (cur_id != id) {
                    if (cur_id != null) {
	            	this.unclip_mark(this.marks_[cur_id]);
		    }
		        this.cur_id_ = id;
		    	this.clipped_ = true;
		    	this.on_show(id);
		    	this.marks_[id].clip();
			} else {
				if (this.clipped_) {
			    	this.clipped_ = false;
		            this.unclip_mark(this.marks_[cur_id]);
				} else {
			    	this.clipped_ = true;
		            this.marks_[cur_id].clip();
				}
			}
    },
    
    unclip_mark: function(mark) {
        if (mark) mark.unclip();
    },
	set_center_marker: function(obj) {
		this.centerMarker_ = new MarkerWrapper(this.map_, obj, -1, this.dispatcher, {
			listen_click: false
		});
	}
}
