var HelpOnInput = Class.create();

HelpOnInput.prototype = {
    initialize: function(element, helptext) {
        var elm = $(element);
        this.elm_ = elm;
        this.helptext_ = helptext;
	this.elm_.value = '';
        this.event_onblur();
    },
    
    event_onfocus: function() {
		if (this.elm_.value == this.helptext_) this.elm_.value = '';
		this.elm_.style.color = '#222';
    },
    
    event_onblur: function() {
		if (this.elm_.value != '') return;
		this.elm_.style.color = '#999';
		this.elm_.value = this.helptext_;
    }
};

var AddressSearcher = Class.create();
AddressSearcher.prototype = {
	lat_: null,
	lng_: null,
	deftimer_: null,
	found_help_: null,
	opts_: null,
	initialize: function(elm_oc, elm_disp, frequency, opts) {
	var hoi = new HelpOnInput(elm_oc, '住所または駅名などの一部を入力');
        var eo = $(elm_oc);
	var ed = $(elm_disp);
	ed.innerHTML = '<div id="ah_innerblock" style="width: 100%; overflow: none; display: block; opacity: 1.0; filter: alpha(opacity=100)"><div id="ah_geoblock"></div><div id="ah_officeblock"></div></div>';
		if (!opts) opts = {};
		this.opts_ = opts;
		this.opts_.name = {
			innerblock: 'ah_innerblock',
			geoblock: 'ah_geoblock',
			officeblock: 'ah_officeblock'
		}
		if (!this.opts_.click_event_label) this.opts_.click_event_label = 'pan_to_center';
		this.deftimer_ = new DeferrableTimer(this.execute_geocoding.bind(this), frequency * 1000 + 500);
        new Form.Element.Observer(eo, frequency, this.check_onchange.bind(this));
        this.elm_oc_ = eo;
        this.elm_disp_ = ed;
		this.elm_disp_.style.width = (this.elm_oc_.getDimensions().width - 2) + 'px';
                if (window['GClientGeocoder']) {
		  this.no_gmap = false;
                } else {
		  this.no_gmap = true;
                }
		if (!this.no_gmap) {
			this.geocoder_ = new GClientGeocoder();
			this.geocoder_.setBaseCountryCode('JP');
		} else {
			$(this.opts_.name.innerblock).removeChild($(this.opts_.name.geoblock));
		}
		this.shown_height_ = this.elm_disp_.getHeight();

		var me = this;
                
		this.elm_oc_.onblur = function() {
			hoi.event_onblur();
                        setTimeout(after, 100);
                        function after(){
                            me.hide_result();
                        }
		}
		this.elm_oc_.onfocus = function() {
			hoi.event_onfocus();
			me.show_result();
		}
    },
	
    setJumpTo: function(name) {
        this.jump_to = name;
    },

	check_onchange: function(element, value) {
		this.deftimer_.start(element, value);
	},
	
	hide_result: function() {
		this.shown_height_ = this.elm_disp_.getHeight();
		$jq(this.elm_disp_).slideUp(250);
//		new Effect.BlindUp(this.elm_disp_, { duration: 0.25 });
	},
	
	show_result: function() {
//		this.elm_disp_.style.display = 'block';
		this.elm_disp_.show();
		this.elm_disp_.style.height = this.shown_height_ + 'px';
	},

    execute_geocoding: function(element, value) {
        if (value == '') {
			this.query_value_ = '';
			return;
	}
	this.query_value_ = value;
        
	if (!this.no_gmap) {
                $(this.opts_.name.geoblock).innerHTML = '<ul style="list-style-type: none"><li><img src="/images/ajax-loader.gif" width="16" height="16"/></li></ul>';
		this.geocoder_.getLocations(value, this.event_onlocalsearch.bind(this));
	} else {
                $(this.opts_.name.officeblock).innerHTML = '<ul style="list-style-type: none"><li><img src="/images/ajax-loader.gif" width="16" height="16"/></li></ul>';
                return; // 施設情報を検索しにいかない。
		this.execute_sites_search();
	}
/*		new Ajax.Request('/qbt', {
			method: 'get',
			parameters: 'max=5&text=' + value,
			onComplete: this.event_ontextsearch.bind(this)
		});
*/    },

    build_html_onlocalsearch: function(results) {
        var telm = $(this.opts_.name.geoblock);
		var t1 = new Template('<ul style="list-style-image: url(/images/s_place.png); margin-top: 0px">#{list}</ul>');
		var t2 = new Template('<li><a href="" id="loc#{id}">#{name}</a></li>');
		var list = [];
		var pms = [];
		var pmindex = 0;
        results.Placemark.each(function(pm) {
			if (pm.AddressDetails.Country.CountryNameCode == 'JP') {
				if(!pm.address) {
					return;
				}
				// 先頭に"日本"が追加されてしまうので削除する
				pm.address = pm.address.replace(/^(日本|Japan)/, '');
				list.push(t2.evaluate({
					name: pm.address,
					id: pmindex + 1
				}));
				pm.pmindex = pmindex;
				pmindex += 1;
				pms.push(pm);
			}
		});
        telm.innerHTML = t1.evaluate({list: list.join('')});
		var me = this;
		pms.each(function(pm) {
			var elm = $('loc' + (pm.pmindex + 1));
			elm.onclick = me.execute_click.bind(me,
				pm.Point.coordinates[1], pm.Point.coordinates[0],
				pm.address, null);
        });
	},

    event_onlocalsearch: function(results) {
		var fh = this.opts_.found_help;
        var telm = $(this.opts_.name.geoblock);
		if (results.Status.code == 200) {
			this.build_html_onlocalsearch(results);
		} else {
	        telm.innerHTML = '';
		}
		this.execute_sites_search();
	},
	
	execute_sites_search: function() {
		this.elm_disp_.style.height = $(this.opts_.name.innerblock).getHeight() + 'px';
                return; // 施設情報を検索しにいかない。
		if (this.query_value_) {
			new Ajax.Request('/qbt', {
				method: 'get',
				parameters: 'max=5&text=' + encodeURIComponent(this.query_value_),
				onComplete: this.event_ontextsearch.bind(this)
			});
		}
    },
	
	event_ontextsearch: function(transport) {
		var docs =  eval('(' + transport.responseText + ')');
		var bufs = [];
		var t1 = new Template('<ul style="list-style-image: url(/images/s_building.png)">#{list}</ul>');
		var t2 = new Template('<li><a href="" id="doc#{id}">#{name}</a> <span style="color: #1f1f1f">(#{pref}#{city})</span></li>');
		var list = [];
		docs.each(function(doc) {
			list.push(t2.evaluate(doc));
		});
		$(this.opts_.name.officeblock).innerHTML = t1.evaluate({
			list: list.join('')
		});
		var me = this;
		docs.each(function(doc) {
			var elm = $('doc' + doc.id);
			if (elm) {
				elm.onclick = me.execute_click.bind(me, doc.location.y, doc.location.x, doc.pref, doc.class_code);
                        }
		});
		this.elm_disp_.style.height = $(this.opts_.name.innerblock).getHeight() + 'px';
	},
	
	execute_click: function(lat, lng, label, cc) {
		this.dispatcher.send(this.opts_.click_event_label, lat, lng);
		return false;
	}
}

