/* Copyright 2017 Siddhartha Das (bablu.boy@gmail.com)
*
* This file is part of Bookworm and is the main Application class
*
* Bookworm 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.
*
* Bookworm 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 Bookworm. If not, see http://www.gnu.org/licenses/.
*/

using Gtk;
using Gee;
using Granite.Widgets;
using Granite.Services;
public const string GETTEXT_PACKAGE = "bookworm";

public class BookwormApp.Bookworm : Granite.Application {
	private static Bookworm application;
	private static bool isBookwormRunning = false;
	public int exitCodeForCommand = 0;
	public static Granite.Services.Paths app_xdg_path;
	public static string bookworm_config_path = "";

	public static string[] commandLineArgs;
	public static bool command_line_option_version = false;
	public static bool command_line_option_debug = false;
	public static bool command_line_option_info = false;
	public static bool command_line_option_discover = false;
	private static OptionEntry[] options;

	public StringBuilder spawn_async_with_pipes_output = new StringBuilder("");

	public static BookwormApp.Settings settings;
	public static Gtk.ApplicationWindow window;
	public static Gtk.IconTheme default_theme;
	public static CssProvider cssProvider;
	public static Gtk.Box bookWormUIBox;
	public static Granite.Widgets.Welcome welcomeWidget;
	public static Granite.Widgets.ModeButton library_mode_button;
	public static Gtk.TreeModelFilter libraryTreeModelFilter;
	public static Gtk.Button library_view_button;
	public static Gtk.Button content_list_button;
	public static Gtk.Button prefButton;
	public static Gdk.Pixbuf bookSelectionPix;
	public static Gdk.Pixbuf bookSelectedPix;
	public static Gdk.Pixbuf image_selection_option_small;
	public static Gdk.Pixbuf image_selection_checked_small;
	public static Gdk.Pixbuf image_selection_transparent_small;
	public static Gdk.Pixbuf image_selection_scaled;
	public static Gdk.Pixbuf image_rating_1;
	public static Gdk.Pixbuf image_rating_2;
	public static Gdk.Pixbuf image_rating_3;
	public static Gdk.Pixbuf image_rating_4;
	public static Gdk.Pixbuf image_rating_5;
	public static Gtk.Image select_book_image;
	public static Gtk.Image add_book_image;
	public static Gtk.Image remove_book_image;
	public static Gtk.Image updateImageIcon;
	public static Gtk.Image add_scan_directory_image;
	public static Gtk.Image remove_scan_directory_image;
	public static Gtk.Image library_list_button_image;
	public static Gtk.Image library_grid_button_image;
	public static Gtk.Image content_list_button_image;
	public static Gtk.Image menu_icon_text_large;
	public static Gtk.Image menu_icon;
	public static Gtk.Image pref_menu_icon_text_large;
	public static Gtk.Image pref_menu_icon_text_small;
	public static Gtk.Image pref_menu_icon_align_left;
	public static Gtk.Image pref_menu_icon_align_right;
	public static Gtk.Image back_button_image;
	public static Gtk.Image forward_button_image;
	public static Gtk.Image back_page_image;
	public static Gtk.Image forward_page_image;

	public static string BOOKWORM_CURRENT_STATE = BookwormApp.Constants.BOOKWORM_UI_STATES[0];
	public static Gee.HashMap<string, BookwormApp.Book> libraryViewMap = new Gee.HashMap<string, BookwormApp.Book>();
	public static string locationOfEBookCurrentlyRead = "";
	public static string[] pathsOfBooksToBeAdded;
	public static int noOfBooksAddedFromCommand = 0;
	public static bool isBookBeingAddedToLibrary = false;
	public static bool isPageScrollRequired = false;
	public static StringBuilder pathsOfBooksInLibraryOnLoadStr = new StringBuilder("");
	public static StringBuilder pathsOfBooksNotAddedStr = new StringBuilder("");
	public static StringBuilder onLoadJavaScript = new StringBuilder("");
	public static string bookwormScripts = "";
	public static string bookTextSearchString = "";
	public static TreeMap<string,string> searchResultsMap = new TreeMap<string,string>();
  	public static StringBuilder aContentFileToBeSearched = new StringBuilder ("");
  	public static string[] profileColorList;
	public static string no_of_books_per_page = "21";
	public static ArrayList<string> paginationlist = new ArrayList<string> ();
	public static int current_page_counter = 0;

