"""
Display query results in multiple pages, like Google does.
"""

__id__ = "$Id$"

import math # for ceil function

class PagedResults:
    """Manage a list of xtuples to be shown in multiple pages.

    @ivar query: Query that was executed to get the results.
    @type query: C{str}
    @ivar results: Result of the query. Note that the list is empty if error
                   is not None.
    @type results: A C{list} of L{XTuple<xtuple.XTuple>}s.
    @ivar error: Error message, if any.
    @type error: C{str} or C{None}
    @ivar schema: Schema information of the query result.
    @type schema: Whatever is returned by XCursor.description.
    @ivar range: Number of previous/next pages shown in the page navigation.
                 Default value is 10.
    @type range: C{int}
    @ivar pageSize: Number of xtuples per page. Default value is 10.
    @type pageSize: C{int}
    @ivar page: Current page number. -1 if the result is empty.
    @type page: C{int}
    """

    def __init__(self, query, results, error, schema, type, isFetchable=False, nolineage=False, conf_computed=False,
        isViewTable=False, distinct=False, u_columns=[], version=-1, num_results=-1, extlineage=False):
        """Constructor.
        @param query: Query that was executed to get the results.
        @type query: C{string}
        @param results: Query results.
        @type results: A C{list} of L{XTuple<xtuple.XTuple>}s
        @param error: Error message, if any.
        @type error: C{string} or C{None}
        @param schema: A list of attribute information of the query results.
                       Each attribute information is a list of 7 items
                       C{(name, type_code, display_size, internal_size,
                       precision, scale, null_ok)}.
                       This is what is returned by XCursor.description.
        @type schema: A C{list} of attribute information.
        """
        self.range = 10
        self.pageSize = 10
        self.query = query
        self.results = results
        self.error = error
        self.schema = schema
        self.type = type
        self.conf_computed = conf_computed
        self.isFetchable = isFetchable
        self.isViewTable = isViewTable
        self.distinct = distinct
        self.u_columns = u_columns
        self.nolineage = nolineage
        self.version = version
        self.num_results = num_results
        self.extlineage = extlineage
        if len(self.results) > 0:
            self.page = 0
        else:
            self.page = -1

    def getResults(self):
        """
        Return a list of tuples for the current page.

        @return: A C{list} of xtuples to be displayed in the current page.
        @rtype: A C{list} of L{XTuple<xtuple.XTuple>}s
        """
        return self.results[self.pageSize * self.page :
            self.pageSize * (self.page + 1)]

    def getNumResults(self):
        """
        Return total number of xtuples in the query result.

        @return: Number of xtuples in the results.
        @rtype: C{int}
        """
        if self.num_results == -1:
            return len(self.results)
        else:
            return self.num_results

    def getNumPages(self):
        """
        Return total number of pages to display the results.

        @return: Number of pages to display the results.
        @rtype: C{int}
        """
        return int(math.ceil(self.getNumResults() / float(self.pageSize)))

    def setPage(self, page):
        """
        Sets the current page.

        @param page: The new value for the current page.
        @type page: C{int}
        @return: C{None}
        """
        self.page = page

    def getPage(self):
        """
        Return current page.

        @return: Current page.
        @rtype: C{int}
        """
        return self.page

    def isLastPage(self):
        """
        Return true iff the current page is the last page.

        @return: True if the current page is the last page. False otherwise.
        @rtype: C{bool}
        """
        return self.page == self.getNumPages() - 1

    def isFirstPage(self):
        """
        Return true iff the current page is the first page.

        @return: True if the current page is the first page. False otherwise.
        @rtype: C{bool}
        """
        return self.page == 0

    def getPageNav(self):
        """
        Return the Google style page navigation.

        @return: Page navigation (e.g. "Previous 1 2 3 4 Next")
        @rtype: C{str}
        """
        nav = ""
        if self.page >= 0:
            # Display "Previous" link unless this is the first page
            if not self.isFirstPage():
                nav += """<a href="?query=%s&page_num=%s">Previous</a> """ % \
                    (self.query, str(self.getPage() - 1))
            # Display page links for the range specified by self.range.
            if self.getNumPages() > 1:
                start = max(0, self.page - self.range)
                end = min(self.getNumPages(), self.getPage() + self.range + 1)
                for i in range(start, end):
                    if i == self.page:
                        nav += str(i + 1) + " "
                    else:
                        nav += """<a href="?query=%s&page_num=%s">%s</a> """ % \
                            (self.query, i, i + 1)
            # Display "Next" link unless this is the last page
            if not self.isLastPage():
                nav += """<a href="?query=%s&page_num=%s">Next</a> """ % \
                    (self.query, str(self.getPage() + 1))
        return nav
