/*
 * SettingsWindow.vala
 *
 * Copyright 2013 Tony George <teejee2008@gmail.com>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 *
 */

using Gtk;
using Gee;

using TeeJee.Logging;
using TeeJee.FileSystem;
using TeeJee.Devices;
using TeeJee.JSON;
using TeeJee.ProcessManagement;
using TeeJee.GtkHelper;
using TeeJee.Multimedia;
using TeeJee.System;
using TeeJee.Misc;

public class SettingsWindow : Gtk.Dialog{
	private Box vbox_main;
	private Notebook notebook;

	//schedule
	private Label lbl_schedule;
	private Box vbox_schedule;
	private Box hbox_auto_snapshots;
	private Label lbl_header_schedule;
	private Switch switch_schedule;
	private TreeView tv_schedule;
	private ScrolledWindow sw_schedule;
	private TreeViewColumn col_sched_enable;
	private TreeViewColumn col_sched_level;
	private TreeViewColumn col_sched_desc;

	//auto_remove
	private Label lbl_auto_remove;
	private Box vbox_auto_remove;
	private TreeView tv_remove;
	private ScrolledWindow sw_remove;
	private TreeViewColumn col_remove_rule;
	private TreeViewColumn col_remove_limit;
	private TreeViewColumn col_remove_units;

	//actions
	private Button btn_save;
	private Button btn_cancel;