	construct {
		build_version = BookwormApp.Constants.bookworm_version;
		application_id = BookwormApp.Constants.bookworm_id;
		flags |= ApplicationFlags.HANDLES_COMMAND_LINE;
		program_name = BookwormApp.Constants.program_name;
		exec_name = BookwormApp.Constants.bookworm_id;

		options = new OptionEntry[4];
		options[0] = { "version", 0, 0, OptionArg.NONE, ref command_line_option_version, _("Display version number"), null };
		options[1] = { "debug", 0, 0, OptionArg.NONE, ref command_line_option_debug, _("Run Bookworm in debug mode"), null };
		options[2] = { "info", 0, 0, OptionArg.NONE, ref command_line_option_info, _("Run Bookworm in info mode"), null };
		options[3] = { "discover", 0, 0, OptionArg.NONE, ref command_line_option_discover, _("Automatically add new books from watched folders"), null };
		add_main_option_entries (options);
	}

	private Bookworm() {
		Object( application_id: BookwormApp.Constants.bookworm_id, 
                flags: ApplicationFlags.HANDLES_COMMAND_LINE
        );
		Intl.setlocale(LocaleCategory.MESSAGES, "");
		Intl.textdomain(GETTEXT_PACKAGE);
		Intl.bind_textdomain_codeset(GETTEXT_PACKAGE, "utf-8");
        //Initialize XDG Paths
		app_xdg_path = new Granite.Services.Paths();
		app_xdg_path.initialize (Constants.bookworm_id, Constants.INSTALL_SCRIPTS_DIR);
		bookworm_config_path = app_xdg_path.user_data_folder.get_path();
		debug("Bookworm Install Directory: "+BookwormApp.Constants.INSTALL_PREFIX);
		debug("Bookworm Install Tasks Scripts Directory: "+BookwormApp.Constants.INSTALL_TASKS_DIR);
		debug("Bookworm Install Mobi Scripts Directory: "+BookwormApp.Constants.INSTALL_MOBILIB_DIR);
		debug("Bookworm User Data Directory: "+bookworm_config_path);
	}

	public static Bookworm getAppInstance(){
		if(application == null){
			application = new Bookworm();
		}else{
			//do nothing, return the existing instance
		}
		return application;
	}

	public override int command_line (ApplicationCommandLine command_line) {
		commandLineArgs = command_line.get_arguments ();
		activate();
		return 0;
	}

	public int processCommandLine (string[] args) {
		try {
			var opt_context = new OptionContext ("- bookworm");
			opt_context.set_help_enabled (true);
			opt_context.add_main_entries (options, null);
			unowned string[] tmpArgs = args;
			opt_context.parse (ref tmpArgs);
			if("--version" in args){
				command_line_option_version = true;
			}
		} catch (OptionError e) {
			info ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
			info ("error: %s\n", e.message);
			return 0;
		}

		if(command_line_option_version){
			print("Bookworm Version "+BookwormApp.Constants.bookworm_version+"\n");
		} 
		if(command_line_option_discover){
			BookwormApp.BackgroundTasks.performTasks();
		}
		return 0;
	}

	public override void activate() {
		Logger.initialize("com.github.babluboy.bookworm");
		if(command_line_option_debug){
			Logger.DisplayLevel = LogLevel.DEBUG;
		}
		if(command_line_option_info){
			Logger.DisplayLevel = LogLevel.INFO;
		}
		info("[START] [FUNCTION:activate]");
		//proceed if Bookworm is not running already
		if(!isBookwormRunning){
			debug("No instance of Bookworm found");
			window = new Gtk.ApplicationWindow (this);
			default_theme = Gtk.IconTheme.get_default();
			//retrieve Settings
			settings = BookwormApp.Settings.get_instance();
			//set window attributes
			window.set_border_width (0);
			window.get_style_context ().add_class ("rounded");
			//set the minimum size of the window on minimize
			window.set_size_request (600, 350);
			//set css provider
			cssProvider = new Gtk.CssProvider();
			loadCSSProvider(cssProvider);
			//load images/icons
			loadImages();
			//add window components
			window.set_titlebar (BookwormApp.AppHeaderBar.create_headerbar());
			BookwormApp.AppWindow.createWelcomeScreen();
			bookWormUIBox = BookwormApp.AppWindow.createBoookwormUI();
			//load saved books from DB and add them to Library view
			loadBookwormState();
			//show welcome screen if no book is present in library instead of the normal library view
			if(libraryViewMap.size == 0){
				window.add(welcomeWidget);
			}else{
				window.add(bookWormUIBox);
			}
			add_window (window);
			window.show_all();
			toggleUIState();
			//capture window re-size events and save the window size
			window.size_allocate.connect(() => {
				saveWindowState();
			});
			//Exit Application Event
			window.destroy.connect (() => {
				//Perform close down activities
				closeBookWorm();
			});
			//Add keyboard shortcuts on the window
			window.add_events (Gdk.EventMask.KEY_PRESS_MASK);
			window.key_press_event.connect (BookwormApp.Shortcuts.handleKeyPress);
			window.key_release_event.connect (BookwormApp.Shortcuts.handleKeyRelease);

			window.window_state_event.connect (BookwormApp.AppWindow.handleWindowStateEvents);

			isBookwormRunning = true;
			debug("Completed creating an instance of Bookworm");
		}else{
			window.present();
			debug("An instance of Bookworm is already running");
		}
		//check if any books needed to be added/opened - if eBook(s) were opened from File Explorer using Bookworm
		if(commandLineArgs.length > 1){
			info("Book(s) to be added/opened based on command line parameters. Size of command line attributes:"+
				commandLineArgs.length.to_string()
			);
			pathsOfBooksToBeAdded = new string[commandLineArgs.length];
			pathsOfBooksToBeAdded = commandLineArgs;
			//Display the progress bar
			BookwormApp.AppWindow.bookAdditionBar.show();
			isBookBeingAddedToLibrary = true;
			//handle the case if the welcome screen is shown
			if(libraryViewMap.size == 0){
				//remove the welcome widget from main window
				window.remove(welcomeWidget);
				//add the library view to the window
				window.add(bookWormUIBox);
				bookWormUIBox.show_all();
				toggleUIState();
			}
			//Update the library view with books - this returns control back immediately
			BookwormApp.Library.addBooksToLibrary ();
		}
		//Perform post start up actions
		BookwormApp.contentHandler.performStartUpActions();
		toggleUIState();
		info("[END] [FUNCTION:activate]");
	}

