# This file is part of Indico.
# Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).
#
# Indico 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.
#
# Indico 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 Indico; if not, see <http://www.gnu.org/licenses/>.

from __future__ import unicode_literals

from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.hybrid import hybrid_property

from indico.core.db import db
from indico.core.db.sqlalchemy import PyIntEnum
from indico.util.string import MarkdownText, PlainText, RichMarkup
from indico.util.struct.enum import TitledIntEnum


class RenderMode(TitledIntEnum):
    """Rendering formats that a description can be written in."""

    __titles__ = [None, 'HTML', 'Markdown', 'Plain Text']
    html = 1
    markdown = 2
    plain_text = 3


RENDER_MODE_WRAPPER_MAP = {
    RenderMode.html: RichMarkup,
    RenderMode.markdown: MarkdownText,
    RenderMode.plain_text: PlainText
}


class DescriptionMixin(object):
    """Mixin to add an html-enabled description column."""

    possible_render_modes = {RenderMode.plain_text}
    default_render_mode = RenderMode.plain_text
    marshmallow_aliases = {'_description': 'description'}

    @declared_attr
    def _description(cls):
        return db.Column(
            'description',
            db.Text,
            nullable=False,
            default=''
        )

    @declared_attr
    def render_mode(cls):
        # Only add the column if there's a choice
        # between several alternatives
        if len(cls.possible_render_modes) > 1:
            return db.Column(
                PyIntEnum(RenderMode),
                default=cls.default_render_mode,
                nullable=False
            )
        else:
            return cls.default_render_mode

    @hybrid_property
    def description(self):
        selected_mode = (self.default_render_mode
                         if len(self.possible_render_modes) == 1 or self.render_mode is None
                         else self.render_mode)
        description_wrapper = RENDER_MODE_WRAPPER_MAP[selected_mode]
        return description_wrapper(self._description)

    @description.setter
    def description(self, value):
        self._description = value

    @description.expression
    def description(cls):
        return cls._description

@@ -1423,19 +1423,18 @@
 ass 
-Description
+RenderMode
 Mixi
@@ -1469,34 +1469,41 @@
 dd a
-n html-enabled description
+  plaintext/html/markdown-enabled
  col
@@ -1614,248 +1614,8 @@
 text
-%0A    marshmallow_aliases = %7B'_description': 'description'%7D%0A%0A    @declared_attr%0A    def _description(cls):%0A        return db.Column(%0A            'description',%0A            db.Text,%0A            nullable=False,%0A            default=''%0A        )
 %0A%0A  
@@ -2023,43 +2023,79 @@
    @
-hybrid_property%0A
+classmethod%0A    def _render_getter(cls, attr_name):%0A  
     
+  
 def 
-description
+_getter
 (sel
@@ -2106,16 +2106,20 @@
         
+ 
+   
 selected
@@ -2152,16 +2152,20 @@
 er_mode%0A
+    
         
@@ -2270,24 +2270,28 @@
             
+  
+  
 else self.re
@@ -2301,16 +2301,20 @@
 r_mode)%0A
+    
         
@@ -2374,32 +2374,36 @@
 d_mode%5D%0A        
+   
+ 
 return descripti
@@ -2417,148 +2417,967 @@
 per(
-self._description)%0A%0A    @description.setter%0A    def description(self, value):%0A        self._description = value%0A%0A    @description.expression
+getattr(self, attr_name))%0A        return _getter%0A%0A    @classmethod%0A    def _render_setter(cls, attr_name):%0A        def _setter(self, value):%0A            setattr(self, attr_name, value)%0A        return _setter%0A%0A    @classmethod%0A    def _render_expression(cls, attr_name):%0A        def _expression(cls):%0A            return getattr(cls, attr_name)%0A        return _expression%0A%0A    @classmethod%0A    def create_hybrid_property(cls, attr_name):%0A        %22%22%22Create a hybrid property that does the rendering of the column.%0A%0A        :param attr_name: a name for the attribute the unprocessed value can be%0A                          accessed through (e.g. %60_description%60).%0A        %22%22%22%0A        return hybrid_property(cls._render_getter(attr_name), fset=cls._render_setter(attr_name),%0A                               expr=cls._render_expression(attr_name))%0A%0A%0Aclass DescriptionMixin(RenderModeMixin):%0A    marshmallow_aliases = %7B'_description': 'description'%7D%0A%0A    @declared_attr
 %0A   
@@ -3381,16 +3381,17 @@
     def 
+_
 descript
@@ -3415,20 +3415,195 @@
  return 
-cls.
+db.Column(%0A            'description',%0A            db.Text,%0A            nullable=False,%0A            default=''%0A        )%0A%0A    description = RenderModeMixin.create_hybrid_property('
 _descrip
@@ -3606,9 +3606,11 @@
 cription
+')
 %0A

Make DescriptionMixin more general