	public SettingsWindow () {
		this.deletable = false;
		this.title = _("Settings");
        this.window_position = WindowPosition.CENTER_ON_PARENT;
        this.set_destroy_with_parent (true);
		this.set_modal (true);
        this.set_default_size (450, 500);
		this.icon = get_app_icon(16);

	    //vboxMain
        vbox_main = get_content_area ();

        //notebook
		notebook = new Notebook ();
		notebook.margin = 6;
		vbox_main.pack_start (notebook, true, true, 0);

        //schedule tab ---------------------------------------------

		//lbl_schedule
		lbl_schedule = new Label (_("Schedule"));

        //vbox_schedule
        vbox_schedule = new Box (Gtk.Orientation.VERTICAL, 6);
        vbox_schedule.margin = 6;
        notebook.append_page (vbox_schedule, lbl_schedule);

		//automatic snapshots ------------------------------

        //hbox_auto_snapshots
        hbox_auto_snapshots = new Box (Gtk.Orientation.HORIZONTAL, 6);
        vbox_schedule.add (hbox_auto_snapshots);

        //lbl_header_schedule
		lbl_header_schedule = new Gtk.Label("<b>" + _("Scheduled Snapshots") + "</b>");
		lbl_header_schedule.set_use_markup(true);
		lbl_header_schedule.hexpand = true;
		lbl_header_schedule.xalign = (float) 0.0;
		lbl_header_schedule.valign = Align.CENTER;
		hbox_auto_snapshots.add(lbl_header_schedule);

		Box hbox_schedule = new Box(Orientation.HORIZONTAL,0);
        hbox_auto_snapshots.add(hbox_schedule);

		//switch_schedule
        switch_schedule = new Gtk.Switch();
        switch_schedule.set_size_request(100,20);
        switch_schedule.active = App.is_scheduled;
        hbox_auto_snapshots.pack_end(switch_schedule,false,false,0);

        switch_schedule.notify["active"].connect(switch_schedule_changed);

		//tv_schedule -----------------------------------------------

        //tv_schedule
		tv_schedule = new TreeView();
		tv_schedule.get_selection().mode = SelectionMode.MULTIPLE;
		tv_schedule.set_rules_hint (true);

		//sw_schedule
		sw_schedule = new ScrolledWindow(null, null);
		sw_schedule.set_shadow_type (ShadowType.ETCHED_IN);
		sw_schedule.add (tv_schedule);
		sw_schedule.expand = true;
		vbox_schedule.add(sw_schedule);

		//col_sched_enable
		col_sched_enable = new TreeViewColumn();
		col_sched_enable.title = " " + _("Enable") + " ";
		CellRendererToggle cell_sched_enable = new CellRendererToggle ();
		cell_sched_enable.activatable = true;
		cell_sched_enable.toggled.connect (cell_sched_enable_toggled);
		col_sched_enable.pack_start (cell_sched_enable, false);
		col_sched_enable.set_cell_data_func (cell_sched_enable, cell_sched_enable_render);
		tv_schedule.append_column(col_sched_enable);

		//col_sched_level
		col_sched_level = new TreeViewColumn();
		col_sched_level.title = " " + _("Backup Level") + " ";
		CellRendererText cell_sched_level = new CellRendererText ();
		col_sched_level.pack_start (cell_sched_level, false);
		col_sched_level.set_cell_data_func (cell_sched_level, cell_sched_level_render);
		tv_schedule.append_column(col_sched_level);

		//col_sched_desc
		col_sched_desc = new TreeViewColumn();
		col_sched_desc.title = " " + _("Description") + " ";
		col_sched_desc.expand = true;
		CellRendererText cell_sched_desc = new CellRendererText ();
		col_sched_desc.pack_start (cell_sched_desc, false);
		col_sched_desc.set_cell_data_func (cell_sched_desc, cell_sched_desc_render);
		tv_schedule.append_column(col_sched_desc);

		//auto-remove tab ------------------------------------------------------

		//lbl_auto_remove
		lbl_auto_remove = new Label (_("Auto-Remove"));

        //vbox_auto_remove
        vbox_auto_remove = new Box(Gtk.Orientation.VERTICAL, 6);
        vbox_auto_remove.margin = 6;
        notebook.append_page (vbox_auto_remove, lbl_auto_remove);

		//lbl_auto_remove_msg
		Label lbl_auto_remove_msg = new Label (_("Snapshots matching following rules will be removed automatically"));
		lbl_auto_remove_msg.xalign = (float) 0.0;
		vbox_auto_remove.add(lbl_auto_remove_msg);

		//tv_remove
		tv_remove = new TreeView();
		tv_remove.get_selection().mode = SelectionMode.MULTIPLE;
		tv_remove.set_rules_hint (true);

		//sw_remove
		sw_remove = new ScrolledWindow(null, null);
		sw_remove.set_shadow_type (ShadowType.ETCHED_IN);
		sw_remove.add (tv_remove);
		sw_remove.expand = true;
		vbox_auto_remove.add(sw_remove);

		//col_remove_rule
		col_remove_rule = new TreeViewColumn();
		col_remove_rule.title = " " + _("Rule") + " ";
		CellRendererText cell_remove_rule = new CellRendererText ();
		col_remove_rule.pack_start (cell_remove_rule, false);
		col_remove_rule.set_cell_data_func (cell_remove_rule, cell_remove_rule_render);
		tv_remove.append_column(col_remove_rule);

		//col_sched_desc
		col_remove_limit = new TreeViewColumn();
		col_remove_limit.title = " " + _("Limit") + " ";
		CellRendererText cell_remove_limit = new CellRendererText ();
		cell_remove_limit.xalign = (float) 0.5;
		cell_remove_limit.editable = true;
		cell_remove_limit.edited.connect (cell_remove_limit_edited);
		col_remove_limit.pack_start (cell_remove_limit, false);
		col_remove_limit.set_cell_data_func (cell_remove_limit, cell_remove_limit_render);
		tv_remove.append_column(col_remove_limit);

		//col_remove_rule
		col_remove_units = new TreeViewColumn();
		col_remove_units.title = "";
		CellRendererText cell_remove_units = new CellRendererText ();
		col_remove_units.pack_start (cell_remove_units, false);
		col_remove_units.set_cell_data_func (cell_remove_units, cell_remove_units_render);
		tv_remove.append_column(col_remove_units);

		tv_schedule.sensitive = switch_schedule.active;

		//initialize ------------------

		refresh_tv_schedule();
		refresh_tv_remove();

		// Actions ----------------------------------------------

        //btn_save
        btn_save = (Button) add_button ("gtk-save", Gtk.ResponseType.ACCEPT);
        btn_save.clicked.connect (btn_save_clicked);

        //btn_cancel
        btn_cancel = (Button) add_button ("gtk-cancel", Gtk.ResponseType.CANCEL);
        btn_cancel.clicked.connect (btn_cancel_clicked);
	}


