/* -*- Mode: vala; tab-width: 4; intend-tabs-mode: t -*- */
/* alm
 *
 * Copyright (C) 2012 Seif Lotfy <seif@lotfy.com>
 * Copyright (C) 2012 Manish Sinha <manishsinha@ubuntu.com>
 * Copyright (C) 2012 Intel Corp.
 *               Authored by: Seif Lotfy <seif.lotfy@collabora.co.uk>
 * Copyright (C) 2012 Stefano Candori <stefano.candori@gmail.com>
 *
 * alm is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * 
 * alm 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
 */

using Gtk;
using Gee;
using Zeitgeist;

namespace Alm {

	public class FileTypeBlacklist {
		private Blacklist blacklist_interface;
		private HashMap<string, CheckButton> checkboxes;

		public static string interpretation_prefix = "interpretation-";
		
		public FileTypeBlacklist (Blacklist blacklist_inter,
					HashMap<string, CheckButton> all_checkboxes) {
			blacklist_interface = blacklist_inter;
			checkboxes = all_checkboxes;
		}

		private string get_name(string interpretation) {
			var names = interpretation.split("#");
			var name = names[names.length-1].down();
			return "%s%s".printf(interpretation_prefix, name);
		}

		public void populate_file_types() {

			foreach(string key in blacklist_interface.all_templates.get_keys()) {
				if(key.has_prefix(this.interpretation_prefix))
				{
					var inter = blacklist_interface.all_templates[key].get_subject(0).interpretation;
					checkboxes.get(inter).active = checkboxes.has_key(inter) ? false: true;
				}
			}
		}

		public void block(string interpretation) {
			Event ev = new Event();
			Subject sub = new Subject();
			sub.interpretation = interpretation;
			ev.add_subject(sub);

			blacklist_interface.add_template(
					this.get_name(interpretation), ev);
		}

		public void unblock(string interpretation) {
			blacklist_interface.remove_template(
					this.get_name(interpretation));
		}
	}

	public class PathBlacklist {
		private Blacklist blacklist_interface;

		public static string folder_prefix = "dir-";
		private static string suffix = "/*";

		private HashSet<string> all_blocked_folder;

		public signal void folder_added(string path);

		public signal void folder_removed(string path);
		
		public PathBlacklist (Blacklist blacklist_inter) {
			blacklist_interface = blacklist_inter;
			this.blacklist_interface.template_added.connect(on_blacklist_added);
			this.blacklist_interface.template_removed.connect(on_blacklist_removed);

			this.get_blocked_folder ();
		}

		public HashSet<string> all_folders {
			get {
				return all_blocked_folder;
			}
		}

		public bool is_duplicate (string path) {
			return all_blocked_folder.contains (path);
		}

		private void get_blocked_folder () {
			all_blocked_folder = new HashSet<string>();

			foreach(string key in blacklist_interface.all_templates.get_keys()) {
				if(key.has_prefix(folder_prefix))
				{
					string folder = get_folder(blacklist_interface.all_templates.get(key));
					if(folder != null)
						all_blocked_folder.add(folder);
				}
			}
		}

		private void on_blacklist_added (string blacklist_id, Event ev) {
			if(blacklist_id.has_prefix(folder_prefix))
			{
				string uri = get_folder(ev);
				if(uri != null)
				{
					folder_added(uri);
					if(!all_blocked_folder.contains(uri))
						all_blocked_folder.add(uri);
				}
			}
		}

		private void on_blacklist_removed (string blacklist_id, Event ev) {
			if(blacklist_id.has_prefix(folder_prefix))
			{
				string uri = get_folder(ev);
				if(uri != null)
				{
					folder_removed(uri);
					if(all_blocked_folder.contains(uri))
						all_blocked_folder.remove(uri);
				}
			}
		}

		private string get_folder(Event ev) {
			Subject sub = ev.get_subject(0);
			string uri = sub.uri;
			uri = uri.replace(suffix, "");
			File blocked_uri = File.new_for_uri(uri);
			
			string final_path = blocked_uri.query_exists(null)? blocked_uri.get_path(): null;

			return final_path;
		}

		public void block(string folder) {
			Event ev = new Event ();
			Subject sub = new Subject ();
			
			File block_path = File.new_for_path(folder);
			string uri = "%s%s".printf(block_path.get_uri(), suffix);
			sub.uri = uri;
			ev.add_subject(sub);

			blacklist_interface.add_template(
					"%s%s".printf(folder_prefix, folder), ev);

			if(!all_blocked_folder.contains(folder))
					all_blocked_folder.add(folder);
		}

		public void unblock(string folder) {
			blacklist_interface.remove_template(
					"%s%s".printf(folder_prefix, folder));

			if(all_blocked_folder.contains(folder))
					all_blocked_folder.remove(folder);
		}
	}




//Based on gnome-contacts contacts-cell-renderer-shape.vala
public class FilesCellRenderer : CellRenderer {

	private const int PIXBUF_SIZE = 24;
	private const int xspacing = 3; 
	
	private const int default_width = 60;
	private const int renderer_height = 50;
	
	private Widget current_widget;
	
	private Gdk.Pixbuf pixbuf_;
	private string text_;
	private string path_;
	
	public Gdk.Pixbuf pixbuf {
			get {
				return pixbuf_;
			}
			set {
				pixbuf_ = value;
			}
	}
	
	public string text {
			get {
				return text_;
			}
			set {
				text_ = value;
			}
	}
	
