/*
 *
 *  Weather extension for GNOME Shell preferences 
 *  - Creates a widget to set the preferences of the weather extension
 *
 * Copyright (C) 2012
 *     Canek Peláez <canek@ciencias.unam.mx>,
 *     Christian METZLER <neroth@xeked.com>,
 *
 * This file is part of gnome-shell-extension-weather.
 *
 * gnome-shell-extension-weather is free software: you can
 * redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * gnome-shell-extension-weather is distributed in the hope that it
 * will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with gnome-shell-extension-weather.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 */

const Gtk = imports.gi.Gtk;
const GObject = imports.gi.GObject;
const GtkBuilder = Gtk.Builder;
const Gio = imports.gi.Gio;
const Gettext = imports.gettext.domain('gnome-shell-extension-weather');
const _ = Gettext.gettext;
const Soup = imports.gi.Soup;

const Lang = imports.lang;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;

const EXTENSIONDIR = Me.dir.get_path();

const WEATHER_SETTINGS_SCHEMA = 'org.gnome.shell.extensions.weather';
const WEATHER_UNIT_KEY = 'unit';
const WEATHER_PRESSURE_UNIT_KEY = 'pressure-unit';
const WEATHER_WIND_SPEED_UNIT_KEY = 'wind-speed-unit';
const WEATHER_WIND_DIRECTION_KEY = 'wind-direction';
const WEATHER_CITY_KEY = 'city';
const WEATHER_ACTUAL_CITY_KEY = 'actual-city';
const WEATHER_TRANSLATE_CONDITION_KEY = 'translate-condition';
const WEATHER_USE_SYMBOLIC_ICONS_KEY = 'use-symbolic-icons';
const WEATHER_SHOW_TEXT_IN_PANEL_KEY = 'show-text-in-panel';
const WEATHER_SHOW_WIND_IN_PANEL_KEY = 'show-wind-in-panel';
const WEATHER_POSITION_IN_PANEL_KEY = 'position-in-panel';
const WEATHER_SHOW_COMMENT_IN_PANEL_KEY = 'show-comment-in-panel';
const WEATHER_REFRESH_INTERVAL = 'refresh-interval';

// Soup session (see https://bugzilla.gnome.org/show_bug.cgi?id=661323#c64) (Simon Legner)
const _httpSession = new Soup.SessionAsync();
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());

let mCities = null;