	private void cell_sched_enable_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
		bool val;
		model.get (iter, 0, out val, -1);
		(cell as Gtk.CellRendererToggle).active = val;
	}

	private void cell_sched_level_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
		string val;
		model.get (iter, 1, out val, -1);
		(cell as Gtk.CellRendererText).text = val;
	}

	private void cell_sched_desc_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
		string val;
		model.get (iter, 2, out val, -1);
		(cell as Gtk.CellRendererText).text = val;
	}

	private void cell_remove_rule_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
		string val;
		model.get (iter, 0, out val, -1);
		(cell as Gtk.CellRendererText).markup = val;
	}

	private void cell_remove_limit_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
		int val;
		string units;
		model.get (iter, 1, out val, 2, out units, -1);
		(cell as Gtk.CellRendererText).text = (units == "GB") ? "%.0f".printf(val/1024.0) : val.to_string();
		(cell as Gtk.CellRendererText).background = "#F2F5A9";
	}

	private void cell_remove_units_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
		string val;
		model.get (iter, 2, out val, -1);
		(cell as Gtk.CellRendererText).text = val;
	}

	private void cell_sched_enable_toggled (string path){
		TreeIter iter;
		var model = (Gtk.ListStore)tv_schedule.model;
		bool enabled;
		int count;

		model.get_iter_from_string (out iter, path);
		model.get (iter, 0, out enabled, 2, out count,-1);
		model.set (iter, 0, !enabled);

		//update switch_auto_backups
		bool atleast_one_enabled = false;
		for(int k = 0; k<5; k++){
			model.get_iter_from_string (out iter, k.to_string());
			model.get (iter, 0, out enabled, -1);
			if (enabled){
				atleast_one_enabled = true;
				break;
			}
		}

		switch_schedule.active = atleast_one_enabled;
	}

	private void cell_remove_limit_edited (string path, string new_text) {
		int count = 0;
		string units;

		TreeIter iter;
		var model = (Gtk.ListStore)tv_remove.model;
		model.get_iter_from_string (out iter, path);
		model.get (iter, 2, out units,-1);
		count = int.parse(new_text);
		if (units == "GB"){
			count = count * 1024;
		}
		model.set (iter, 1, count);
	}

	private void refresh_tv_schedule(){

		var model = new Gtk.ListStore(3, typeof(bool), typeof(string), typeof(string));

		TreeIter iter;
		model.append(out iter);
		model.set (iter, 0, App.schedule_monthly);
		model.set (iter, 1, _("Monthly"));
		model.set (iter, 2, _("Keep one snapshot per month"));

		model.append(out iter);
		model.set (iter, 0, App.schedule_weekly);
		model.set (iter, 1, _("Weekly"));
		model.set (iter, 2, _("Keep one snapshot per week"));

		model.append(out iter);
		model.set (iter, 0, App.schedule_daily);
		model.set (iter, 1, _("Daily"));
		model.set (iter, 2, _("Keep one snapshot per day"));

		model.append(out iter);
		model.set (iter, 0, App.schedule_hourly);
		model.set (iter, 1, _("Hourly"));
		model.set (iter, 2, _("Keep one snapshot per hour"));

		model.append(out iter);
		model.set (iter, 0, App.schedule_boot);
		model.set (iter, 1, _("Boot"));
		model.set (iter, 2, _("Keep one snapshot per reboot"));

		tv_schedule.set_model (model);
		tv_schedule.columns_autosize ();
	}

	private void refresh_tv_remove(){

		var model = new Gtk.ListStore(3, typeof(string), typeof(int), typeof(string));

		string span = "<span foreground=\"#2E2E2E\">";
		if (switch_schedule.active){
			span = "<span foreground=\"blue\">";
		}
		else{
			span = "<span>";
		}

		TreeIter iter;
		model.append(out iter);
		model.set (iter, 0, span + _("Monthly") + "</span> " + _("snapshots older than"));
		model.set (iter, 1, App.count_monthly);
		model.set (iter, 2, "months");

		model.append(out iter);
		model.set (iter, 0, span + _("Weekly") + "</span> " + _("snapshots older than"));
		model.set (iter, 1, App.count_weekly);
		model.set (iter, 2, "weeks");

		model.append(out iter);
		model.set (iter, 0, span + _("Daily") + "</span> " + _("snapshots older than"));
		model.set (iter, 1, App.count_daily);
		model.set (iter, 2, "days");

		model.append(out iter);
		model.set (iter, 0, span + _("Hourly") + "</span> " + _("snapshots older than"));
		model.set (iter, 1, App.count_hourly);
		model.set (iter, 2, "hours");

		model.append(out iter);
		model.set (iter, 0, span + _("Boot") + "</span> " + _("snapshots older than"));
		model.set (iter, 1, App.count_boot);
		model.set (iter, 2, "reboots");

		model.append(out iter);
		model.set (iter, 0, _("All snapshots older than"));
		model.set (iter, 1, App.retain_snapshots_max_days);
		model.set (iter, 2, "days");

		model.append(out iter);
		model.set (iter, 0, _("When free space less than"));
		model.set (iter, 1, App.minimum_free_disk_space_mb);
		model.set (iter, 2, "GB");

		tv_remove.set_model (model);
		//tv_remove.columns_autosize ();
	}

	private void switch_schedule_changed(){
		tv_schedule.sensitive = switch_schedule.active;

		if (switch_schedule.active){

			bool atleast_one_enabled = App.schedule_boot
			|| App.schedule_hourly
			|| App.schedule_daily
			|| App.schedule_weekly
			|| App.schedule_monthly;

			if(!atleast_one_enabled){
				TreeIter iter;
				var store = (Gtk.ListStore) tv_schedule.model;
				bool enabled;
				int index = -1;

				bool iterExists = store.get_iter_first (out iter);
				while (iterExists) {
					store.get (iter, 0, out enabled,-1);
					switch(++index){
						case 4:
							store.set (iter, 0, true);
							break;
					}
					iterExists = store.iter_next (ref iter);
				}
			}
		}
		else{
			tv_schedule.get_selection().unselect_all();
			tv_remove.get_selection().unselect_all();
		}

		refresh_tv_remove(); //refresh the item colors
	}

	private void btn_save_clicked(){
		tv_schedule_save_changes();
		tv_remove_save_changes();

		App.save_app_config();
		App.cron_job_update();

		this.destroy();
	}

	private void tv_schedule_save_changes(){
		TreeIter iter;
		TreeModel store = tv_schedule.model;
		bool enabled;
		int index = -1;

		bool iterExists = store.get_iter_first (out iter);

		while (iterExists) {

			store.get (iter, 0, out enabled,-1);

			switch(++index){
				case 0:
					App.schedule_monthly = enabled;
					break;
				case 1:
					App.schedule_weekly = enabled;
					break;
				case 2:
					App.schedule_daily = enabled;
					break;
				case 3:
					App.schedule_hourly = enabled;
					break;
				case 4:
					App.schedule_boot = enabled;
					break;
			}
			iterExists = store.iter_next (ref iter);
		}

		App.is_scheduled = switch_schedule.active;
	}

	private void tv_remove_save_changes(){
		TreeIter iter;
		TreeModel store = tv_remove.model;
		int count = -1;
		int index = -1;

		bool iterExists = store.get_iter_first (out iter);

		while (iterExists) {

			store.get (iter, 1, out count, -1);

			switch(++index){
				case 0:
					App.count_monthly = count;
					break;
				case 1:
					App.count_weekly = count;
					break;
				case 2:
					App.count_daily	= count;
					break;
				case 3:
					App.count_hourly = count;
					break;
				case 4:
					App.count_boot = count;
					break;
				case 5:
					App.retain_snapshots_max_days = count;
					break;
				case 6:
					App.minimum_free_disk_space_mb = count;
					break;
			}
			iterExists = store.iter_next (ref iter);
		}
	}

	private void btn_cancel_clicked(){
		this.destroy();
	}

}
