/* Copyright 2017 Siddhartha Das (bablu.boy@gmail.com)
*
* This file is part of Bookworm and serves as the UI for Book metadata
* information like Table of Contents, Bookmarks
*
* 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;
public class BookwormApp.Info:Gtk.Window {
  public static Box info_box;
  public static Gtk.Stack stack;
  public static Box content_box;
  public static ScrolledWindow content_scroll;
  public static Box bookmarks_box;
  public static ScrolledWindow bookmarks_scroll;
  public static Box searchresults_box;
  public static ScrolledWindow searchresults_scroll;
  public static Box annotations_box;
  public static ScrolledWindow annotations_scroll;
  public static Box dictionary_box;
  public static Gtk.Label dictionaryResultsLabel;
  public static ScrolledWindow dictionary_scroll;
  public static LinkButton firstSearchResultLinkButton;

  public static Gtk.Box createBookInfo(){
    info("[START] [FUNCTION:createBookInfo]");
    info_box = new Gtk.Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    info_box.set_border_width(BookwormApp.Constants.SPACING_WIDGETS);

    //define the stack for the tabbed view
    stack = new Gtk.Stack();
    stack.set_transition_type(StackTransitionType.SLIDE_LEFT_RIGHT);
    stack.set_transition_duration(1000);

    //define the switcher for switching between tabs
    StackSwitcher switcher = new StackSwitcher();
    switcher.set_halign(Align.CENTER);
    switcher.set_stack(stack);

    content_box = new Gtk.Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    content_scroll = new ScrolledWindow (null, null);
    content_scroll.set_policy (PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
    content_scroll.add (content_box);
    stack.add_titled(content_scroll, "content-list", BookwormApp.Constants.TEXT_FOR_INFO_TAB_CONTENTS);

    bookmarks_box = new Gtk.Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    bookmarks_scroll = new ScrolledWindow (null, null);
    bookmarks_scroll.set_policy (PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
    bookmarks_scroll.add (bookmarks_box);
    stack.add_titled(bookmarks_scroll, "bookmark-list", BookwormApp.Constants.TEXT_FOR_INFO_TAB_BOOKMARKS);

    searchresults_box = new Gtk.Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    searchresults_scroll = new ScrolledWindow (null, null);
    searchresults_scroll.set_policy (PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
    searchresults_scroll.add (searchresults_box);
    stack.add_titled(searchresults_scroll, "searchresults-list", BookwormApp.Constants.TEXT_FOR_INFO_TAB_SEARCHRESULTS);

    annotations_box = new Gtk.Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    annotations_scroll = new ScrolledWindow (null, null);
    annotations_scroll.set_policy (PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
    annotations_scroll.add (annotations_box);
    stack.add_titled(annotations_scroll, "annotations-list", BookwormApp.Constants.TEXT_FOR_INFO_TAB_ANNOTATIONS);
    
    dictionaryResultsLabel = new Label(BookwormApp.Constants.TEXT_FOR_DICTIONARY_LOOKUP_TEXT);
    dictionary_box = new Gtk.Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    dictionary_box.pack_start(dictionaryResultsLabel,false,false,0);
    dictionary_scroll = new ScrolledWindow (null, null);
    dictionary_scroll.set_policy (PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
    dictionary_scroll.add (dictionary_box);
    stack.add_titled(dictionary_scroll, "dictionary-list", BookwormApp.Constants.TEXT_FOR_INFO_TAB_DICTRESULTS);

    info_box.pack_start(switcher, false, true, 0);
    info_box.pack_start(stack, true, true, 0);

    //Check every time a tab is clicked and perform necessary actions
    stack.notify["visible-child"].connect ((sender, property) => {
      if("content-list"==stack.get_visible_child_name()){
        createTableOfContents();
      }
      if("bookmark-list"==stack.get_visible_child_name()){
        populateBookmarks();
      }
      if("annotations-list"==stack.get_visible_child_name()){
        populateAnnotations();
      }
      if("searchresults-list"==stack.get_visible_child_name()){
        //This is called from the header search bar
        if(firstSearchResultLinkButton != null){
          firstSearchResultLinkButton.grab_focus(); //sets the focus on the first link
        }
      }
      if("dictionary-list"==stack.get_visible_child_name()){
        //This is called from the context menu
      }
      //Set the value of the info tab currently being viewed so that the same tab is opened subsequently
      BookwormApp.Bookworm.settings.current_info_tab = stack.get_visible_child_name();
    });
    info("[END] [FUNCTION:createBookInfo]");
    return info_box;
  }

  public static void populateAnnotations(){
    info("[START] [FUNCTION:populateAnnotations]");
    //get the book being currently read
	BookwormApp.Book aBook = BookwormApp.Bookworm.libraryViewMap.get(BookwormApp.Bookworm.locationOfEBookCurrentlyRead);
    Gtk.Label annotationsLabel = new Label(BookwormApp.Constants.TEXT_FOR_ANNOTATIONS_FOUND);
    Box annotations_box = new Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    annotations_box.pack_start(annotationsLabel,false,false,0);
    TreeMap<string,string> aAnnotationMap = aBook.getAnnotationList();
    if(aAnnotationMap != null && aAnnotationMap.size > 0){
      foreach (var entry in aAnnotationMap.ascending_entries){
          LinkButton annotationLinkButton = new LinkButton.with_label (
                                                                            entry.key.strip().split("#~~#")[0],
                                                                            "Section " + entry.key.strip().split("#~~#")[0] +
                                                                            " [" +    BookwormApp.Utils.minimizeStringLength(
                                                                                                    entry.key.strip().split("#~~#")[1].replace("\n", " "),
                                                                                                    BookwormApp.Constants.MAX_NUMBER_OF_CHARS_FOR_ANNOTATION_TAB) +
                                                                             "] : " + BookwormApp.Utils.minimizeStringLength(
                                                                                                    entry.value.strip().replace("\n", " "),
                                                                                                    BookwormApp.Constants.MAX_NUMBER_OF_CHARS_FOR_ANNOTATION_TAB));
          annotationLinkButton.halign = Align.START;
          annotations_box.pack_start(annotationLinkButton,false,false,0);
          annotationLinkButton.activate_link.connect (() => {
                aBook.setBookPageNumber(int.parse(annotationLinkButton.get_uri ().strip()));
                //update book details to libraryView Map
                BookwormApp.Bookworm.libraryViewMap.set(aBook.getBookLocation(), aBook);
                aBook = BookwormApp.contentHandler.renderPage(aBook, "");
                //Set the mode back to Reading mode
                BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE = BookwormApp.Constants.BOOKWORM_UI_STATES[1];
                BookwormApp.Bookworm.toggleUIState();
                return true;
          });
        }
    }else{
      annotationsLabel.set_text(BookwormApp.Constants.TEXT_FOR_ANNOTATIONS_NOT_FOUND.replace("BBB", aBook.getBookTitle()));
    }
    //Remove the existing annotations Gtk.Box and add the current one
    annotations_scroll.get_child().destroy();
    annotations_scroll.add (annotations_box);
    annotations_box.show_all();
    info("[END] [FUNCTION:populateAnnotations]");
  }

  public static void populateBookmarks(){
    info("[START] [FUNCTION:populateBookmarks]");
    //get the book being currently read
	BookwormApp.Book aBook = BookwormApp.Bookworm.libraryViewMap.get(BookwormApp.Bookworm.locationOfEBookCurrentlyRead);
    Box bookmarks_box = new Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    Gtk.Label bookmarksLabel = new Label(BookwormApp.Constants.TEXT_FOR_BOOKMARKS_FOUND);
    bookmarks_box.pack_start(bookmarksLabel,false,false,0);
    if(aBook.getBookmark().index_of("**") != -1){
      string[] bookmarkList = aBook.getBookmark().split_set("**", -1);
      int bookmarkNumber = 1;
      foreach (string bookmarkedPage in bookmarkList) {
        if(bookmarkedPage != null && bookmarkedPage.length > 0){
          LinkButton bookmarkLinkButton = new LinkButton.with_label (bookmarkedPage,
                BookwormApp.Constants.TEXT_FOR_BOOKMARKS
                                .replace("NNN", bookmarkNumber.to_string())
                                .replace("PPP", (int.parse(bookmarkedPage)+1).to_string()));
          bookmarkNumber++;
          bookmarkLinkButton.halign = Align.START;
          bookmarks_box.pack_start(bookmarkLinkButton,false,false,0);
          bookmarkLinkButton.activate_link.connect (() => {
            aBook.setBookPageNumber(int.parse(bookmarkLinkButton.get_uri ().strip()));
            //update book details to libraryView Map
            BookwormApp.Bookworm.libraryViewMap.set(aBook.getBookLocation(), aBook);
            aBook = BookwormApp.contentHandler.renderPage(aBook, "");
            //Set the mode back to Reading mode
            BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE = BookwormApp.Constants.BOOKWORM_UI_STATES[1];
            BookwormApp.Bookworm.toggleUIState();
            return true;
          });
        }
      }
    }else{
      bookmarksLabel.set_text(BookwormApp.Constants.TEXT_FOR_BOOKMARKS_NOT_FOUND.replace("BBB", aBook.getBookTitle()));
    }
    //Remove the existing search results Gtk.Box and add the current one
    bookmarks_scroll.get_child().destroy();
    bookmarks_scroll.add (bookmarks_box);
    info_box.show_all();
     info("[END] [FUNCTION:populateBookmarks]");
  }
  
  public static void populateDictionaryResults(string word){
    info("[START] [FUNCTION:populateDictionaryResults]");
    //Create the Gtk.Box for displaying results
    Box dictionary_box = new Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    dictionaryResultsLabel = new Label("");
    dictionary_box.pack_start(dictionaryResultsLabel,false,false,0);
    //Remove the existing dictionary results Gtk.Box and add the current one
    dictionary_scroll.get_child().destroy();
    dictionary_scroll.add (dictionary_box);
    string dictionaryResults = "";
    //Show help text if no word is available for dictionary lookup
    if(word == null || word.strip().length < 1){
            dictionaryResultsLabel.set_text( BookwormApp.Constants.TEXT_FOR_DICTIONARY_LOOKUP_TEXT);
    }else{
            //Perform book search only if the Reading View is active
            if(
                BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE == BookwormApp.Constants.BOOKWORM_UI_STATES[1]
            ){
                //Set the mode to Info View Mode
                BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE = BookwormApp.Constants.BOOKWORM_UI_STATES[4];
                BookwormApp.Bookworm.toggleUIState();
                //Set the visible tab to search result tab
                BookwormApp.Info.stack.set_visible_child (
                        BookwormApp.Info.stack.get_child_by_name ("dictionary-list")
                );
            }
            //Execute word meaning script - invokes online/offline dictionary
            dictionaryResults = BookwormApp.Utils.execute_sync_command (
                                    BookwormApp.Constants.DICTIONARY_SCRIPT_LOCATION + " " + word
                                );
            debug ("Dictionary Search Results:" + dictionaryResults);
            dictionaryResultsLabel.set_text(dictionaryResults);
    }
    info_box.show_all();
    info("[END] [FUNCTION:populateDictionaryResults]");
  }

  public static async BookwormApp.Book populateSearchResults(){
    info("[START] [FUNCTION:populateSearchResults]");
    BookwormApp.Book aBook = BookwormApp.Bookworm.libraryViewMap.get(BookwormApp.Bookworm.locationOfEBookCurrentlyRead);
    Box searchresults_box = new Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
    bool hasResultsBeenFound = false;
    bool isSearchResultFocussed = false;

    Gtk.Label searchLabel = new Label(
                                    BookwormApp.Constants.TEXT_FOR_SEARCH_RESULTS_PROCESSING
                                        .replace(
                                            "$$$", BookwormApp.Utils.minimizeStringLength(
                                                        BookwormApp.AppHeaderBar.headerSearchBar.get_text(), 50)
                                        )
                                        .replace(
                                            "&&&", BookwormApp.Utils.minimizeStringLength(aBook.getBookTitle(), 35)
                                        )
                            );
    searchresults_box.pack_start(searchLabel,false,false,0);

    //Remove the existing search results Gtk.Box and add the current one
    searchresults_scroll.get_child().destroy();
    searchresults_scroll.add (searchresults_box);

    //Loop through each html file of the book and search for content
    foreach (string aBookContentFile in aBook.getBookContentList()) {
      //Add callback to join back the loop after releasing control
      Idle.add (populateSearchResults.callback);

      BookwormApp.Bookworm.aContentFileToBeSearched.assign(aBookContentFile);
      BookwormApp.contentHandler.searchHTMLContents();
      //Add link buttons representing the search results - if found
      if(BookwormApp.Bookworm.searchResultsMap.size > 0){
        hasResultsBeenFound = true;
        foreach (var entry in BookwormApp.Bookworm.searchResultsMap.ascending_entries) {
            string pageNumber = entry.key.slice(entry.key.index_of("~~")+2, entry.key.length).strip();
            LinkButton searchResultLinkButton = new LinkButton.with_label (pageNumber,  BookwormApp.Utils.parseMarkUp(
                                                                                   getChapterNameFromPage(pageNumber)+" : "+
                                                                                   BookwormApp.Utils.minimizeStringLength(entry.value, 100)));
            searchResultLinkButton.halign = Align.START;
            searchresults_box.pack_start(searchResultLinkButton,false,false,0);
            //grab the focus on the first search result
            if(!isSearchResultFocussed){
                searchResultLinkButton.grab_focus();
                isSearchResultFocussed = true;
                firstSearchResultLinkButton = searchResultLinkButton;
            }
            searchResultLinkButton.activate_link.connect (() => {
            aBook.setBookPageNumber(aBook.getBookContentList().index_of(searchResultLinkButton.get_uri ().strip()));
            //update book details to libraryView Map
            BookwormApp.Bookworm.libraryViewMap.set(aBook.getBookLocation(), aBook);
            BookwormApp.Bookworm.bookTextSearchString = BookwormApp.AppHeaderBar.headerSearchBar.get_text() +
                                                                                                   "#~~#" + searchResultLinkButton.get_label();
            aBook = BookwormApp.contentHandler.renderPage(aBook, "SEARCH");
            //Set the mode back to Reading mode
            BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE = BookwormApp.Constants.BOOKWORM_UI_STATES[1];
            BookwormApp.Bookworm.toggleUIState();
            return true;
          });
        }
      }
      searchresults_box.show_all();
      //release back control to UI
      yield;
    }
    //Set the text based on whether results have been found or not
    if(hasResultsBeenFound){
      searchLabel.set_text(BookwormApp.Constants.TEXT_FOR_SEARCH_RESULTS_FOUND
                                                                                        .replace("$$$", BookwormApp.Utils.minimizeStringLength
                                                                                                                     (BookwormApp.AppHeaderBar.headerSearchBar.get_text(), 50))
                                                                                        .replace("&&&", BookwormApp.Utils.minimizeStringLength(aBook.getBookTitle(), 35)));
    }else{
      searchLabel.set_text(BookwormApp.Constants.TEXT_FOR_SEARCH_RESULTS_NOT_FOUND
                                                                                        .replace("$$$", BookwormApp.Utils.minimizeStringLength
                                                                                                                     (BookwormApp.AppHeaderBar.headerSearchBar.get_text(), 50))
                                                                                        .replace("&&&", BookwormApp.Utils.minimizeStringLength(aBook.getBookTitle(), 35)));
    }
    info("[END] [FUNCTION:populateSearchResults]");
    return aBook;
  }

  public static string getChapterNameFromPage(string pageNumber){
    debug("[START] [FUNCTION:getChapterNameFromPage] pageNumber="+pageNumber);
    string chapterName = "";
    //get the book being currently read
		BookwormApp.Book aBook = BookwormApp.Bookworm.libraryViewMap.get(BookwormApp.Bookworm.locationOfEBookCurrentlyRead);
    if(aBook != null){
      int indexOfPageInContentList = aBook.getBookContentList().index_of(pageNumber);
      int indexOfCurrentChapterInContentList = 0;
      string previousChapterName = "";
      ArrayList<HashMap<string,string>> tocList = aBook.getTOC();
      foreach(HashMap<string,string> tocListItemMap in tocList){
        foreach (var entry in tocListItemMap.entries) {
          indexOfCurrentChapterInContentList = aBook.getBookContentList().index_of(entry.key);
          if(entry.key == pageNumber){
            chapterName =  entry.value; //search result page is a chapter page
            break;
          }else{
            if(indexOfCurrentChapterInContentList > indexOfPageInContentList){
              chapterName =  previousChapterName; //search result page is prior to the current chapter page - so assign previous chapter page
              break;
            }
          }
          previousChapterName = entry.value;
        }
        if(chapterName.length > 0){
            break;
        }
      }
      //handle the case when no chapter name has been selected
      if(chapterName.length < 1){
        chapterName = "Section " + pageNumber;
      }
    }
    debug("[END] [FUNCTION:getChapterNameFromPage] chapterName="+chapterName);
    return chapterName;
  }

  public static BookwormApp.Book createTableOfContents(){
    info("[START] [FUNCTION:createTableOfContents]");
    Box content_box;
    //get the book being currently read
	BookwormApp.Book aBook = BookwormApp.Bookworm.libraryViewMap.get(BookwormApp.Bookworm.locationOfEBookCurrentlyRead);
    if(aBook != null){
      //Return TOC if it has already been determined for the book - otherwise create TOC for the book
      if(aBook.getBookWidget("TABLE_OF_CONTENTS_WIDGET") != null){
        content_box = (Gtk.Box) (aBook.getBookWidget("TABLE_OF_CONTENTS_WIDGET"));
        debug("found TABLE_OF_CONTENTS_WIDGET");
      }else{
        content_box = new Box (Orientation.VERTICAL, BookwormApp.Constants.SPACING_WIDGETS);
        //Use Table Of Contents if present
        if(aBook.getTOC().size > 0){
          ArrayList<HashMap<string,string>> tocList = aBook.getTOC();
          foreach(HashMap<string,string> tocListItemMap in tocList){
            foreach (var entry in tocListItemMap.entries) {
              LinkButton contentLinkButton = new LinkButton.with_label (entry.key, BookwormApp.Utils.parseMarkUp(entry.value));
              contentLinkButton.halign = Align.START;
              content_box.pack_start(contentLinkButton,false,false,0);
              contentLinkButton.activate_link.connect (() => {
                string linkURI = contentLinkButton.get_uri ().strip();
                //check if the link contains a # - bookmarked section
                if(linkURI.index_of("#") != -1){
                    string URIWithoutBookmark = linkURI.slice(0, linkURI.last_index_of("#"));
                    aBook.setBookPageNumber(aBook.getBookContentList().index_of(URIWithoutBookmark));
                    aBook.setAnchor(linkURI.slice(linkURI.last_index_of("#")+1, linkURI.length));
                    BookwormApp.Bookworm.isPageScrollRequired = true;
                }else{
                    aBook.setBookPageNumber(aBook.getBookContentList().index_of(linkURI));
                }
                debug("Selected link from Table of Contents with URI ["+linkURI+"] and page location:"+aBook.getBookLocation().to_string());
                //update book details to libraryView Map
                BookwormApp.Bookworm.libraryViewMap.set(aBook.getBookLocation(), aBook);
                aBook = BookwormApp.contentHandler.renderPage(aBook, "");
                //Set the mode back to Reading mode
                BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE = BookwormApp.Constants.BOOKWORM_UI_STATES[1];
                BookwormApp.Bookworm.toggleUIState();
                return true;
              });
            }
          }
        }else{
          //If Table Of Contents is not found, use the spine data
          int contentNumber = 1;
          foreach(string contentPath in aBook.getBookContentList()){
            LinkButton contentLinkButton = new LinkButton.with_label (contentPath,
                                BookwormApp.Constants.TEXT_FOR_INFO_TAB_CONTENT_PREFIX+contentNumber.to_string());
            contentLinkButton.halign = Align.START;
            content_box.pack_start(contentLinkButton,false,false,0);
            //add to the table of contents of the book
            HashMap<string,string> TOCMapItem = new HashMap<string,string>();
            TOCMapItem.set(contentPath, BookwormApp.Constants.TEXT_FOR_INFO_TAB_CONTENT_PREFIX+contentNumber.to_string());
            aBook.setTOC(TOCMapItem);
            contentNumber++;
            //add the action for the link button
            contentLinkButton.activate_link.connect (() => {
              aBook.setBookPageNumber(aBook.getBookContentList().index_of(contentLinkButton.get_uri ()));
              //update book details to libraryView Map
              BookwormApp.Bookworm.libraryViewMap.set(aBook.getBookLocation(), aBook);
              aBook = BookwormApp.contentHandler.renderPage(aBook, "");
              //Set the mode back to Reading mode
              BookwormApp.Bookworm.BOOKWORM_CURRENT_STATE = BookwormApp.Constants.BOOKWORM_UI_STATES[1];
              BookwormApp.Bookworm.toggleUIState();
              return true;
            });
          }
        }
        aBook.setBookWidget("TABLE_OF_CONTENTS_WIDGET", content_box);
      }
      //remove the current content widget from any existing parent (ViewPort is a default parent while adding to ScrollWindow)
      content_box.unparent ();
      if(content_scroll.get_child() != null){
        //add a ref to the existing content widget so that it is not destoyed on removal
        content_scroll.get_child().@ref();
        //remove the existing content widget from the ScrollWindow
        content_scroll.remove(content_scroll.get_child());
        //add the current content widget to the ScrollWindow
        content_scroll.add (content_box);
      }else{
        //No existing widget in the ScrollWindow, add the current content widget
        content_scroll.add (content_box);
      }
    }
    info("[END] [FUNCTION:createTableOfContents]");
    return aBook;
  }
}
