package com.gmail.dpierron.calibre.opds;
/**
 * Class for implementing the AllBooks sub-catalog
 * Inherits from:
 *  -> BooksSubcatalog - methods for listing contained books.
 *     -> SubCatalog
 */

import com.gmail.dpierron.calibre.datamodel.Book;
import com.gmail.dpierron.calibre.datamodel.Series;
import com.gmail.dpierron.tools.i18n.Localization;
import com.gmail.dpierron.tools.Helper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class AllBooksSubCatalog extends BooksSubCatalog {
  private final static Logger logger = LogManager.getLogger(AllBooksSubCatalog.class);

  //------------------ CONSTRUCTORS ------------------------
  public AllBooksSubCatalog(List<Book> books) {

    super(books);
    sortBooks();
    setCatalogType(Constants.ALLBOOKS_TYPE);
  }

  /**
   * Version of catalog constructor where we can force sort by Title if wanted
   * @param stuffToFilterOut
   * @param subbooks
   * @param sortByTitle
   */
  public AllBooksSubCatalog(List<Object> stuffToFilterOut,
                            List<Book> subbooks,
                            boolean sortByTitle) {
    super(stuffToFilterOut, subbooks);
    if (sortByTitle)
      sortBooksByTitle(getBooks());
    else
      sortBooks();
    setCatalogType(Constants.ALLBOOKS_TYPE);
 }

  /**
   * Version of catalog constructor when we may want items filetered out
   * @param stuffToFilterOut
   * @param books
   */
  public AllBooksSubCatalog(List<Object> stuffToFilterOut, List<Book> books) {
    super(stuffToFilterOut, books);
    sortBooks();
    setCatalogType(Constants.ALLBOOKS_TYPE);
  }

  //------------------ METHODS ------------------------

  public boolean isBookTheStepUnit() {
    return true;
  }

  /**
   * Sort books according to the profile settings
   */
  private void sortBooks() {
    if (currentProfile.getOrderAllBooksBySeries()) {
      // sort the books by series
      sortBooksBySeries(getBooks());
    } else {
      // sort the books by title
      sortBooksByTitle(getBooks());
    }
  }

  /**
   * Sort the list of books sorted with the following key sequence:
   * - Series (alphabetically)
   * - Series-Index (numerically ascending)
   * - Book Title (Alphabetically for case where series-Index is the same)
   *
   * NOTE:  This could be in the more generic BooksSubCatalog class, but since
   *        it is only ever used from this class it has been place here.
   *
   * TODO:  Bug c2o131 - We seem to occasionally get an exception thrown inside the Java libraries
   *        Not sure at the moment why this should be the case as nothing obvious shows up
   *        It looks as it only occurs when series information is set and the series indexes are
   *        the same for the book objects that are being compared but this needs confirming and
   *        in fact may be due to some other currently unknown factor.
   *        THINK THIS MAY NOW BE FIXED - NEED FURTHER CHECKING TO ENSURE THIS IS THE CASE
   *
   * @param books
   */
  void sortBooksBySeries(List<Book> books) {
    // logger.trace("sortBooksBySeries: enter");
    try {
      Collections.sort(books, new Comparator<Book>() {
        public int compare(Book o1, Book o2) {
          // logger.trace("o1=" + o1 + ", o2=" + o2);
          assert (o1 != null) && (o2 != null);
          // logger.trace("comparing books " + o1.getDisplayName() + "(" + o1.getId() + ") " + o2.getDisplayName() + "(" + o2.getId() + ")");
          Series series1 = o1.getSeries();
          Series series2 = o2.getSeries();
          // logger.trace("series1=" + series1 + ", series2=" + series2);

          if ((series1 == null) && (series2 == null)) {
            // both series are null, we need to compare the book titles (as always...)
            String title1;
            String title2;
            if (currentProfile.getSortUsingTitle()) {
              title1 = o1.getDisplayName();
              title2 = o2.getDisplayName();
            } else {
              title1 = o1.getSortName();
              title2 = o2.getSortName();
            }
            // logger.trace("comparing title '" + title1.toString() + "' to '" + title2.toString() + "'");
            try {
              int result = Helper.checkedCollatorCompareIgnoreCase(title1, title2, collator);
              // logger.trace("return=" + result);
              return result;
            } catch (Exception e) {
              logger.error("Error sorting between titles '" + title1.toString() + "' and '" + title2.toString() + "'"); Helper.statsErrors++;
              return 0;
            }
          }
          if (series1 == null) {
            // only series2 set  so assume series2 sorts greater than series1
            // logger.trace("return 1");
            return 1;
          }
          if (series2 == null) {
            // only series1 set  so assume series2 sorts less than series2
            // logger.trace("return -1");
            return -1;
          }

          // Both series set if we get to here
          assert (series1 != null) && (series2 != null);
          if (!series1.getId().equals(series2.getId())) {
            // different series, we need to compare the series title
            try {
              // logger.trace("comparing series " + series1.getDisplayName() + "(" + series1.getId() + ") to " + series2.getDisplayName() + "(" + series2.getId() + ")");
              int result = Helper.checkedCollatorCompareIgnoreCase(series1.getDisplayName(), series2.getDisplayName(), collator);
              // logger.trace("return=" + result);
              return result;
            } catch (Exception e) {
              logger.error("Error sorting between series '" + series1.getDisplayName().toString() + "' and '" + series2.getDisplayName().toString() + "'"); Helper.statsErrors++;
              return 0;
            }
          }
          // same series, we need to compare the index
          // logger.trace("o1.index=" + o1.getSerieIndex() + ", o2.index=" + o2.getSerieIndex());
          if (o1.getSerieIndex() == o2.getSerieIndex()) {
            // series index the same, so we need to sort on the book title
            // both series are null, we need to compare the book titles (as always...)
            // logger.trace("Same series and series index");
            String title1;
            String title2;
            if (currentProfile.getSortUsingTitle()) {
              title1 = o1.getDisplayName();
              title2 = o2.getDisplayName();
            } else {
              title1 = o1.getSortName();
              title2 = o2.getSortName();
            }
            // logger.trace("comparing title '" + title1.toString() + "' to '" + title2.toString() + "'");
            try {
              int result = Helper.checkedCollatorCompareIgnoreCase(title1, title2, collator);
              // logger.trace("return=" + result);
              return result;
            } catch (Exception e) {
              logger.error("Error sorting between titles '" + title1.toString() + "' and '" + title2.toString() + "'"); Helper.statsErrors++;
              // logger.trace("return 0");
              return 0;
            }
          }
          if (o1.getSerieIndex() > o2.getSerieIndex()) {
            // logger.trace("return 1");
            return 1;
          } else {
            // logger.trace("return -1");
            return -1;
          }
        }  // end of compare()
      } // End of Comparator()
      ); // End of Collections.sort() statement
    } catch (Exception e) {
      logger.error("Unexpected exception reached trying to sort books by series: " + e); Helper.statsErrors++;
    }
    // logger.trace("sortBooksBySeries: exit");
    return;
  }

  public String getSummary() {
    if (getBooks().size() > 1)
      return Localization.Main.getText("allbooks.alphabetical", getBooks().size());
    else if (getBooks().size() == 1)
      return Localization.Main.getText("allbooks.alphabetical.single");
    else return "";
  }

  /**
   *
   * @return
   */
  public String getUrn() {
    return Constants.INITIAL_URN_PREFIX + getCatalogType();
  }

}
