# w.c.s. - web application for online forms
# Copyright (C) 2005-2010  Entr'ouvert
#
# 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, see <http://www.gnu.org/licenses/>.

from django.utils.six.moves.urllib import parse as urllib

from quixote import get_request, get_publisher, redirect
from quixote.html import htmltext, TemplateIO

from ..qommon import _
from ..qommon import misc
from ..qommon.form import *
from ..qommon.backoffice.listing import pagination_links
from wcs.roles import logged_users_role

class FormDefUI(object):
    def __init__(self, formdef):
        self.formdef = formdef

    def listing(self, fields, selected_filter='all', url_action=None,
                    items=None, offset=0, limit=0,
                    query=None, order_by=None, criterias=None,
                    include_checkboxes=False):

        partial_display = False

        if not items:
            if offset and not limit:
                limit = int(get_publisher().get_site_option('default-page-size') or 20)
            items, total_count = self.get_listing_items(
                            selected_filter, offset, limit, query, order_by,
                            criterias=criterias)
            if (offset > 0) or (total_count > limit > 0):
                partial_display = True

            if offset > total_count:
                get_request().form['offset'] = '0'
                return redirect('?' + urllib.urlencode(get_request().form))

        r = TemplateIO(html=True)

        if self.formdef.workflow:
            colours = []
            for status in self.formdef.workflow.possible_status:
                if status.colour and status.colour != 'FFFFFF':
                    fg_colour = misc.get_foreground_colour(status.colour)
                    colours.append((status.id, status.colour, fg_colour))
            if colours:
                r += htmltext('<style>')
                for status_id, bg_colour, fg_colour  in colours:
                    r += htmltext('tr.status-%s-wf-%s td.cell-status { '\
                            'background-color: #%s !important; color: %s !important; }\n' % (
                                self.formdef.workflow.id, status_id, bg_colour,
                                fg_colour))
                r += htmltext('</style>')

        r += htmltext('<table id="listing" class="main compact">')

        r += htmltext('<colgroup>')
        if include_checkboxes:
            r += htmltext('<col/>')  # checkbox
        r += htmltext('<col/>') # lock
        r += htmltext('<col/>')
        r += htmltext('<col/>')
        for f in fields:
            r += htmltext('<col />')
        r += htmltext('</colgroup>')

        using_postgresql = get_publisher().is_using_postgresql()

        r += htmltext('<thead><tr>')
        if self.formdef.workflow.criticality_levels and using_postgresql:
            r += htmltext('<th class="criticality-level-cell" data-field-sort-key="criticality_level"><span></span></th>')
        else:
            r += htmltext('<th></th>') # lock
        if include_checkboxes:
            r += htmltext('<th class="select"><input type="checkbox" name="select[]" value="_all"/>')
            r += htmltext(' <span id="info-all-rows">%s</span></th>') % _(
                    'Do note the selected action will run on all pages when this is checked.')
        for f in fields:
            field_sort_key = None
            if getattr(f, 'fake', False):
                field_sort_key = f.id
                if f.id == 'time':
                    field_sort_key = 'receipt_time'
                elif f.id in ('user-label', 'submission_agent'):
                    field_sort_key = None
            else:
                field_sort_key = 'f%s' % f.id

            if field_sort_key and using_postgresql:
                r += htmltext('<th data-field-sort-key="%s">') % field_sort_key
            else:
                r += htmltext('<th>')
            if len(f.label) < 20:
                r += htmltext('<span>%s</span>') % f.label
            else:
                r += htmltext('<span title="%s">%s</span>') % (
                        f.label, misc.ellipsize(f.label, 20))
            r += htmltext('</th>')
        r += htmltext('</tr></thead>')
        r += htmltext('<tbody>')
        r += htmltext(self.tbody(fields, items, url_action,
            include_checkboxes=include_checkboxes))
        r += htmltext('</tbody>')
        r += htmltext('</table>')

        if get_publisher().is_using_postgresql():
            # add links to paginate
            r += pagination_links(offset, limit, total_count)

        return r.getvalue()

    def get_listing_item_ids(self, selected_filter='all', query=None, order_by=None, user=None, criterias=None, anonymise=False):
        formdata_class = self.formdef.data_class()
        if selected_filter == 'all':
            item_ids = formdata_class.keys()
            drafts = {x: True for x in formdata_class.get_ids_with_indexed_value('status', 'draft')}
            item_ids = [x for x in item_ids if x not in drafts]
        elif selected_filter == 'waiting':
            user_roles = [logged_users_role().id] + user.get_roles()
            item_ids = formdata_class.get_actionable_ids(user_roles)
        else:
            applied_filters = []
            if selected_filter == 'pending':
                applied_filters = ['wf-%s' % x.id for x in \
                              self.formdef.workflow.get_not_endpoint_status()]
            elif selected_filter == 'done':
                applied_filters = ['wf-%s' % x.id for x in \
                              self.formdef.workflow.get_endpoint_status()]
            else:
                applied_filters = ['wf-%s' % selected_filter]
            item_ids = []
            for status_id in applied_filters:
                item_ids.extend(formdata_class.get_ids_with_indexed_value(
                                        str('status'), status_id))

        if query:
            query_ids = formdata_class.get_ids_from_query(query)
            item_ids = list(set(item_ids).intersection(query_ids))

        if criterias:
            select_ids = [x.id for x in formdata_class.select(clause=criterias)]
            item_ids = list(set(item_ids).intersection(select_ids))

        if item_ids and not anonymise:
            # as we are in the backoffice, we don't have to care about the
            # situation where the user is the submitter, and we limit ourselves
            # to consider treating roles.
            if not user.is_admin:
                user_roles = set(user.get_roles())
                concerned_ids = set()
                for role in user_roles:
                    concerned_ids |= set(formdata_class.get_ids_with_indexed_value(
                        'concerned_roles', str(role)))
                item_ids = list(set(item_ids).intersection(concerned_ids))

        return item_ids

    def get_listing_items(self, selected_filter='all', offset=None,
            limit=None, query=None, order_by=None, user=None, criterias=None, anonymise=False):
        user = user or get_request().user
        formdata_class = self.formdef.data_class()

        item_ids = self.get_listing_item_ids(
                selected_filter=selected_filter,
                query=query,
                user=user,
                criterias=criterias,
                anonymise=anonymise)

        if order_by and not hasattr(formdata_class, 'get_sorted_ids'):
            # get_sorted_ids is only implemented in the SQL backend
            order_by = None
        if order_by and not anonymise:
            ordered_ids = formdata_class.get_sorted_ids(order_by)
            item_ids_dict = {x: True for x in item_ids}
            item_ids = [x for x in ordered_ids if x in item_ids_dict]
        else:
            item_ids.sort(key=lambda x: int(x))
            item_ids.reverse()

        total_count = len(item_ids)

        if not offset:
            offset = 0

        if limit:
            items = formdata_class.get_ids(item_ids[offset:offset+limit],
                    keep_order=True)
        else:
            items = formdata_class.get_ids(item_ids, keep_order=True)

        return (items, total_count)


    def tbody(self, fields=None, items=None, url_action=None, include_checkboxes=False):
        r = TemplateIO(html=True)
        if url_action:
            pass
            #url_action = '/' + url_action
        else:
            url_action = ''
        root_url = get_publisher().get_root_url()
        user = get_request().user
        visited_objects = get_publisher().get_visited_objects(exclude_user=user.id)
        include_criticality_level = bool(self.formdef.workflow.criticality_levels)
        for i, filled in enumerate(items):
            classes = ['status-%s-%s' % (filled.formdef.workflow.id, filled.status)]
            if i%2:
                classes.append('even')
            else:
                classes.append('odd')

            object_key = 'formdata-%s-%s' % (filled.formdef.url_name, filled.id)
            if object_key in visited_objects:
                classes.append('advisory-lock')
            if filled.backoffice_submission:
                classes.append('backoffice-submission')

            style = ''
            if include_criticality_level:
                try:
                    level = filled.get_criticality_level_object()
                except IndexError:
                    style = ''
                else:
                    classes.append('criticality-level')
                    style = ' style="border-left-color: #%s;"' % level.colour

            link = str(filled.id) + '/'
            data = ' data-link="%s"' % link
            if filled.anonymised:
                data += ' data-anonymised="true"'
            r += htmltext('<tr class="%s"%s>' % (' '.join(classes), data))
            if include_criticality_level:
                r += htmltext('<td class="criticality-level-cell" %s></td>' % style) # criticality_level
            else:
                r += htmltext('<td class="lock-cell"></td>') # lock
            if include_checkboxes:
                r += htmltext('<td class="select"><input type="checkbox" name="select[]" ')
                r += htmltext('value="%s"') % filled.id
                workflow_roles = {}
                if self.formdef.workflow_roles:
                    workflow_roles.update(self.formdef.workflow_roles)
                if filled.workflow_roles:
                    workflow_roles.update(filled.workflow_roles)
                for function_key, function_value in workflow_roles.items():
                    if function_value in user.get_roles():
                        # dashes are replaced by underscores to prevent HTML5
                        # normalization to CamelCase.
                        r += htmltext(' data-is_%s="true" ' % function_key.replace('-', '_'))
                r += htmltext('/></td>')
            for i, f in enumerate(fields):
                field_value = filled.get_field_view_value(f, max_length=30)
                if f.type == 'id':
                    r += htmltext('<td class="cell-id"><a href="%s%s">%s</a></td>') % (link, url_action,
                            field_value)
                    continue
                css_class = {
                    'time': 'cell-time',
                    'last_update_time': 'cell-time',
                    'user-label': 'cell-user',
                    'status': 'cell-status',
                    'anonymised': 'cell-anonymised',
                    'submission_agent': 'cell-submission-agent',
                }.get(f.type)
                if css_class:
                    r += htmltext('<td class="%s">' % css_class)
                else:
                    r += htmltext('<td>')
                r += field_value.replace(str('[download]'), str('%sdownload' % link))
                r += htmltext('</td>')
            r += htmltext('</tr>\n')
        return r.getvalue()
