
/* Gnome Encfs Manager (GEncfsM)
 * Copyright (C) 2011 Moritz Molch <mail@moritzmolch.de>
 
 * 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 Encfs;
using Gtk;
using Gee;
using GLib;
using Secret;

namespace GnomeEncfsManager {
    public class AddStash : GLib.Object {

        private static Window window;
        private static Builder builder;

		private static ulong sighandler;
        private static int atomic = 1;

        private static Entry entry_stash_dir;
        private static Entry entry_mount_dir;
        private static Label label_status;
        private static bool import_mode;
        private static string encfs_status;

        private static string encfs_file;
        private static string stash_dir_default;
        private static string stash_dir_custom;
        private static string mount_dir_default;
        private static string mount_dir_custom;
        
        private static string mount_dir_default_override = null;
        private static string stash_dir_default_override = null;
        private static string config_override = null;
        
        private static int busy_id;

        public static void init() {
            mount_dir_default_override = null;
            stash_dir_default_override = null;
            config_override = null;
        }

        public static void show() {
            if (window != null)
                return;
        
            try {
            	atomic = 1;
                builder = new Builder();
                builder.add_from_file(get_glade_file("add_stash.ui"));
                builder.connect_signals(null);
                window = (Window)builder.get_object("add_stash");
                
                entry_stash_dir = (Entry)builder.get_object("entry_stash_dir");
                entry_mount_dir = (Entry)builder.get_object("entry_mount_dir");
                label_status = (Label)builder.get_object("label_status");
				
                update();
                window.show_all();

            } catch (GLib.Error e) {
                stderr.printf("Could not load UI: %s\n", e.message);
            }
        }
        
        public static void show_with_custom_defaults(string stash_dir, string mount_dir, string config) {
            stash_dir_default_override = stash_dir;
            mount_dir_default_override = mount_dir;
            if (config == "") {
                config_override = null;
            } else {
                config_override = config;
            }
            show();
        }
        
        public static void update() {
            if (window == null)
                return;
                
            string encfs_mount_dir_base = Stash.get_mount_dir_base();
            
            if (stash_dir_default_override == null) {
	            stash_dir_default = Path.build_filename(encfs_mount_dir_base, ".%s".printf(_("Private")));
	        } else {
	        	stash_dir_default = stash_dir_default_override;
	        }
            for (int i = 0; i < stashes.size; i++) {
            gen_mount_dir_num_max = 0;
		        foreach (Stash s in stashes) {
		        	if (s.cryptDir == stash_dir_default) {
		        		stash_dir_default = gen_mount_dir(stash_dir_default);
		        	}
		        }
		    }
            ((RadioButton)builder.get_object("radiobutton_stash_dir_default")).set("label", stash_dir_default);

            stash_dir_custom = entry_stash_dir.text;
    		if (entry_stash_dir.text[0] != '/') {
    			stash_dir_custom = Path.build_filename(encfs_mount_dir_base, entry_stash_dir.text);
    		}
    		if (entry_stash_dir.text.length > 2) {
    		    if (entry_stash_dir.text[0:2] == "~/") {
       			    stash_dir_custom = Path.build_filename(Environment.get_home_dir(), entry_stash_dir.text[2:entry_stash_dir.text.length]);
       			}
    		}
            
			mount_dir_custom = entry_mount_dir.text;
    		if (entry_mount_dir.text[0] != '/') {
    			mount_dir_custom = Path.build_filename(encfs_mount_dir_base, entry_mount_dir.text);
    		}
    		if (entry_mount_dir.text.length > 2) {
    		    if (entry_mount_dir.text[0:2] == "~/") {
       			    mount_dir_custom = Path.build_filename(Environment.get_home_dir(), entry_mount_dir.text[2:entry_mount_dir.text.length]);
       			}
    		}

            import_mode = false;
            if (config_override == null) {
                string[] filenames = settings.get_strv("encfs-config-files");
                foreach (string filename in filenames) {
                    if (filename[0] == '/' || filename[0] == '~') {
		                if (FileUtils.test( filename, FileTest.EXISTS )) {
	                	    encfs_file = filename;
					        import_mode = true;
		                }
		            } else {
                    	if (((RadioButton)builder.get_object("radiobutton_stash_dir_custom")).get_active()) {
	                    	if (FileUtils.test( Path.build_filename(stash_dir_custom, filename), FileTest.EXISTS )) {
	                    	    encfs_file = filename;
					            import_mode = true;
	                    	}
	                    } else {
	                    	if (FileUtils.test( Path.build_filename(stash_dir_default, filename), FileTest.EXISTS )) {
	                    	    encfs_file = filename;
	                        	import_mode = true;
	                    	}
	                    }
                    }
                }
            } else {
                if (config_override[0] == '/' || config_override[0] == '~') {
	                if (FileUtils.test( config_override, FileTest.EXISTS )) {
                	    encfs_file = config_override;
				        import_mode = true;
	                }
	            } else {
                	if (((RadioButton)builder.get_object("radiobutton_stash_dir_custom")).get_active()) {
                    	if (FileUtils.test( Path.build_filename(stash_dir_custom, config_override), FileTest.EXISTS )) {
                    	    encfs_file = config_override;
				            import_mode = true;
                    	}
                    } else {
                    	if (FileUtils.test( Path.build_filename(stash_dir_default, config_override), FileTest.EXISTS )) {
                    	    encfs_file = config_override;
                        	import_mode = true;
                    	}
                    }
                }
            }
            // hide password fields when importing
            if (import_mode) {
            	((Widget)builder.get_object("frame3")).set_visible(false);
            	((Widget)builder.get_object("separator2")).set_visible(false);
            	((Label)builder.get_object("label_mode")).set_markup(_("Import"));
            	((ToggleButton)builder.get_object("checkbutton_disable_filename_encryption")).set_visible( false );
            	((ToggleButton)builder.get_object("checkbutton_use_keyring")).set_visible( false );
            } else {
            	((Widget)builder.get_object("frame3")).set_visible(true);
            	((Widget)builder.get_object("separator2")).set_visible(true);
            	((Label)builder.get_object("label_mode")).set_markup(_("Create"));
            	((ToggleButton)builder.get_object("checkbutton_disable_filename_encryption")).set_visible( true );
            	((ToggleButton)builder.get_object("checkbutton_use_keyring")).set_visible( true );
            }
                
            entry_stash_dir.set_sensitive(
                         ((RadioButton)builder.get_object("radiobutton_stash_dir_custom")).get_active());
            entry_mount_dir.set_sensitive(
                         ((RadioButton)builder.get_object("radiobutton_mount_dir_custom")).get_active());

            /** generate proposed directory */                         
            string basename;
            if (((RadioButton)builder.get_object("radiobutton_stash_dir_default")).get_active()) {
	            basename = Path.get_basename(stash_dir_default);
	        }
	        else {
				basename = Path.get_basename(stash_dir_custom);
	        }
            
            try {
                var regex = new Regex("""^\.|[-_]encfs$""");
                basename = regex.replace(basename, -1, 0, "");
            } catch (GLib.Error e) {
                GLib.stdout.printf(e.message);
            }
            
            if (mount_dir_default_override == null) {
	            mount_dir_default = Path.build_filename(encfs_mount_dir_base, basename);
	        } else {
		        mount_dir_default = mount_dir_default_override;
	        }
            for (int i = 0; i < stashes.size; i++) {
            gen_mount_dir_num_max = 0;
		        foreach (Stash s in stashes) {
		        	if (s.mountDir == mount_dir_default) {
		        		mount_dir_default = gen_mount_dir(mount_dir_default);
		        	}
		        }
		    }
            
            ((RadioButton)builder.get_object("radiobutton_mount_dir_default")).set("label", mount_dir_default);

            if (encfs_status != null) {
                label_status.set_markup("<span color='red'>✘</span> "+encfs_status);
                //((Widget)builder.get_object("button_ok")).set_sensitive(false);
                encfs_status = null;
            } else {
                string status;
                if (check_options( out status )) {
                    //label_status.set_markup("<span color='green'>✔</span> "+_("Ready"));
                    label_status.set_visible(false);
                    ((Widget)builder.get_object("button_ok")).set_sensitive(true);     
                } else {
                    label_status.set_visible(true);
                    label_status.set_markup("<span color='red'>✘</span> "+status);
                    ((Widget)builder.get_object("button_ok")).set_sensitive(false);
                }
            }
                
        }

        public static bool check_options(out string message) {
            string stash_dir = stash_dir_default;
            string mount_dir = mount_dir_default;
            if (((RadioButton)builder.get_object("radiobutton_stash_dir_custom")).get_active()) {
                if (entry_stash_dir.text == "") {
                    message = _("Please provide a stash directory");
                    return false;
                }
                stash_dir = stash_dir_custom;
            }
            if (((RadioButton)builder.get_object("radiobutton_mount_dir_custom")).get_active()) {
                if (entry_mount_dir.text == "") {
                    message = _("Please provide a mount directory");
                    return false;
                }
                mount_dir = mount_dir_custom;
            }
        
            if (stash_dir == mount_dir) {
	            message = _("The stash and mount directories are equal");
	            return false;
            }
            
            foreach (Stash s in stashes) {
            	if (s.cryptDir == stash_dir) {
            		message = _("The stash is already configured");
            		return false;
            	}
            	if (s.mountDir == mount_dir) {
            		message = _("The mount directory is already allocated");
            		return false;
            	}  
            }
            
            try {
	            FileEnumerator enumerator = File.new_for_path(mount_dir).enumerate_children (
		            "standard::*",
		            FileQueryInfoFlags.NOFOLLOW_SYMLINKS, 
		            null);
	            //FileInfo info = null;
	            if (enumerator.next_file(null) != null) {
                    if ((!import_mode) || (import_mode && !Encfs.is_mounted(mount_dir))) {
                        message = _("The mount folder is not empty");
                		return false;
            		}
	            }
	        } catch (GLib.Error e) {}
        
            /** passwords have to match */
            if (!import_mode) {
                Entry passwdFld = (Entry)builder.get_object("entry_password");
                Entry passwdRtpFld = (Entry)builder.get_object("entry_password_retype");
                
                if ((passwdFld.get_text() == "") && (passwdRtpFld.get_text() == "")) {
                    message = _("Please enter a password");
                    return false;
                }
                else if (passwdFld.get_text() != passwdRtpFld.get_text() ) {
                    message = _("Passwords do not match");
                    return false;
                }
            }
            message = "";
            return true;
        }
        
        private static int gen_mount_dir_num_max = 0;
        private static string gen_mount_dir(string dir) {
            int index = dir.last_index_of("(");
        	int idx = index +1;
            int num = int.parse(dir[idx:-1]) + 1;
            
            if (num > gen_mount_dir_num_max)
            	gen_mount_dir_num_max = num;
            
            if (gen_mount_dir_num_max >= 2) {
	            return dir[0:index] + "("+ gen_mount_dir_num_max.to_string() +")";
	        } else {
	        	return dir + " (2)";
	        }
        }
        
        public static void password_changed() {
            update();            
        }
        
        public static void apply_toggled( ToggleButton button ) {
        	if (button.get_active()) {
        		apply();
        	}
        }
        
        public static void apply() {
        	if (!((Widget)builder.get_object("button_ok")).get_sensitive()) {
        		return;
        	}
        	
        	if (import_mode) {
        	    import();
        	} else {
        	    create();
        	}
        }
        
        public static void button_settings_toggled( ToggleButton b ) {
        	//((Widget)builder.get_object("settings")).set_visible(false);
        	Widget wid = (Widget)builder.get_object("settings");
        	wid.set_visible(b.get_active());
        }
        
        public static void create() {
        	if (!AtomicInt.dec_and_test( ref atomic )) {
        		AtomicInt.inc( ref atomic );
        		return;
        	}

        	//window.set_sensitive(false);
        	((Label)builder.get_object("label_status")).set_visible(false);
        	((Widget)builder.get_object("spinner")).set_visible(true);
            ((ToggleButton)builder.get_object("button_ok")).set_active(true);
            ((Widget)builder.get_object("button_ok")).set_sensitive(false);
            //((Widget)builder.get_object("button_cancel")).set_sensitive(false);
            ((Widget)builder.get_object("button_cancel")).grab_focus();
            ((Widget)builder.get_object("radiobutton_stash_dir_custom")).set_sensitive(false);
            ((Widget)builder.get_object("radiobutton_stash_dir_default")).set_sensitive(false);
            ((Widget)builder.get_object("radiobutton_mount_dir_custom")).set_sensitive(false);
            ((Widget)builder.get_object("radiobutton_mount_dir_default")).set_sensitive(false);
            ((Widget)builder.get_object("settings")).set_sensitive(false);
            ((Widget)builder.get_object("entry_password")).set_sensitive(false);
            ((Widget)builder.get_object("entry_password_retype")).set_sensitive(false);
            ((Widget)builder.get_object("entry_stash_dir")).set_sensitive(false);
            ((Widget)builder.get_object("entry_mount_dir")).set_sensitive(false);
            ((Label)builder.get_object("label_status")).set_markup("");
            ((Spinner)builder.get_object("spinner")).start();
        	
            string stash_dir = stash_dir_default;
            string mount_dir = mount_dir_default;
            
            if (((RadioButton)builder.get_object("radiobutton_stash_dir_custom")).get_active()) {
                stash_dir = stash_dir_custom;
            }
            if (((RadioButton)builder.get_object("radiobutton_mount_dir_custom")).get_active()) {
                mount_dir = mount_dir_custom;
            }
            
			busy_id = busy(Path.get_basename(mount_dir) + ": " + _("Creating..."));

            string password  = ((Entry)builder.get_object("entry_password")).get_text();
            
            bool disable_filename_encryption = ((ToggleButton)builder.get_object("checkbutton_disable_filename_encryption")).get_active();

			Stash stash;
			if (config_override == null) {
			    stash = new Stash(stash_dir, mount_dir, ".encfs6.xml");
			} else {
			    stash = new Stash(stash_dir, mount_dir, config_override);
			}
			sighandler = stash.createResponse.connect( (status, message) => {
			    Gdk.threads_enter();
				stash.disconnect(sighandler);
            	busy(busy_id.to_string());
            	AtomicInt.inc( ref atomic );
            	if (status) {
                    stashes_mutex.lock();
            		stashes.add(stash);
                    stashes_mutex.unlock();
                    bool use_keyring = ((ToggleButton)builder.get_object("checkbutton_use_keyring")).get_active();
                    if (use_keyring) {
                        stash.useKeyring = true;

                        try {
                            Secret.password_store_sync(Secret.get_schema(Secret.SchemaType.COMPAT_NETWORK),
                                                       Secret.COLLECTION_DEFAULT,
                                                       "Encfs password for "+stash.cryptDir,
                                                       password,
                                                       null,
                                                       "server", stash.cryptDir);

                        } catch (GLib.Error error) {
                            print("LibSecret:" + error.message + "\n");
                        }
                    }
            		stash.update();
		            TrayIcon.update();
		            save_stashes();
		            hide();
            	} else {
            		stash.destroy();
            		((Widget)builder.get_object("spinner")).set_visible(false);
            		((Spinner)builder.get_object("spinner")).stop();
				    ((ToggleButton)builder.get_object("button_ok")).set_active(false);
				    ((Widget)builder.get_object("button_ok")).set_sensitive(true);
				    ((Widget)builder.get_object("button_cancel")).set_sensitive(true);
				    ((Widget)builder.get_object("radiobutton_stash_dir_custom")).set_sensitive(true);
				    ((Widget)builder.get_object("radiobutton_stash_dir_default")).set_sensitive(true);
				    ((Widget)builder.get_object("radiobutton_mount_dir_custom")).set_sensitive(true);
				    ((Widget)builder.get_object("radiobutton_mount_dir_default")).set_sensitive(true);
		            ((Widget)builder.get_object("settings")).set_sensitive(true);
				    ((Widget)builder.get_object("entry_password")).set_sensitive(true);
				    ((Widget)builder.get_object("entry_password_retype")).set_sensitive(true);
				    ((Widget)builder.get_object("entry_stash_dir")).set_sensitive(true);
				    ((Widget)builder.get_object("entry_mount_dir")).set_sensitive(true);
				    ((Label)builder.get_object("label_status")).set_markup(message);
				    ((Label)builder.get_object("label_status")).set_visible(true);
				    encfs_status = message;
				    update();
            	}
            	Gdk.threads_leave();
			});
			
			stash.create(password, disable_filename_encryption);
        }
        
        public static void import() {
        	window.set_sensitive(false);
				
            string stash_dir = stash_dir_default;
            string mount_dir = mount_dir_default;
            
            if (((RadioButton)builder.get_object("radiobutton_stash_dir_custom")).get_active()) {
                stash_dir = stash_dir_custom;
            }
            if (((RadioButton)builder.get_object("radiobutton_mount_dir_custom")).get_active()) {
                mount_dir = mount_dir_custom;
            }
            
            Stash s = new Stash(stash_dir, mount_dir, encfs_file);
            stashes_mutex.lock();
            stashes.add(s);
            stashes_mutex.unlock();
            TrayIcon.update();
            
            save_stashes();
            hide();
        }
        
        public static void hide() {
            if (window == null)
                return;
                
			if (atomic == 0) {
				return;
		    }

		    mount_dir_default_override = null;
		    stash_dir_default_override = null;
		    config_override = null;
            window.destroy();
            window = (Window)null;
        }
        
        public static void cancel() {
            if (window == null)
                return;

			if (atomic == 0) {
			    EncfsWrapper2.create_cancel();
				return;
		    }
			hide();
        }
        
        public static void show_filechooser_stash_dir( Widget wid ) {
        	FileChooserDialog dialog = new FileChooserDialog(_("Please select the stash folder"), window,
        													 FileChooserAction.SELECT_FOLDER,
        													 Stock.CANCEL, ResponseType.CANCEL,
        													 Stock.OPEN, ResponseType.ACCEPT);
			dialog.set_modal(true);
			dialog.set_local_only(false);
			if (stash_dir_custom == "") {
				dialog.set_filename(stash_dir_default);
			} else {
				dialog.set_filename(stash_dir_custom);
			}
        	dialog.response.connect( (dlg, response_id) => {
        		if (response_id == ResponseType.ACCEPT) {
	        		entry_stash_dir.text = dialog.get_filename();
	        	}
        		dialog.destroy();
        	});

        	dialog.run();
        }
        
        public static void show_filechooser_mount_dir( Widget wid ) {
        	FileChooserDialog dialog = new FileChooserDialog(_("Please select the mount folder"), window,
        													 FileChooserAction.SELECT_FOLDER,
        													 Stock.CANCEL, ResponseType.CANCEL,
        													 Stock.OPEN, ResponseType.ACCEPT);
			dialog.set_modal(true);
			dialog.set_local_only(false);
			if (mount_dir_custom == "") {
				dialog.set_filename(mount_dir_default);
			} else {
				dialog.set_filename(mount_dir_custom);
			}
        	dialog.response.connect( (dlg, response_id) => {
        		if (response_id == ResponseType.ACCEPT) {
	        		entry_mount_dir.text = dialog.get_filename();
	        	}
        		dialog.destroy();
        	});
			dialog.run();
        }
        
    }
}