	public string path {
			get {
				return path_;
			}
			set {
				path_ = value;
			}
	}
	
	public FilesCellRenderer () {
		GLib.Object ();
	}
	
	private Pango.Layout get_text_layout (Widget widget,
					Gdk.Rectangle? cell_area,
					CellRendererState flags,
					string text,
					bool bold,
					int size) {
		Pango.Layout layout;
		int xpad;
		var attr_list = new Pango.AttrList ();

		layout = widget.create_pango_layout (text);

		var attr = new Pango.AttrSize (size * Pango.SCALE);
		attr.absolute = 1;
		attr.start_index = 0;
		attr.end_index = attr.start_index + text.length;
		attr_list.insert ((owned) attr);

		if (bold) {
			var desc = new Pango.FontDescription();
			desc.set_weight (Pango.Weight.BOLD);
			var attr_f = new Pango.AttrFontDesc (desc);
			attr_list.insert ((owned) attr_f);
		}
		layout.set_attributes (attr_list);

		get_padding (out xpad, null);

		layout.set_ellipsize (Pango.EllipsizeMode.END);

		Pango.Rectangle rect;
		int width, text_width;

		layout.get_extents (null, out rect);
		text_width = rect.width;

		if (cell_area != null)
			width = (cell_area.width - xpad) * Pango.SCALE;
		else
			width = default_width * Pango.SCALE;

		width = int.min (width, text_width);
		layout.set_width (width);

		Pango.Alignment align;
		if (widget.get_direction () == TextDirection.RTL)
			align = Pango.Alignment.RIGHT;
		else
			align = Pango.Alignment.LEFT;
		layout.set_alignment (align);

		return layout;
	}

	public override void get_size (Widget        widget,
				 Gdk.Rectangle? cell_area,
				 out int       x_offset,
				 out int       y_offset,
				 out int       width,
				 out int       height) {
		x_offset = y_offset = width = height = 0;
		// Not used
	}

	private void do_get_size (Widget       widget,
				Gdk.Rectangle? cell_area,
				Pango.Layout? layout,
				out int       x_offset,
				out int       y_offset) {
		Pango.Rectangle rect;
		int xpad, ypad;

		get_padding (out xpad, out ypad);

		layout.get_pixel_extents (null, out rect);

		if (cell_area != null) {
			rect.width  = int.min (rect.width, cell_area.width - 2 * xpad + xspacing);

		if (widget.get_direction () == TextDirection.RTL)
			x_offset = cell_area.width - (rect.width + xpad);
		else
			x_offset = xpad;

		x_offset = int.max (x_offset, 0);
		} else {
			x_offset = 0;
		}

		y_offset = ypad;
	}


	public override void render (Cairo.Context   cr,
			       Widget          widget,
			       Gdk.Rectangle   background_area,
			       Gdk.Rectangle   cell_area,
			       CellRendererState flags) {
		StyleContext context;
		Pango.Layout text_layout, path_layout;
		int text_x_offset = 0;
		int path_x_offset = 0;
		int text_y_offset = 0;
		int path_y_offset = 0;
		int xpad;
		Pango.Rectangle text_rect, path_rect;

		current_widget = widget;

		context = widget.get_style_context ();
		var font_size = context.get_font (StateFlags.NORMAL).get_size () / Pango.SCALE;
		get_padding (out xpad, null);

		text_layout = get_text_layout (widget, cell_area, flags, text, true, font_size);
		do_get_size (widget, cell_area, text_layout, out text_x_offset, out text_y_offset);
		text_layout.get_pixel_extents (null, out text_rect);
		text_x_offset = text_x_offset - text_rect.x;

		path_layout = get_text_layout (widget, cell_area, flags, path, false, font_size - 1);
		do_get_size (widget, cell_area, path_layout, out path_x_offset, out path_y_offset);
		path_layout.get_pixel_extents (null, out path_rect);
		path_x_offset = path_x_offset - path_rect.x;

		cr.save ();

		Gdk.cairo_rectangle (cr, cell_area);
		cr.clip ();

		Gdk.cairo_set_source_pixbuf (cr, this.pixbuf, cell_area.x, cell_area.y);
		cr.paint ();

		context.render_layout (cr,
				cell_area.x + text_x_offset + this.pixbuf.width+ xspacing ,
				cell_area.y + text_y_offset + 2,
				text_layout);
		context.render_layout (cr,
				cell_area.x + path_x_offset,
				cell_area.y + path_y_offset + renderer_height - 11 - path_layout.get_baseline () / Pango.SCALE,
				path_layout);
		cr.restore ();
	}
	
	public override void get_preferred_width (Widget       widget,
					    out int      min_width,
					    out int      nat_width) {
		int xpad;

		get_padding (out xpad, null);

		nat_width = min_width = xpad + default_width;
	}

	public override void get_preferred_height_for_width (Widget       widget,
						       int          width,
						       out int      minimum_height,
						       out int      natural_height) {
		int ypad;

		get_padding (null, out ypad);
		minimum_height = renderer_height + ypad;
		natural_height = renderer_height + ypad;
	}

	public override void get_preferred_height (Widget       widget,
					     out int      minimum_size,
					     out int      natural_size) {
		int min_width;

		get_preferred_width (widget, out min_width, null);
		get_preferred_height_for_width (widget, min_width,
					out minimum_size, out natural_size);
	}
}
}
