"""Specific views and actions for application using the Comment entity type

:organization: Logilab
:copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"
_ = unicode

from itertools import count

from logilab.mtconverter import xml_escape
from logilab.common.decorators import monkeypatch

from cubicweb.web import dumps

from cubicweb.selectors import (implements, has_permission, authenticated_user,
                                score_entity, relation_possible, one_line_rset)
from cubicweb.view import EntityView
from cubicweb.uilib import rql_for_eid, cut, safe_cut
from cubicweb.mixins import TreeViewMixIn
from cubicweb.web import stdmsgs, uicfg, component, form, formwidgets as fw
from cubicweb.web.action import LinkToEntityAction, Action
from cubicweb.web.views import primary, baseviews, xmlrss, basecontrollers

_afs = uicfg.autoform_section
_afs.tag_subject_of(('*', 'comments', '*'), formtype='main', section='hidden')
_afs.tag_object_of(('*', 'comments', '*'), formtype='main', section='hidden')

_abaa = uicfg.actionbox_appearsin_addmenu
_abaa.tag_subject_of(('*', 'comments', '*'),  False)
_abaa.tag_object_of(('*', 'comments', '*'), False)

_pvs = uicfg.primaryview_section
_pvs.tag_subject_of(('*', 'comments', '*'),  'hidden')
_pvs.tag_object_of(('*', 'comments', '*'), 'hidden')


# comment views ###############################################################

class CommentPrimaryView(primary.PrimaryView):
    __select__ = implements('Comment')

    def cell_call(self, row, col):
        self._cw.add_css('cubes.comment.css')
        entity = self.cw_rset.complete_entity(row, col)
        # display text, author and creation date
        self.w(u'<div class="comment">')
        self.w(u'<div class="commentInfo">')
        # do not try to display creator when you're not allowed to see CWUsers
        if entity.creator:
            authorlink = entity.creator.view('oneline')
            self.w(u'%s %s\n' % (self._cw._('written by'), authorlink))
        self.w(self._cw.format_date(entity.creation_date))
        # commented object
        if entity.comments:
            self.w(u",  %s " % self._cw._('comments'))
            entity.comments[0].view('oneline', w=self.w)
            self.w(u"\n")
        # don't include responses in this view, since the comment section
        # component will display them
        self.w(u'</div>\n')
        self.w(u'<div class="commentBody">%s</div>\n'
               % entity.printable_value('content'))
        # XXX attribute generated_by added by email component
        if hasattr(self, 'generated_by') and self.generated_by:
            gen = self.generated_by[0]
            link = '<a href="%s">%s</a>' % (gen.absolute_url(),
                                            gen.dc_type().lower())
            txt = self._cw._('this comment has been generated from this %s') % link
            self.w(u'<div class="commentBottom">%s</div>\n' % txt)
        self.w(u'</div>\n')


class CommentRootView(EntityView):
    __regid__ = 'commentroot'
    __select__ = implements('Comment')

    def cell_call(self, row, col, **kwargs):
        entity = self.cw_rset.get_entity(row, col)
        root = entity.root()
        self.w(u'<a href="%s">%s %s</a> ' % (
            xml_escape(root.absolute_url()),
            xml_escape(root.dc_type()),
            xml_escape(cut(root.dc_title(), 40))))


class CommentSummary(EntityView):
    __regid__ = 'commentsummary'
    __select__ = implements('Comment')

    def cell_call(self, row, col, **kwargs):
        entity = self.cw_rset.get_entity(row, col)
        maxsize = self._cw.property_value('navigation.short-line-size')
        content = entity.printable_value('content', format='text/plain')
        self.w(xml_escape(cut(content, maxsize)))


class CommentOneLineView(baseviews.OneLineView):
    __select__ = implements('Comment')

    def cell_call(self, row, col, **kwargs):
        entity = self.cw_rset.get_entity(row, col)
        self.w(u'[%s] ' % entity.view('commentroot'))
        self.w(u'<a href="%s"><i>%s</i></a>\n' % (
            xml_escape(entity.absolute_url()),
            entity.view('commentsummary')))


class CommentTreeItemView(baseviews.ListItemView):
    __regid__ = 'treeitem'
    __select__ = implements('Comment')

    def cell_call(self, row, col, **kwargs):
        self._cw.add_js('cubicweb.ajax.js')
        self._cw.add_css('cubes.comment.css')
        entity = self.cw_rset.get_entity(row, col)
        actions = self._cw.vreg['actions']
        # DOM id of the whole comment's content
        cdivid = 'comment%sDiv' % entity.eid
        self.w(u'<div id="%s">' % cdivid)
        self.w(u'<div class="commentInfo">')
        self.w(self._cw.format_date(entity.creation_date))
        self.w(u' %s' % self._cw.format_time(entity.creation_date))
        if entity.creator:
            authorlink = entity.creator.view('oneline')
            self.w(u', %s <span class="author">%s</span> \n'
                   % (self._cw._('written by'), authorlink,))
        replyaction = actions.select_or_none('reply_comment', self._cw,
                                       rset=self.cw_rset, row=row)
        if replyaction is not None:
            url = self._cw.build_ajax_replace_url(
                'comment%sHolder' % entity.eid, rql_for_eid(entity.eid),
                'addcommentform')
            self.w(u' | <span class="replyto"><a href="%s">%s</a></span>'
                   % (xml_escape(url), self._cw._(replyaction.title)))
        editaction = actions.select_or_none('edit_comment', self._cw,
                                            rset=self.cw_rset, row=row)
        if editaction is not None:
            # split(':', 1)[1] to remove javascript:
            formjs = self._cw.build_ajax_replace_url(
                cdivid, rql_for_eid(entity.eid),
                'editcommentform', 'append').split(':', 1)[1]
            url = "javascript: jQuery('#%s div').hide(); %s" % (cdivid, formjs)
            self.w(u' | <span class="replyto"><a href="%s">%s</a></span>'
                   % (xml_escape(url), self._cw._(editaction.title)))

        deleteaction = actions.select_or_none('delete_comment', self._cw,
                                             rset=self.cw_rset, row=row)
        if deleteaction is not None:
            url = self._cw.build_ajax_replace_url(
                'comment%s' % entity.eid, rql_for_eid(entity.eid),
                'deleteconf', __redirectpath=entity.root().rest_path())

            self.w(u' | <span class="replyto"><a href="%s">%s</a></span>'
                   % (xml_escape(url), self._cw._(deleteaction.title)))
        self.w(u'</div>\n') # close comment's info div
        self.w(u'<div class="commentBody">%s</div>\n'
               % entity.printable_value('content'))
        # holder for reply form
        self.w(u'<div id="comment%sHolder" class="replyComment"></div>' % entity.eid)
        self.w(u'</div>\n') # close comment's content div


class CommentThreadView(TreeViewMixIn, baseviews.ListView):
    """a recursive tree view"""
    __select__ = implements('Comment')
    title = _('thread view')

    def open_item(self, entity):
        self.w(u'<li id="comment%s" class="comment">\n' % entity.eid)


class RssItemCommentView(xmlrss.RSSItemView):
    __select__ = implements('Comment')

    def cell_call(self, row, col):
        entity = self.cw_rset.complete_entity(row, col)
        self.w(u'<item>\n')
        self.w(u'<guid isPermaLink="true">%s</guid>\n'
               % xml_escape(entity.absolute_url()))
        self.render_title_link(entity)
        description = entity.dc_description(format='text/html') + \
                      self._cw._(u'about') + \
                      u' <a href=%s>%s</a>' % (entity.root().absolute_url(),
                                               entity.root().dc_title())
        self._marker('description', description)
        self._marker('dc:date', entity.dc_date(self.date_format))
        self.render_entity_creator(entity)
        self.w(u'</item>\n')
        self.wview('rssitem', entity.related('comments', 'object'), 'null')


# comment forms ################################################################

class InlineEditCommentForm(form.FormViewMixIn, EntityView):
    __regid__ = 'editcommentform'
    __select__ = implements('Comment')

    jsfunc = "processComment(%s, '%s', false)"
    jsonmeth = 'edit_comment'

    def cell_call(self, row, col):
        self.comment_form(self.cw_rset.get_entity(row, col))

    def propose_to_login(self):
        self.w(u'<div class="warning">%s ' % self._cw._('You are not authenticated.'))
        if 'registration' in self._cw.vreg.config.cubes():
            self.w(u'<a href="%s">%s</a> or ' % (self._cw.build_url('register'),
                                                 self._cw._(u'register')))
        self.w(u'<a onclick="showLoginBox()">%s</a>' % self._cw._(u'login'))
        self.w(u'</div>')

    def comment_form(self, commented, newcomment=None):
        self._cw.add_js('cubes.comment.js')
        if newcomment is None:
            newcomment = commented
        if self._cw.cnx.anonymous_connection:
            self.propose_to_login()
        # hack to avoid tabindex conflicts caused by Ajax requests
        self._cw.next_tabindex = count(20).next
        jseid = dumps(commented.eid)
        cancel_action = self.jsfunc % (jseid, '')
        buttons = [fw.Button(onclick=self.jsfunc % (jseid, self.jsonmeth)),
                   fw.Button(stdmsgs.BUTTON_CANCEL,
                             onclick=cancel_action)]
        form = self._cw.vreg['forms'].select('edition', self._cw,
                                             entity=newcomment,
                                             form_buttons=buttons)
        self.w(u'<div id="comment%sSlot">%s</div>' % (
            commented.eid, form.render(main_form_title=u'',
                                       display_label=False,
                                       display_relations_form=False)))


class InlineAddCommentForm(InlineEditCommentForm):
    __regid__ = 'addcommentform'
    __select__ = relation_possible('comments', 'object', 'Comment', 'add')

    jsfunc = "processComment(%s, '%s', true)"
    jsonmeth = 'add_comment'

    def cell_call(self, row, col):
        commented = self.cw_rset.get_entity(row, col)
        newcomment = self._cw.vreg['etypes'].etype_class('Comment')(self._cw)
        newcomment.eid = self._cw.varmaker.next()
        self.comment_form(commented, newcomment)


# contextual components ########################################################

class CommentSectionVComponent(component.EntityVComponent):
    """a component to display a <div> html section including comments
    related to an object
    """
    __regid__ = 'commentsection'
    __select__ = (component.EntityVComponent.__select__
                  & relation_possible('comments', 'object', 'Comment'))

    context = 'navcontentbottom'

    def cell_call(self, row, col, view=None):
        req = self._cw
        req.add_js( ('cubicweb.ajax.js', 'cubes.comment.js') )
        eid = self.cw_rset[row][col]
        self.w(u'<div id="%s" class="%s" cubicweb:rooteid="%s">' % (
            self.div_id(), self.div_class(), eid))
        rql = u'Any C,CD,CC,CCF,U,UL,US,UF ORDERBY CD WHERE C is Comment, '\
              'C comments X, C creation_date CD, C content CC, C content_format CCF, ' \
              'C created_by U?, U login UL, U firstname UF, U surname US, X eid %(x)s'
        rset = req.execute(rql, {'x': eid}, 'x')
        if rset.rowcount:
            self.w(u'<h4>%s</h4>' % (req._('Comment_plural')))
        if rset.rowcount:
            self.w(u'<ul class="comment">')
            for i in xrange(rset.rowcount):
                self.wview('tree', rset, row=i)
            self.w(u'</ul>')
        self.w(u'</div>')
        addcomment = self._cw.vreg['actions'].select_or_none('reply_comment', req,
                                                        rset=self.cw_rset,
                                                        row=row, col=col)
        if addcomment is not None:
            self.w(u'<div id="comment%sHolder"></div>' % eid)
            url = req.build_ajax_replace_url(
                'comment%sHolder' % eid, rql_for_eid(eid), 'addcommentform')
            self.w(u' (<a href="%s">%s</a>)' % (url, req._(addcomment.title)))
            # XXX still necessary?
            #if req.use_fckeditor() and req.property_value('ui.default-text-format') == 'text/html':
            #    req.fckeditor_config()


class UserLatestCommentsSection(component.EntityVComponent):
    """a section to display latest comments by a user"""
    __select__ = component.EntityVComponent.__select__ & implements('CWUser')
    __regid__ = 'latestcomments'

    def cell_call(self, row, col, view=None):
        user = self.cw_rset.get_entity(row, col)
        maxrelated = self._cw.property_value('navigation.related-limit') + 1
        rset = self._cw.execute(
            'Any C,CD,C,CCF ORDERBY CD DESC LIMIT %s WHERE C is Comment, '
            'C creation_date CD, C content CC, C content_format CCF, '
            'C created_by U, U eid %%(u)s' % maxrelated,
            {'u': user.eid})
        if rset:
            self.w(u'<div class="section">')
            self.w(u'<h4>%s</h4>\n' % self._cw._('Latest comments').capitalize())
            self.wview('table', rset,
                       displaycols=range(3), # XXX may be removed with cw >= 3.8
                       headers=[_('about'), _('on date'),
                                _('comment content')],
                       cellvids={0: 'commentroot',
                                 2: 'commentsummary',
                                 })
            self.w(u'</div>')


# actions ######################################################################

class ReplyCommentAction(LinkToEntityAction):
    __regid__ = 'reply_comment'
    __select__ = LinkToEntityAction.__select__ & implements('Comment')

    rtype = 'comments'
    role = 'object'
    target_etype = 'Comment'

    title = _('reply to this comment')
    category = 'hidden'
    order = 111

    def url(self):
        comment = self.cw_rset.get_entity(self.cw_row or 0, self.cw_col or 0)
        linkto = '%s:%s:subject' % (self.rtype, comment.eid)
        return self._cw.build_url(vid='creation', etype=self.target_etype,
                                  __linkto=linkto,
                                  __redirectpath=comment.root().rest_path(),
                                  __redirectvid=self._cw.form.get('vid', ''))


class AddCommentAction(LinkToEntityAction):
    """add comment is like reply for everything but Comment"""
    __regid__ = 'reply_comment'
    __select__ = LinkToEntityAction.__select__ & ~implements('Comment')

    rtype = 'comments'
    role = 'object'
    target_etype = 'Comment'

    title = _('add comment')
    category = 'hidden'
    order = 111


class EditCommentAction(Action):
    __regid__ = 'edit_comment'
    __select__ = one_line_rset() & implements('Comment') & has_permission('update')

    title = _('edit comment')
    category = 'hidden'
    order = 110

    def url(self):
        return self._cw.build_url(rql=self.cw_rset.printable_rql(), vid='edition')

class DeleteCommentAction(Action):
    __regid__ = 'delete_comment'
    __select__ = implements('Comment') & authenticated_user() & \
                 score_entity(lambda x: not x.reverse_comments and x.has_perm('delete'))

    title = _('delete comment')
    category = 'hidden'
    order = 110

    def url(self):
        return self._cw.build_url(rql=self.cw_rset.printable_rql(), vid='deleteconf')


# JSONController extensions through monkey-patching ############################

@monkeypatch(basecontrollers.JSonController)
@basecontrollers.jsonize
def js_add_comment(self, commented, text, format):
    return self._cw.execute('INSERT Comment C: C comments X, C content %(text)s, '
                            'C content_format %(format)s  WHERE X eid %(x)s',
                            {'format' : format, 'text' : text, 'x' : commented}, 'x')[0][0]

@monkeypatch(basecontrollers.JSonController)
@basecontrollers.jsonize
def js_edit_comment(self, comment, text, format):
    self._cw.execute('SET C content %(text)s, C content_format %(format)s '
                     'WHERE C eid %(x)s',
                     {'format' : format, 'text' : text, 'x' : comment}, 'x')