const WeatherPrefsWidget = new GObject.Class(
{
Name: 'WeatherExtension.Prefs.Widget',
GTypeName: 'WeatherExtensionPrefsWidget',
Extends: Gtk.Box,

	_init: function(params)
	{
	this.parent(params);

	this.initWindow();

	this.refreshUI();

	this.add(this.MainWidget);
	},

	Window : new Gtk.Builder(),

	initWindow : function()
	{
	let that = this;
	mCities = null;

	this.Window.add_from_file(EXTENSIONDIR+"/weather-settings.ui");

	this.MainWidget = this.Window.get_object("main-widget");
	this.treeview = this.Window.get_object("tree-treeview");
	this.liststore = this.Window.get_object("liststore");
	this.Iter = this.liststore.get_iter_first();

		this.Window.get_object("tree-toolbutton-add").connect("clicked",function()
		{
		that.addCity();
		});

		this.Window.get_object("tree-toolbutton-remove").connect("clicked",function()
		{
		that.removeCity();
		});

		this.Window.get_object("treeview-selection").connect("changed",function(selection)
		{
		that.selectionChanged(selection);
		});

	this.treeview.set_model(this.liststore);

	let column = new Gtk.TreeViewColumn()
	this.treeview.append_column(column);

	let renderer = new Gtk.CellRendererText();
	column.pack_start(renderer,null);

		column.set_cell_data_func(renderer,function()
		{
		arguments[1].markup = arguments[2].get_value(arguments[3],0);
		});

	this.initConfigWidget();
	this.addLabel(_("Temperature Unit"));
	this.addComboBox(["\u00b0C","\u00b0F","K","\u00b0Ra","\u00b0R\u00E9","\u00b0R\u00F8","\u00b0De","\u00b0N"],"units");
	this.addLabel(_("Wind Speed Unit"));
	this.addComboBox(["km/h","mph","m/s","kn","ft/s","Beaufort"],"wind_speed_unit");
	this.addLabel(_("Pressure Unit"));
	this.addComboBox(["hPa","inHg","bar","Pa","kPa","atm","at","Torr","psi"],"pressure_unit");
	this.addLabel(_("Position in Panel"));
	this.addComboBox([_("Center"),_("Right"),_("Left")],"position_in_panel");
	this.addLabel(_("Wind Direction by Arrows"));
	this.addSwitch("wind_direction");
	this.addLabel(_("Translate Conditions"));
	this.addSwitch("translate_condition");
	this.addLabel(_("Symbolic Icons"));
	this.addSwitch("icon_type");
	this.addLabel(_("Temperature in Panel"));
	this.addSwitch("text_in_panel");
	this.addLabel(_("Conditions in Panel"));
	this.addSwitch("comment_in_panel");
	this.addLabel(_("Wind in Panel"));
	this.addSwitch("wind_in_panel");
	},

	refreshUI : function()
	{
	this.MainWidget = this.Window.get_object("main-widget");
	this.treeview = this.Window.get_object("tree-treeview");
	this.liststore = this.Window.get_object("liststore");
	this.Iter = this.liststore.get_iter_first();

	this.Window.get_object("tree-toolbutton-remove").sensitive = Boolean(this.city.length);

		if(mCities != this.city)
		{
			if(typeof this.liststore != "undefined")
			this.liststore.clear();

			if(this.city.length > 0)
			{
			let city = String(this.city).split(" && ");

				if(city && typeof city == "string")
				city = [city];

			let current = this.liststore.get_iter_first();

				for(let i in city)
				{
				current = this.liststore.append();
				this.liststore.set_value(current, 0, this.extractLocation(city[i]));
				}
			}

		mCities = this.city;
		}

	this.changeSelection();

	let config = this.configWidgets;
		for(let i in config)
			if(config[i][0].active != this[config[i][1]])
			config[i][0].active = this[config[i][1]];
	},

	initConfigWidget : function()
	{
	this.inc(1);
	let a = this.Window.get_object("right-widget-table");
	a.visible = 1;
	a.can_focus = 0;
	this.right_widget = a;
	},

	x : [0,1],

	y : [0,1],

	configWidgets : [],

	inc : function()
	{
		if(arguments[0])
		{
		this.x[0] = 0;
		this.x[1] = 1;
		this.y[0] = 0;
		this.y[1] = 1;
		return 0;
		}

		if(this.x[0] == 1)
		{
		this.x[0] = 0;
		this.x[1] = 1;
		this.y[0] += 1;
		this.y[1] += 1;
		return 0;
		}
		else
		{
		this.x[0] += 1;
		this.x[1] += 1;
		return 0;
		}
	},

	addLabel : function(text)
	{
	let l = new Gtk.Label({label:text,xalign:0});
	l.visible = 1;
	l.can_focus = 0;
	this.right_widget.attach(l, this.x[0],this.x[1], this.y[0],this.y[1],0,0,0,0);
	this.inc();
	},

	addComboBox : function(a,b)
	{
	let that = this;
	let cf = new Gtk.ComboBoxText();
	this.configWidgets.push([cf,b]);
	cf.visible = 1;
	cf.can_focus = 0;
	cf.width_request = 100;
		for(let i in a)
		cf.append_text(a[i]);
	cf.active = this[b];
	cf.connect("changed",function(){that[b] = arguments[0].active;});
	this.right_widget.attach(cf, this.x[0],this.x[1], this.y[0],this.y[1],0,0,0,0);
	this.inc();
	},

	addSwitch : function(a)
	{
	let that = this;
	let sw = new Gtk.Switch();
	this.configWidgets.push([sw,a]);
	sw.visible = 1;
	sw.can_focus = 0;
	sw.active = this[a];
	sw.connect("notify::active",function(){that[a] = arguments[0].active;});
	this.right_widget.attach(sw, this.x[0],this.x[1], this.y[0],this.y[1],0,0,0,0);
	this.inc();
	},

	selectionChanged : function(select)
	{
	let a = select.get_selected_rows(this.liststore)[0][0];

		if(typeof a != "undefined")
			if(this.actual_city != parseInt(a.to_string()))
			this.actual_city = parseInt(a.to_string());
	},

	addCity : function()
	{
	let that = this;
	let textDialog = _("Name of the city");
	let dialog = new Gtk.Dialog({title : ""});
	let entry = new Gtk.Entry();
	let completion = new Gtk.EntryCompletion();
	entry.set_completion(completion);
	let completionModel = Gtk.ListStore.new([GObject.TYPE_STRING]);
	completion.set_model(completionModel);
	completion.set_text_column(0);
	completion.set_popup_single_match(true);
	completion.set_minimum_key_length(1);
		completion.set_match_func(function(completion,key,iter)
		{
			if(iter)
			{
				if(completionModel.get_value(iter,0))
				return true;
			}
		return false;
		});
	entry.margin_top = 12;
	entry.margin_bottom = 12;
	let label = new Gtk.Label({label : textDialog});

	dialog.set_border_width(12);
	dialog.set_modal(1);
	dialog.set_resizable(0);
	//dialog.set_transient_for(***** Need parent Window *****);

	dialog.add_button(Gtk.STOCK_CANCEL, 0);
	let d = dialog.add_button(Gtk.STOCK_OK, 1);

	d.set_can_default(true);
	d.sensitive = 0;

	dialog.set_default(d);
	entry.activates_default = true;

		let testLocation = function(location)
		{
			if(location.search(/\[/) == -1 || location.search(/\]/) == -1)
			return 0;

		let woeid = location.split(/\[/)[1].split(/\]/)[0];
			if(!woeid)
			return 0;

			that.loadJsonAsync(encodeURI('https://query.yahooapis.com/v1/public/yql?q=select woeid from geo.places where woeid = "'+woeid+'" limit 1&format=json'),function()
			{
			d.sensitive = 0;
				if(typeof arguments[0].query == "undefined")
				return 0;

			let city = arguments[0].query;
				if(Number(city.count) == 0)
				return 0;

			d.sensitive = 1;
			return 0;
			},"testLocation");
		return 0;
		};

		let searchLocation = function()
		{
		let location = entry.get_text();
			if(testLocation(location) == 0)
			that.loadJsonAsync(encodeURI('https://query.yahooapis.com/v1/public/yql?q=select woeid,name,admin1,country from geo.places where text = "*'+location+'*" or text = "'+location+'"&format=json'),function()
			{
				if(!arguments[0])
				return 0;
			let city = arguments[0].query;
			let n = Number(city.count);
				if(n > 0)
				city = city.results.place;
				else
				return 0;
			completionModel.clear();

			let current = this.liststore.get_iter_first();

				if(n > 1)
				{
					for(var i in city)
					{
						if(typeof m == "undefined")
						var m = {};

					current = completionModel.append();
					let cityText = city[i].name;
						if(city[i].admin1)
						cityText += ", "+city[i].admin1.content;

						if(city[i].country)
						cityText += " ("+city[i].country.code+")";

					cityText += " ["+city[i].woeid+"]";

						if(m[cityText])
						continue;
						else
						m[cityText] = 1;

					completionModel.set_value(current,0,cityText);
					}
				}
				else
				{
				current = completionModel.append();
				let cityText = city.name;
					if(city.admin1)
					cityText += ", "+city.admin1.content;

					if(city.country)
					cityText += " ("+city.country.code+")";

				cityText += " ["+city.woeid+"]";
				completionModel.set_value(current,0,cityText);
				}
			completion.complete();
			return 0;
			},"getInfo");
		return 0;
		};

		entry.connect("changed",searchLocation);

	let dialog_area = dialog.get_content_area();
	dialog_area.pack_start(label,0,0,0);
	dialog_area.pack_start(entry,0,0,0);
		dialog.connect("response",function(w, response_id) {
		   	if(response_id)
			{
				if(entry.get_text().search(/\[/) == -1 || entry.get_text().search(/\]/) == -1)
				return 0;

			let woeid = entry.get_text().split(/\[/)[1].split(/\]/)[0];
				if(!woeid)
				return 0;

				that.loadJsonAsync(encodeURI('https://query.yahooapis.com/v1/public/yql?format=json&q=select woeid,name,admin1,country from geo.places where woeid = "'+woeid+'" limit 1'),function()
				{
				let city = arguments[0].query;
					if(Number(city.count) > 0)
					city = city.results.place;
					else
					return 0;

				let cityText = city.name;
					if(city.admin1)
					cityText += ", "+city.admin1.content;

					if(city.country)
					cityText += " ("+city.country.code+")";

					if(that.city)
					that.city = that.city+" && "+city.woeid+">"+cityText;
					else
					that.city = city.woeid+">"+cityText;
				return 0;
				},"lastTest");
			}
		dialog.hide();
		return 0;
		});

	dialog.show_all();
	},

	removeCity : function()
	{
	let that = this;
	let city = this.city.split(" && ");
		if(!city.length)
		return 0;
	let ac = this.actual_city;
	let textDialog = _("Remove %s ?").replace("%s",this.extractLocation(city[ac]));
	let dialog = new Gtk.Dialog({title : ""});
	let label = new Gtk.Label({label : textDialog});
	label.margin_bottom = 12;

	dialog.set_border_width(12);
	dialog.set_modal(1);
	dialog.set_resizable(0);
	//dialog.set_transient_for(***** Need parent Window *****);

	dialog.add_button(Gtk.STOCK_NO, 0);
	let d = dialog.add_button(Gtk.STOCK_YES, 1);

	d.set_can_default(true);
	dialog.set_default(d);

	let dialog_area = dialog.get_content_area();
	dialog_area.pack_start(label,0,0,0);
		dialog.connect("response",function(w, response_id)
		{
		   	if(response_id)
			{
				if(city.length == 0)
				city = [];

				if(city.length > 0 && typeof city != "object")
				city = [city];

				if(city.length > 0)
				city.splice(ac,1);

				if(city.length > 1)
				that.city = city.join(" && ");
				else if(city[0])
				that.city = city[0];
				else
				that.city = "";
			}
		dialog.hide();
		return 0;
		});

	dialog.show_all();
	return 0;
	},

	changeSelection : function()
	{
	let path = this.actual_city;
		if(arguments[0])
		path = arguments[0];
	path = Gtk.TreePath.new_from_string(String(path));
	this.treeview.get_selection().select_path(path);
	},

	loadJsonAsync : function(url, fun, id)
	{
        let here = this;
        let message = Soup.Message.new('GET', url);

		if(typeof this.asyncSession == "undefined")
		this.asyncSession = {};

		if(typeof this.asyncSession[id] != "undefined" && this.asyncSession[id])
		{
		_httpSession.abort();
		this.asyncSession[id] = 0;
		}

		this.asyncSession[id] = 1;
		_httpSession.queue_message(message, function(_httpSession, message)
		{
		here.asyncSession[id] = 0;
			if(!message.response_body.data)
			{
			fun.call(here,0);
			return 0;
			}

			try
			{
			let jp = JSON.parse(message.response_body.data);
			fun.call(here, jp);
			}
			catch(e)
			{
			fun.call(here,0);
			return 0;
			}
		return 0;
		});
	},

	loadConfig : function()
	{
	let that = this;
   	this.Settings = Convenience.getSettings(WEATHER_SETTINGS_SCHEMA);	
	this.Settings.connect("changed", function(){that.refreshUI();});
	},

	get units()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_enum(WEATHER_UNIT_KEY);
	},

	set units(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_enum(WEATHER_UNIT_KEY,v);
	},

	get pressure_unit()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_enum(WEATHER_PRESSURE_UNIT_KEY);
	},

	set pressure_unit(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_enum(WEATHER_PRESSURE_UNIT_KEY,v);
	},

	get wind_speed_unit()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_enum(WEATHER_WIND_SPEED_UNIT_KEY);
	},

	set wind_speed_unit(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_enum(WEATHER_WIND_SPEED_UNIT_KEY,v);
	},

	get wind_direction()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_boolean(WEATHER_WIND_DIRECTION_KEY);
	},

	set wind_direction(v)
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.set_boolean(WEATHER_WIND_DIRECTION_KEY,v);
	},

	get city()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_string(WEATHER_CITY_KEY);
	},

	set city(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_string(WEATHER_CITY_KEY,v);
	},

	get actual_city()
	{
		if(!this.Settings)
		this.loadConfig();
	let a = this.Settings.get_int(WEATHER_ACTUAL_CITY_KEY);
	let citys = this.city.split(" && ");

		if(citys && typeof citys == "string")
		citys = [citys];

	let l = citys.length-1;

		if(a < 0)
		a = 0;

		if(l < 0)
		l = 0;

		if(a > l)
		a = l;

	return a;
	},

	set actual_city(a)
	{
		if(!this.Settings)
		this.loadConfig();
	let citys = this.city.split(" && ");

		if(citys && typeof citys == "string")
		citys = [citys];

	let l = citys.length-1;

		if(a < 0)
		a = 0;

		if(l < 0)
		l = 0;

		if(a > l)
		a = l;

	this.Settings.set_int(WEATHER_ACTUAL_CITY_KEY,a);
	},

	get translate_condition()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_boolean(WEATHER_TRANSLATE_CONDITION_KEY);
	},

	set translate_condition(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_boolean(WEATHER_TRANSLATE_CONDITION_KEY,v);
	},

	get icon_type()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_boolean(WEATHER_USE_SYMBOLIC_ICONS_KEY);
	},

	set icon_type(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_boolean(WEATHER_USE_SYMBOLIC_ICONS_KEY,v);
	},

	get text_in_panel()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_boolean(WEATHER_SHOW_TEXT_IN_PANEL_KEY);
	},

	set text_in_panel(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_boolean(WEATHER_SHOW_TEXT_IN_PANEL_KEY,v);
	},
	get wind_in_panel()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_boolean(WEATHER_SHOW_WIND_IN_PANEL_KEY);
	},

	set wind_in_panel(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_boolean(WEATHER_SHOW_WIND_IN_PANEL_KEY,v);
	},

	get position_in_panel()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_enum(WEATHER_POSITION_IN_PANEL_KEY);
	},

	set position_in_panel(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_enum(WEATHER_POSITION_IN_PANEL_KEY,v);
	},

	get comment_in_panel()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_boolean(WEATHER_SHOW_COMMENT_IN_PANEL_KEY);
	},

	set comment_in_panel(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_boolean(WEATHER_SHOW_COMMENT_IN_PANEL_KEY,v);
	},

	get refresh_interval()
	{
		if(!this.Settings)
		this.loadConfig();
	return this.Settings.get_int(WEATHER_REFRESH_INTERVAL);
	},

	set refresh_interval(v)
	{
		if(!this.Settings)
		this.loadConfig();
	this.Settings.set_int(WEATHER_REFRESH_INTERVAL,v);
	},

	extractLocation : function(a)
	{
		if(a.search(">") == -1)
		return _("Invalid city");
	return a.split(">")[1];
	},

	extractWoeid : function(a)
	{
		if(a.search(">") == -1)
		return 0;
	return a.split(">")[0];
	}
});

function init()
{
Convenience.initTranslations('gnome-shell-extension-weather');
}

function buildPrefsWidget()
{
let widget = new WeatherPrefsWidget();
widget.show_all();
return widget;
}