var DeferrableTimer = Class.create();

DeferrableTimer.prototype = {
	func_: null,
	timer_id_: null,
	timeout_: null,
	running_: null,
	arguments_: null,
	
	initialize: function(func_, timeout) {
		this.func_ = func_;
		this.timeout_ = timeout;
		this.running_ = false;
	},
	
	start: function() {
		if (this.running_) {
			clearTimeout(this.timer_id_);
		}
		this.arguments_ = arguments;
		this.running_ = true;
		this.timer_id_ = setTimeout(this.execute_.bind(this), this.timeout_);
	},
	
	execute_: function() {
		var result = this.func_.apply(this, this.arguments_);
		this.running_ = false;
		return result;
	}
}

var Dispatcher = Class.create();
Dispatcher.prototype = {

    initialize: function () {
        this.callbacks_ = new Array();
    },

    register: function(object) {
        object.dispatcher = this;
        this.callbacks_.push(object);
        if (object.register_dispatchers) {
            object.register_dispatchers(this);
        }
    },

    unregister: function(func) {
        this.callbacks_ = this.callbacks_.without(func);
    },
    
    send: function() {
        var args = Array.from(arguments);
        var fn = 'on_' + args.shift();
        this.callbacks_.each(function(callback, index) {
            Dispatcher.call_by_name(callback, fn, args);
            if (callback.forwarders) {
                fwds = callback.forwarders;
                fwds.each(function(forwarder) {
                    Dispatcher.call_by_name(forwarder, fn, args);
                });
            }
        });
    }
    
}

Dispatcher.call_by_name = function(callback, name, args) {
    if (callback[name]) {
        return callback[name].apply(callback, args);
    }
    return null;
}