	public override void open (File[] files, string hint) {
		/* TODO */
	}

	public void loadImages(){
		info("[START] [FUNCTION:loadImages]");
		try{
			image_selection_option_small = new Gdk.Pixbuf.from_resource(BookwormApp.Constants.SELECTION_OPTION_IMAGE_SMALL_LOCATION);
			image_selection_checked_small = new Gdk.Pixbuf.from_resource (BookwormApp.Constants.SELECTION_CHECKED_IMAGE_SMALL_LOCATION);
			image_selection_transparent_small = new Gdk.Pixbuf.from_resource (BookwormApp.Constants.SELECTION_CHECKED_IMAGE_SMALL_LOCATION);
			image_selection_transparent_small.fill(0x00000000);
			image_rating_1 = new Gdk.Pixbuf.from_resource (BookwormApp.Constants.RATING_1_IMAGE_LOCATION);
			image_rating_2 = new Gdk.Pixbuf.from_resource (BookwormApp.Constants.RATING_2_IMAGE_LOCATION);
			image_rating_3 = new Gdk.Pixbuf.from_resource (BookwormApp.Constants.RATING_3_IMAGE_LOCATION);
			image_rating_4 = new Gdk.Pixbuf.from_resource (BookwormApp.Constants.RATING_4_IMAGE_LOCATION);
			image_rating_5 = new Gdk.Pixbuf.from_resource (BookwormApp.Constants.RATING_5_IMAGE_LOCATION);

			if (Gtk.IconTheme.get_default ().has_icon ("object-select-symbolic")) {
				select_book_image = new Gtk.Image.from_icon_name ("object-select-symbolic", Gtk.IconSize.MENU);
			}else{
				select_book_image = new Gtk.Image.from_resource (BookwormApp.Constants.SELECT_BOOK_ICON_IMAGE_LOCATION);
			}
			if (Gtk.IconTheme.get_default ().has_icon ("list-add-symbolic")) {
				add_book_image = new Gtk.Image.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU);
			}else{
				add_book_image = new Gtk.Image.from_resource (BookwormApp.Constants.ADD_BOOK_ICON_IMAGE_LOCATION);
			}
			if (Gtk.IconTheme.get_default ().has_icon ("list-remove-symbolic")) {
				remove_book_image = new Gtk.Image.from_icon_name ("list-remove-symbolic", Gtk.IconSize.MENU);
			}else{
				remove_book_image = new Gtk.Image.from_resource (BookwormApp.Constants.REMOVE_BOOK_ICON_IMAGE_LOCATION);
			}
			if (Gtk.IconTheme.get_default ().has_icon ("list-add-symbolic")) {
				add_scan_directory_image = new Gtk.Image.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU);
			}else{
				add_scan_directory_image = new Gtk.Image.from_resource (BookwormApp.Constants.ADD_BOOK_ICON_IMAGE_LOCATION);
			}
			if (Gtk.IconTheme.get_default ().has_icon ("list-remove-symbolic")) {
				remove_scan_directory_image = new Gtk.Image.from_icon_name ("list-remove-symbolic", Gtk.IconSize.MENU);
			}else{
				remove_scan_directory_image = new Gtk.Image.from_resource (BookwormApp.Constants.REMOVE_BOOK_ICON_IMAGE_LOCATION);
			}
			if (Gtk.IconTheme.get_default ().has_icon ("view-list-symbolic")) {
				library_list_button_image = new Gtk.Image.from_icon_name ("view-list-symbolic", Gtk.IconSize.SMALL_TOOLBAR);
			}else{
				library_list_button_image = new Gtk.Image.from_pixbuf (new Gdk.Pixbuf.from_resource_at_scale(BookwormApp.Constants.LIBRARY_VIEW_LIST_IMAGE_LOCATION, 16, 16, true));
			}
			if (Gtk.IconTheme.get_default ().has_icon ("view-grid-symbolic")) {
				library_grid_button_image = new Gtk.Image.from_icon_name ("view-grid-symbolic", Gtk.IconSize.SMALL_TOOLBAR);
			}else{
				library_grid_button_image = new Gtk.Image.from_pixbuf (new Gdk.Pixbuf.from_resource_at_scale  (BookwormApp.Constants.LIBRARY_VIEW_GRID_IMAGE_LOCATION, 16, 16, true));
			}
			if (Gtk.IconTheme.get_default ().has_icon ("format-justify-left-symbolic")) {
				pref_menu_icon_align_left = new Gtk.Image.from_icon_name ("format-justify-left-symbolic", Gtk.IconSize.SMALL_TOOLBAR);
			}else{
				pref_menu_icon_align_left = new Gtk.Image.from_pixbuf (new Gdk.Pixbuf.from_resource_at_scale  (BookwormApp.Constants.TEXT_ALIGN_LEFT_ICON_LOCATION, 16, 16, true));
			}
			if (Gtk.IconTheme.get_default ().has_icon ("format-justify-right-symbolic")) {
				pref_menu_icon_align_right = new Gtk.Image.from_icon_name ("format-justify-right-symbolic", Gtk.IconSize.SMALL_TOOLBAR);
			}else{
				pref_menu_icon_align_right = new Gtk.Image.from_pixbuf (new Gdk.Pixbuf.from_resource_at_scale  (BookwormApp.Constants.TEXT_ALIGN_RIGHT_ICON_LOCATION, 16, 16, true));
			}
			if (Gtk.IconTheme.get_default ().has_icon ("help-info-symbolic")) {
				content_list_button_image = new Gtk.Image.from_icon_name ("help-info-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
			}else{
				content_list_button_image = new Gtk.Image.from_pixbuf (new Gdk.Pixbuf.from_resource_at_scale 	(BookwormApp.Constants.BOOK_INFO_IMAGE_LOCATION, 24, 24, true) );
			}
			if (Gtk.IconTheme.get_default ().has_icon ("format-text-larger-symbolic")) {
				menu_icon_text_large = new Gtk.Image.from_icon_name ("format-text-larger-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
			}else{
				menu_icon_text_large = new Gtk.Image.from_pixbuf (new Gdk.Pixbuf.from_resource_at_scale	(BookwormApp.Constants.TEXT_LARGER_IMAGE_ICON_LOCATION, 24, 24, true) );
			}
			if (Gtk.IconTheme.get_default ().has_icon ("open-menu")) {
				menu_icon = new Gtk.Image.from_icon_name ("open-menu", Gtk.IconSize.LARGE_TOOLBAR);
			}else{
				menu_icon = new Gtk.Image.from_pixbuf (new Gdk.Pixbuf.from_resource_at_scale (BookwormApp.Constants.HEADERBAR_PROPERTIES_IMAGE_LOCATION, 24, 24, true)	);
			}
			if (Gtk.IconTheme.get_default ().has_icon ("format-text-larger-symbolic")) {
				pref_menu_icon_text_large = new Gtk.Image.from_icon_name ("format-text-larger-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
			}else{
				pref_menu_icon_text_large = new Gtk.Image.from_pixbuf (new Gdk.Pixbuf.from_resource_at_scale  (BookwormApp.Constants.TEXT_LARGER_IMAGE_ICON_LOCATION, 24, 24, true));
			}
			if (Gtk.IconTheme.get_default ().has_icon ("format-text-smaller-symbolic")) {
				pref_menu_icon_text_small = new Gtk.Image.from_icon_name ("format-text-smaller-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
			}else{
				pref_menu_icon_text_small = new Gtk.Image.from_pixbuf (new Gdk.Pixbuf.from_resource_at_scale(BookwormApp.Constants.TEXT_SMALLER_IMAGE_ICON_LOCATION, 24, 24, true));
			}
			if (Gtk.IconTheme.get_default ().has_icon ("go-previous-symbolic")) {
				back_button_image = new Gtk.Image.from_icon_name ("go-previous-symbolic", Gtk.IconSize.MENU);
			}else{
				back_button_image = new Gtk.Image.from_resource (BookwormApp.Constants.PREV_PAGE_ICON_IMAGE_LOCATION);
			}
			if (Gtk.IconTheme.get_default ().has_icon ("go-next-symbolic")) {
				forward_button_image = new Gtk.Image.from_icon_name ("go-next-symbolic", Gtk.IconSize.MENU);
			}else{
				forward_button_image = new Gtk.Image.from_resource (BookwormApp.Constants.NEXT_PAGE_ICON_IMAGE_LOCATION);
			}
			if (Gtk.IconTheme.get_default ().has_icon ("go-previous-symbolic")) {
				back_page_image = new Gtk.Image.from_icon_name ("go-previous-symbolic", Gtk.IconSize.MENU);
			}else{
				back_page_image = new Gtk.Image.from_resource (BookwormApp.Constants.PREV_PAGE_ICON_IMAGE_LOCATION);
			}
			if (Gtk.IconTheme.get_default ().has_icon ("go-next-symbolic")) {
				forward_page_image = new Gtk.Image.from_icon_name ("go-next-symbolic", Gtk.IconSize.MENU);
			}else{
				forward_page_image = new Gtk.Image.from_resource (BookwormApp.Constants.NEXT_PAGE_ICON_IMAGE_LOCATION);
			}
		}catch(GLib.Error e){
			warning("Image could not be loaded. Error:"+e.message);
		}
		info("[END] [FUNCTION:loadImages]");
	}

	public static void loadCSSProvider(Gtk.CssProvider cssProvider){
		info("[START] [FUNCTION:loadCSSProvider] cssProvider="+cssProvider.to_string());
		string dynamicCSSContent = "";
		try{
			profileColorList = settings.list_of_profile_colors.split (",");
			//temp check to ensure the change to settings for colours increasing from 6 to 9 is handled
			if(profileColorList.length < 9){
				profileColorList = {"#000000","#fbfbfb","#E8ED00","#586e75","#fdf6e3","#87FF2B","#93a1a1","#002b36","#3465A4"};
			}
			dynamicCSSContent = BookwormApp.Constants.DYNAMIC_CSS_CONTENT
															 .replace("<profile_1_color>",profileColorList[0])
															 .replace("<profile_1_bgcolor>",profileColorList[1])
															 .replace("<profile_2_color>",profileColorList[3])
															 .replace("<profile_2_bgcolor>",profileColorList[4])
															 .replace("<profile_3_color>",profileColorList[6])
															 .replace("<profile_3_bgcolor>",profileColorList[7]);
			debug("CSS for Profile Buttons:"+dynamicCSSContent);
			cssProvider.load_from_data(dynamicCSSContent, dynamicCSSContent.length);
		}catch(GLib.Error e){
			warning("Stylesheet could not be loaded from CSS Content["+dynamicCSSContent+"]. Error:"+e.message);
		}
		Gtk.StyleContext.add_provider_for_screen(
			Gdk.Screen.get_default(),
			cssProvider,
			Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
		);
		info("[END] [FUNCTION:loadCSSProvider]");
	}

	public void loadBookwormState(){
		info("[START] [FUNCTION:loadBookwormState]");
		//check and create required directory structure
    	BookwormApp.Utils.fileOperations("CREATEDIR", 
							BookwormApp.Constants.EBOOK_EXTRACTION_LOCATION, "", ""
		);
		BookwormApp.Utils.fileOperations("CREATEDIR", 
							BookwormApp.Bookworm.bookworm_config_path, "", ""
		);
		BookwormApp.Utils.fileOperations("CREATEDIR", 
							BookwormApp.Bookworm.bookworm_config_path+"/covers/", "", ""
		);
		BookwormApp.Utils.fileOperations("CREATEDIR", 
							BookwormApp.Bookworm.bookworm_config_path+"/books/", "", ""
		);
		//Set the window to the last saved position
		if(settings.pos_x == 0 && settings.pos_y == 0){
			window.set_position (Gtk.WindowPosition.CENTER);
		}else{
			window.move(settings.pos_x, settings.pos_y);
		}
		//set window size to the last saved height/width
		if(settings.window_is_maximized){
			window.maximize();
		}else{
			if(settings.window_width > 0 && settings.window_height > 0){
				window.set_default_size(settings.window_width, settings.window_height);
			}else{
				window.set_default_size(1200, 700);
			}
		}
		//check last state and turn on dark theme
		if(BookwormApp.Bookworm.settings.is_dark_theme_enabled){
			Gtk.Settings.get_default ().gtk_application_prefer_dark_theme = true;
		}
		//set the number of books per library page as per user set value
		no_of_books_per_page = settings.library_page_items.to_string();

		//check if the database exists otherwise create database and required tables
		BookwormApp.DB.initializeBookWormDB(BookwormApp.Bookworm.bookworm_config_path);
		//set the library view
		BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE = settings.library_view_mode;
		//Fetch details of Books from the database
		BookwormApp.Bookworm.paginationlist.add("");
		BookwormApp.Bookworm.current_page_counter = 0;
		BookwormApp.Library.paginateLibrary("","PAGINATED_SEARCH");
		//Set the library pagination buttons based on the paginate call
		BookwormApp.AppWindow.handleLibraryPageButtons("", false);

		info("[END] [FUNCTION:loadBookwormState]");
	}

	public async void closeBookWorm (){
		info("[START] [FUNCTION:closeBookWorm]");
		//If Bookworm was closed while in Reading mode - save book details for subsequent read
		if(BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[1]) {
			//Save the page scroll position of the book being read
			(libraryViewMap.get(locationOfEBookCurrentlyRead)).setBookScrollPos(BookwormApp.contentHandler.getScrollPos());
			//Save the path to the book being read
			settings.book_being_read = locationOfEBookCurrentlyRead;
		}else{
			//Bookworm was not in reading view during close - remove path of book read last
			settings.book_being_read = "";
		}
		//release the control so that the window is closed
		Idle.add (closeBookWorm.callback);
		yield;
		//Update the book details to the database if it was opened in this session
		foreach (var book in libraryViewMap.values){
			if(((BookwormApp.Book)book).getWasBookOpened()){
				BookwormApp.DB.updateBookToDataBase((BookwormApp.Book)book);
				debug("Completed saving the book data into DB");
			}
		}
		//Run dicovery of books as a background task if not already running
		string checkBackgroundTask = BookwormApp.Utils.execute_sync_command("ps -ef");
		if(checkBackgroundTask.index_of("bookworm --discover") == -1){
			BookwormApp.Utils.execute_async_multiarg_command_pipes({"com.github.babluboy.bookworm", "--discover", "&"});
		}else{
    		debug("Bookworm discover process already running....");
		}
		info("[END] [FUNCTION:closeBookWorm]");
	}

	public void saveWindowState(){
		int width;
		int height;
		int x;
		int y;
		window.get_size (out width, out height);
		window.get_position (out x, out y);
		if(settings.pos_x != x || settings.pos_y != y){
			settings.pos_x = x;
    		settings.pos_y = y;
		}
		if(settings.window_width != width || settings.window_height != height){
    		settings.window_width = width;
			settings.window_height = height;
		}
		if(window.is_maximized == true){
			settings.window_is_maximized = true;
		}else{
			settings.window_is_maximized = false;
		}
		settings.zoom_level = BookwormApp.AppWindow.aWebView.get_zoom_level();
	}

	public static void readSelectedBook(owned BookwormApp.Book aBook){
		info("[START] [FUNCTION:readSelectedBook] book.location="+aBook.getBookLocation());
		//Fetch the book meta data from the database if it is not already available
		if(aBook.getBookContentList().size < 1){ 
            //content size should be greater than 1 if the book data has been loaded
			aBook = BookwormApp.DB.getBookMetaDataFromDB(aBook);
		}
		debug("Book details before attempting to open book for reading:"+ aBook.to_string());
		//Handle the case when the page number of the book is not set
    	if(aBook.getBookPageNumber() == -1){
			aBook.setBookPageNumber(0);
		}else{
			//This book was previously being read, so it should be opened at the last reading position
			//Enable the flag which will scroll the page to the last read position
			isPageScrollRequired = true;
		}
		//Handle the case when the page number of the book is outside limits
    	if(aBook.getBookPageNumber() >= aBook.getBookContentList().size){
			aBook.setBookPageNumber(aBook.getBookContentList().size - 1);
		}
		//check if the extracted contents for the book exists
		if(BookwormApp.Bookworm.settings.is_local_storage_enabled &&
			"true" == BookwormApp.Utils.fileOperations(
                            "DIR_EXISTS", aBook.getBookExtractionLocation(), "", ""
            ) &&
			aBook.getBookContentList() != null && 
            aBook.getBookContentList().size > 0 &&
			aBook.getBookContentList().size >= aBook.getBookPageNumber() &&
			"true" == BookwormApp.Utils.fileOperations(
						"EXISTS", 
						BookwormApp.Utils.decodeHTMLChars(
							aBook.getBookContentList().get(aBook.getBookPageNumber())
						), 
						"", 
						""
        			  )
		){
			//extraction of book not required
			aBook.setIsBookParsed(true);
			debug("Book has already been extracted and the extracted contents exist at:"+aBook.getBookExtractionLocation());
		}else{
			//Extract and Parse the eBook (this will overwrite the contents already extracted)
			debug("Extracted contents of the book was not found at expected location ["
					+ aBook.getBookExtractionLocation() +
				  "], attempting to load book from original location ["
					+ aBook.getBookLocation() +
				  "]");
			aBook = genericParser(aBook);
			//If ebook was not parsed successfully, show the warning info banner message
			if(!aBook.getIsBookParsed()){
				BookwormApp.AppWindow.showInfoBar(aBook, MessageType.WARNING);
			}
		}
		//progress in opening the book for reading if it has been parsed successfully
		if(aBook.getIsBookParsed()){
			//update the total number of pages in the book
			aBook.setBookTotalPages(aBook.getBookContentList().size);
			//update book to mark it has been opened in this session
			aBook.setBookLastModificationDate((new DateTime.now_utc().to_unix()).to_string());
			aBook.setWasBookOpened(true);
			//update book details to libraryView Map
			libraryViewMap.set(aBook.getBookLocation(), aBook);
			locationOfEBookCurrentlyRead = aBook.getBookLocation();
			//Update header title
			BookwormApp.AppHeaderBar.get_headerbar().title = BookwormApp.Utils.parseMarkUp(aBook.getBookTitle());
			//change the application view to Book Reading mode
			BOOKWORM_CURRENT_STATE = BookwormApp.Constants.BOOKWORM_UI_STATES[1];
			toggleUIState();
			//reset the contents of the search entry
			BookwormApp.Info.searchresults_scroll.get_child().destroy();
			//set the max value and the current value of the page slider
			BookwormApp.AppWindow.pageAdjustment.set_upper(aBook.getBookContentList().size);
			BookwormApp.AppWindow.pageAdjustment.set_value(aBook.getBookPageNumber());
			//render the contents of the current page of book
			aBook = BookwormApp.contentHandler.renderPage(aBook, "");
            //set the focus to the webview to capture keypress events
            BookwormApp.AppWindow.aWebView.grab_focus();
		}
        info("[END] [FUNCTION:readSelectedBook] book.location="+aBook.getBookLocation());
	}

	public static void toggleUIState(){
		info("[START] [FUNCTION:toggleUIState] bookworm current state:" +
				 			BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE
 			   );
		//hide the inforbar if there is no text in it
		if(BookwormApp.AppWindow.infobarLabel.get_text().length < 1){
			BookwormApp.AppWindow.infobar.hide();
		}
		//Set-up UI specific for Library Grid View Mode
    	if(BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[0] ||
			 BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[2] ||
			 BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[3])
		{
      		BookwormApp.AppWindow.library_list_scroll.set_visible(false);
			BookwormApp.AppWindow.library_grid_scroll.set_visible(true);
			BookwormApp.AppWindow.library_grid.show_all();
			BookwormApp.AppWindow.bookLibrary_ui_box.grab_focus();
		}
		//Set-up UI specific for Library List View Mode
		if(BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[5] ||
			 BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[6] ||
			 BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[7])
		{
	  		BookwormApp.AppWindow.library_grid_scroll.set_visible(false);
			BookwormApp.AppWindow.library_list_scroll.set_visible(true);
			BookwormApp.AppWindow.library_table_treeview.show_all();
			BookwormApp.AppWindow.library_table_treeview.grab_focus();
		}
		//Set-up UI for Library View Mode (List or Grid)
		if(BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[0] ||
			 BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[2] ||
			 BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[3] ||
			 BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[5])
		{
			BookwormApp.AppHeaderBar.headerSearchBar.set_placeholder_text(
                BookwormApp.Constants.TEXT_FOR_HEADERBAR_LIBRARY_SEARCH
            );
			library_mode_button.set_visible(true);
			content_list_button.set_visible(false);
			library_view_button.set_visible(false);
			BookwormApp.AppWindow.bookLibrary_ui_box.set_visible(true);
			BookwormApp.AppWindow.bookReading_ui_box.set_visible(false);
			BookwormApp.Info.info_box.set_visible(false);
			prefButton.set_visible(false);
			BookwormApp.AppHeaderBar.bookmark_active_button.set_visible(false);
			BookwormApp.AppHeaderBar.bookmark_inactive_button.set_visible(false);
			if(!isBookBeingAddedToLibrary)
				BookwormApp.AppWindow.bookAdditionBar.hide();
		}
		//Set-up UI for Reading Mode
		if(BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[1]){
			BookwormApp.AppHeaderBar.headerSearchBar.set_placeholder_text(
                    BookwormApp.Constants.TEXT_FOR_HEADERBAR_BOOK_SEARCH
            );
			library_mode_button.set_visible(false);
			content_list_button.set_visible(true);
			library_view_button.set_visible(true);
			library_view_button.set_label(BookwormApp.Constants.TEXT_FOR_LIBRARY_BUTTON);
			BookwormApp.AppWindow.bookLibrary_ui_box.set_visible(false);
			BookwormApp.AppWindow.bookReading_ui_box.set_visible(true);
			BookwormApp.Info.info_box.set_visible(false);
			prefButton.set_visible(true);
			BookwormApp.contentHandler.handleBookMark("DISPLAY");
			BookwormApp.AppWindow.bookAdditionBar.hide();
		}
		//Set-up UI for Info Mode -  Meta Data / Table of Content
		if(BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[4]){
			BookwormApp.AppHeaderBar.headerSearchBar.set_placeholder_text(
                    BookwormApp.Constants.TEXT_FOR_HEADERBAR_LIBRARY_SEARCH
            );
			BookwormApp.Info.info_box.show_all();
			library_mode_button.set_visible(false);
			content_list_button.set_visible(false);
			library_view_button.set_visible(true);
			library_view_button.set_label(BookwormApp.Constants.TEXT_FOR_RESUME_BUTTON);
			BookwormApp.AppWindow.bookLibrary_ui_box.set_visible(false);
			BookwormApp.AppWindow.bookReading_ui_box.set_visible(false);
			BookwormApp.Info.info_box.set_visible(true);
			BookwormApp.Info.stack.set_visible_child_name (settings.current_info_tab);
			prefButton.set_visible(false);
			BookwormApp.AppHeaderBar.bookmark_active_button.set_visible(false);
			BookwormApp.AppHeaderBar.bookmark_inactive_button.set_visible(false);
			BookwormApp.AppWindow.bookAdditionBar.hide();
		}
		info("[END] [FUNCTION:toggleUIState] bookworm current state:"+
              BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE
        );
	}

	public static BookwormApp.Book genericParser(owned BookwormApp.Book aBook){
		info("[START] [FUNCTION:genericParser] book.location="+aBook.getBookLocation());
		//set defaults for title and author - this will be over-ridden with extracted data if found
        aBook.setBookAuthor(BookwormApp.Constants.TEXT_FOR_UNKNOWN_TITLE);
        aBook.setBookTitle(BookwormApp.Constants.TEXT_FOR_UNKNOWN_TITLE);
		//check if ebook is present at provided location
		if("false" == BookwormApp.Utils.fileOperations("EXISTS", "", aBook.getBookLocation(), "")){
			warning("EBook not found at provided location:"+aBook.getBookLocation());
			aBook.setIsBookParsed(false);
			aBook.setParsingIssue(BookwormApp.Constants.TEXT_FOR_EXTRACTION_ISSUE);
			return aBook;
		}
		//determine the extension of the ebook file
		string ebookFileName = (File.new_for_path(aBook.getBookLocation()).get_basename());
		if(ebookFileName.index_of(".") != -1){
			string fileExtension = ebookFileName.substring(ebookFileName.last_index_of(".")).up();
			//parse file based on extension found
			try{
				switch(fileExtension){
					case ".EPUB":
						aBook = BookwormApp.ePubReader.parseEPubBook(aBook);
						break;
					case ".PDF":
						aBook = BookwormApp.pdfReader.parsePDFBook(aBook);
						break;
					case ".CBR":
						aBook = BookwormApp.comicsReader.parseComicsBook(aBook, fileExtension);
						break;
					case ".CBZ":
						aBook = BookwormApp.comicsReader.parseComicsBook(aBook, fileExtension);
						break;
					case ".MOBI":
						aBook = BookwormApp.mobiReader.parseMobiBook(aBook);
						break;
					case ".PRC":
						aBook = BookwormApp.mobiReader.parseMobiBook(aBook);
						break;
					case ".ZIP":
						//check if the file is a zipped FB2 file
						if(ebookFileName.up().last_index_of (".FB2.ZIP") != -1){
							aBook = BookwormApp.fb2Reader.parseFictionBook(aBook);
							break;
						}else{
							aBook.setIsBookParsed(false);
							aBook.setParsingIssue(BookwormApp.Constants.TEXT_FOR_FORMAT_NOT_SUPPORTED);
							break;
						}
					case ".FB2":
						aBook = BookwormApp.fb2Reader.parseFictionBook(aBook);
						break;
					default:
						aBook.setIsBookParsed(false);
						aBook.setParsingIssue(BookwormApp.Constants.TEXT_FOR_FORMAT_NOT_SUPPORTED);
						break;
				}
			}catch(GLib.Error e){
				info ("Error while parsing book: %s\n", e.message);
				aBook.setIsBookParsed(false);
				aBook.setParsingIssue(BookwormApp.Constants.TEXT_FOR_PARSING_ISSUE);
			}
		}
		//If the book could not be parsed, add the book location to the list of books which could not be loaded
		if(!aBook.getIsBookParsed() ){
			BookwormApp.Bookworm.pathsOfBooksNotAddedStr.append(aBook.getBookLocation()).append("~~");
		}
		info("[END] [FUNCTION:genericParser] book.is book parsed="+aBook.getIsBookParsed().to_string());
		return aBook;
	}

}
