import time

from MoinMoin.util import web
from MoinMoin import wikiutil

class ErrorData:
    
    def __init__(self, data_string, error_description):
        self.data = data_string
        self.description = error_description

    def __str__(self):
        return self.data


# ===========================================================================
# == Simple Data Types
# ===========================================================================

class UI:

    def __init__(self, name, description='', defaultvalue='', description2=''):
        self.name = name
        self.description = description     # rendered before value (e.g. 'Birthday')
        self.defaultvalue = defaultvalue   # rendered if no value is given (empty form)
        self.description2 = description2   # rendered after value (e.g. 'DD.MM.YYYY')

    def render_value(self, value, fmt, name=''):
        return ""

    def render_editform(self, value, fmt, name=''):
        return ""

    def get_value(self, form, msg, name=''):
        result = form.get("%s%s" % (name, self.name), [''])[0]
        return result

    def is_hidden(self):
        return False
    
    def is_novalue(self):
        return False

class StringUI(UI):

    def render_value(self, value, fmt, name=''):
        return fmt.text("%s" % value)

    def render_editform(self, value, fmt, name=''):
        return fmt.rawHTML('<input  name="%s%s" type="text" size="30" maxlength="255" value="%s">' %
                    (name, self.name, wikiutil.escape(value, quote=1)))

class NoValueUI(UI):

    def __init__(self, description='', description2=''):
        self.description = description     # rendered only, spanning two rows
        self.description2 = description2   # rendered after these rows (e.g. 'DD.MM.YYYY')
        self.name = 'novalue'
        self.defaultvalue = ''

    def render_value(self, value, fmt, name=''):
        return ''

    def render_editform(self, value, fmt, name=''):
        return ''

    def is_novalue(self):
        return True


class UrlUI(StringUI):

    def render_value(self, value, fmt, name=''):
        if not "://" in value:
            url = "http://" + value
        else:
            url = value
        return (fmt.url(True, url) +
                fmt.text(value) + 
                fmt.url(False))

class EmailUI(StringUI):
    
    def render_value(self, value, fmt, name=''):
        if not value: return ""
        return (fmt.url(True, "mailto:" + value) +
                fmt.icon("mailto") +         
                fmt.text(value) + 
                fmt.url(False)) 

class TextUI(StringUI):
    
    def __init__(self, name, description='', defaultvalue='', description2='',
            cols=30, rows=15):
        UI.__init__(self, name, description, defaultvalue, description2)
        self.cols = cols
        self.rows = rows
    
    def render_editform(self, value, fmt, name=''):
        return fmt.rawHTML(
                '<textarea name="%s%s" cols="%s" rows="%s">%s</textarea>' % 
                (name, self.name, self.cols, self.rows, 
                    wikiutil.escape(value, quote=1)))
    
class HiddenUI(UI):

    def __init__(self, name, defaultvalue=''):
        self.name = name
        self.defaultvalue = defaultvalue
        self.description=''
        self.description2=''
    
    def render_editform(self, value, fmt, name=''):
        return fmt.rawHTML('<input  name="%s%s" type="hidden" value="%s">' %
                           (name, self.name, wikiutil.escape(value, quote=1)))

    def is_hidden(self):
        return True 

class PasswordUI(UI):
    
    def render_editform(self, value, fmt, name=''):
        return fmt.rawHTML('<input  name="%s%s" type="password" size="30" maxlength\="30" value="%s">' %
                           (name, self.name, wikiutil.escape(value, quote=1)))
            

class DateUI(StringUI):

    input_format = "%d.%m.%Y"
    output_format = "%d.%m.%Y"
    
    def _parse_string(self, value):
        pass
    
    def render_value(self, value, fmt, name=''):
        if isinstance(value,str):
            return fmt.text(value)
        return fmt.text(time.strftime(self.output_format, value))
    
    def render_editform(self, value, fmt, name=''):
        if isinstance(value, ErrorData):
            value = value.data
        elif isinstance(value, str):
            pass
        else:
            value = time.strftime(self.input_format, value)
        return StringUI.render_editform(self, value, fmt, name)
        
    def get_value(self, form, msg, name=''):
        result = form.get("%s%s" % (name, self.name), [''])[0]
        if result == "": 
            return result
        try:
            return time.strptime(result, self.input_format)
        except ValueError:
            msg.append("Date expected!")
            return ErrorData(result, "Date expected")        

class DateTimeUI(DateUI):
    input_format = "%d.%m.%Y %H:%M"
    output_format = "%d.%m.%Y %H:%M"


class IntUI(StringUI):

    def get_value(self, form, msg, name=''):
        result = UI.get_value(self, form, msg, name)
        try:
            return int(result)
        except ValueError:
            return ErrorData(result, "Number expected")

class HiddenIntUI(HiddenUI, IntUI):
    pass
 
class FloatUI(UI):

    def get_value(self, form, msg, name=''):
        result = UI.get_value(self, form, msg, name)
        try:
            return float(result)
        except ValueError:
            return ErrorData(result, "Floating point number expected")

class BoolUI(UI):

    def __init__(self, name, description, defaultvalue='', description2='', yes='yes', no='no'):
        """
            yes: text displayed if value is set to "yes"
            no:  text displayed if value is set to "no"
        """
        self.name = name
        self.description = description
        self.defaultvalue = defaultvalue
        self.description2 = description2
        self.yes = yes
        self.no = no

    def get_value(self, form, msg, name=''):
        result = UI.get_value(self, form, msg, name)
        return bool(result)

    def render_value(self, value, fmt, name=''):
        if value:
            printvalue = self.yes
        else:
            printvalue = self.no
        return fmt.text("%s" % printvalue)
        
    def render_editform(self, value, fmt, name=''):
        if value:
            checked = 'checked="checked"'
        else:
            checked = ''
        return fmt.rawHTML('<input type="checkbox" name="%(name)s" value="True" %(checked)s>') \
                % {'name': name + self.name, 'checked': checked}


class SelectUI(UI):
    
    def __init__(self, name, description, defaultvalue, values,
                 description2=''):
        self.name = name
        self.description = description
        self.defaultvalue = defaultvalue
        self.values = values
        self.description2 = description2
        
    def render_value(self, value, fmt, name=''):
        if not value:
            value = self.defaultvalue
        for val, text in self.values:
            if unicode(value) == val:
                return fmt.text(text)
        raise ValueError

    def render_editform(self, value, fmt, name=''):
        value = unicode(value)
        return unicode(web.makeSelection(
            "%s%s" % (name, self.name),
            self.values, value))

    def get_value(self, form, msg, name=''):
        value = UI.get_value(self, form, msg, name)
        for val, text in self.values:
            if val==value:
                return value
        msg.append("Unexpected Value %s%s: %s" % (name,self.name,value))
        return ErrorData(value, "Unexpected Value %s%s: %s" % (name,self.name,value))

class SelectMultipleUI(UI):

    def __init__(self,name,description='',preselectedvalues=[], values=[], 
                    description2='',listed='comma', size=5):
        self.name = name
        self.description = description
        self.defaultvalue = preselectedvalues
        self.values = values
        self.description2 = description2
        self.listed = listed
        self.size = size
        
    def render_value(self, selectedvalues, fmt, name=''):
        result = []
        for val, text in self.values:
            if val in selectedvalues:
                result.append(text)
        if self.listed == 'comma':
            return fmt.text(', '.join(result))
        if self.listed == 'ul':
            html = [fmt.bullet_list(1)]
            for item in result:
                html.append(fmt.listitem(1))
                html.append(fmt.text(item))
                html.append(fmt.listitem(0))
            html.append(fmt.bullet_list(0))
            return ''.join(html)
    
    def render_editform(self, selectedvalues, fmt, name=''):
        html = ['<select name="%(name)s" size="%(size)s" multiple="multiple">\n' % 
            {'name': '%s%s' % (name, self.name),
             'size': self.size}]
        for val, text in self.values:
            selected = ''
            if val in selectedvalues:
                selected=' selected="selected"'
            html.append('   <option value="%(val)s" %(sel)s>%(text)s</option>\n' %
                    {'val' : val,
                     'sel' : selected, 
                     'text' : wikiutil.escape(text, quote=1)})
        html.append('</select>\n')
        return fmt.rawHTML(''.join(html))

    def get_value(self, form, msg, name=''):
        name = '%s%s' % (name, self.name)
        result = []
        allowedvalues = [val for val,text in self.values]
        for val in form.get(name,[]):
            if val in allowedvalues:
                result.append(val)
        return result





        
            
        


# ==============================================================
# == UI Elements
# ==============================================================

class EditButton(UI):

    def __init__(self, name, description, description2="",
                 fields=[]):
        self.name = name
        self.description = description
        self.description2 = description2
        self.fields = fields

    def render_value(self, value, fmt, name=''):
        return fmt.rawHTML('''<form method="post">
  <input type="hidden" name="action" value="form">
  <input type="hidden" name="do" value="%s">
  <input type="hidden" name="method" value="edit">
  <input type="submit" value="%s">%s
</form>''' % (self.name, self.description, self._render_fields(value)))

    def render_editform(self, value, fmt, name=''):
        return fmt.rawHTML('''<form method="post">
  <input type="hidden" name="action" value="form">
  <input type="hidden" name="data" value="%s">
  <input type="hidden" name="filter" value="%s">
  <input type="submit" value="%s">
</form>''' % (self.name, value, self.description2))

    def get_value(self, form, msg, name=''):
        return ''

    def _render_fields(self, value):
        if not (isinstance(value, list) or isinstance(value, tuple)):
            value = (value, )
        return '\n' + '\n'.join(['<input type="hidden" name="%s" value="%s"' %
                                 (field, wikiutil.escape(val, quote=1))
                                 for field, val in zip(self.fields, value)])
class GenericButton(UI):
    """ Renders a button
    The following values can be set either via the class var default_values,
    via keyword in the constructor or in the value which must be a dict:

    "action" : url to open
    "text" : label shown on button 
    "fields" : dict which is converted to hidden input tags
    "fields_html" : raw html that is generated out of the "fields" value
                    if not explicitly given
    "custom_html" : raw html (defaults to "")

    Subclasses may require additionally values and set some of the above 
    by program logic.
    """
    default_values = {
        "text" : "Submit",
        "action" : "",
        }
    
    defaultvalue = {} # used in empty edit forms
    
    def __init__(self, name, description='', description2='', **kw):
        self.name = name
        self.description = description
        self.description2 = description2
        self.keywords = self.default_values.copy() # copy class var 
        fields = self.keywords.get("fields", {}).copy()
        fields.update(kw.get("fields", {}))
        self.keywords.update(kw)
        self.keywords["fields"] = fields
        self.keywords.setdefault('name',name)
    
    def expand_value(self, value):
        kw = self.keywords.copy() 
        fields = kw.get("fields", {}).copy()
        fields.update(value.get("fields", {}))
        kw.update(value)
        kw["fields"] = fields
        
        kw.setdefault("custom_html", "")
        kw.setdefault("action", "")
        if kw["action"]:
            kw["action"] = ' action="%s"' % kw["action"]
            
        return kw
    
    def render(self, value, fmt, name=''):
        if (not value.has_key("fields_html") and
            value.has_key("fields")):  
            value["fields_html"] = '\n' + '\n'.join(
                ['  <input type="hidden" name="%s" value="%s">' %
                (wikiutil.escape(field, quote=1), 
                 wikiutil.escape(val, quote=1)) 
                for field, val in value["fields"].iteritems()]) 

        value.setdefault("fields_html", "")
        
        return fmt.rawHTML('''
<form%(action)s method="post">
  <input type="submit" value="%(text)s" name="%(name)s">%(fields_html)s%(custom_html)s
</form>''' % value)

    def render_value(self, value, fmt, name=''):
        value = self.expand_value(value)
        return self.render(value, fmt, name)
        
    def render_editform(self, value, fmt, name=''):
        value = self.expand_value(value)
        return self.render(value, fmt, name)
    
    def get_value(self, form, msg, name=''):
        return ''

               
class Button(GenericButton):
    """ Renders a button
    The following values must be set either via the class var default_values,
    via keyword in the constructor or in the value which must be a dict:

    "target" : dialog to invoke
    "method" : "view", "edit" or "save"
    "text" : label shown on button
    
    Additionally 
    "fields" : dict which is converted to hidden input tags
    "fields_html" : raw html that is generated out of the "fields" value
                    if not explicitly given
    "custom_html" : raw html (defaults to "")
    may be given.

    Subclasses may require additionally values and set some of the above 
    by program logic
    """
    default_values = {
        "text" : "Submit", # overwrite in constructor
        "dialog" : "", # overwrite in constructor
        "method" : "", # overwrite in constructor

        "fields" : {
            "action" : "form",
        }
    }
    
    def __init__(self, name, **kw):
        GenericButton.__init__(self, name, **kw)
         
    def render_value(self, value, fmt, name=''):
        value = self.expand_value(value)
        if value["dialog"]:
            value["fields"].setdefault("do", value["dialog"])
        if value["method"]:
            value["fields"].setdefault("method", value["method"])

        return self.render(value, fmt, name='')
        

    render_editform = render_value
        
class EditSaveButton(Button):
    
    default_values = {
        "edit_text" : "Edit", # overwrite via constructor for i18n
        "save_text" : "Save", # overwrite via constructor for i18n
        "dialog" : "", # overwrite via constructor
        "fields" : { "action" : "form" },
        }
        
    def render_value(self, value, fmt, name=''):
        value = self.expand_value(value)
        value["fields"].setdefault("method", "edit")
        value.setdefault("text", value["edit_text"])
        if value["dialog"]:
            value["fields"].setdefault("do", value["dialog"])
        return self.render(value, fmt, name)

    def render_editform(self, value, fmt, name=''):
        value = self.expand_value(value)
        value.setdefault("method", "save")
        value.setdefault("text", value["save_text"])
        if value["dialog"]:
            value["fields"].setdefault("do", value["dialog"]) 
        return self.render(value, fmt, name)
    
class ToggleButton(Button):
    """
    uses "on" parameter in value
    """

    default_values = {
        "on_text" : "On", # overwrite in constructor for i18n
        "off_text" : "Off", # overwrite in constructor for i18n
        "dialog" : "", # overwrite via constructor
        "fields" : { "action" : "form" },
        }

    def render_editform(self, value, fmt, name=''):
        value = self.expand_value(value)
        value["fields"].setdefault("method", "save")
        if value["dialog"]:
            value["fields"].setdefault("do", value["dialog"])
        if value["on"]:
            value.setdefault("text", self.keywords["on_text"])
            value["fields"].setdefault("on", "0")
        else:
            value.setdefault("text", self.keywords["off_text"])
            value["fields"].setdefault("on", "1")
            
        return self.render(value, fmt, name)
 
    render_value = render_editform    
        
# ==============================================================
# == DataSets
# ==============================================================

class SetUI(UI):
    
    def __init__(self, name='', description='', description2='', cssclass=''):
        self.filters = []
        self.columns = []
        self.name = name
        self.description = description  # rendered as caption
        self.description2 = description2
        self.cssclass = cssclass  # rendered as cssclass for the table
        self.fixed = []
        
    def add(self, datatype):
        self.columns.append(datatype)

    def add_fixed(self, name, value):
        self.fixed.append((name, value))

    def remove(self, maskname):
        self.columns = filter(lambda x: x.name != maskname, self.columns)

    def __del__(self, index):
        del self.columns[index]
    
class FormUI(SetUI):

    defaultvalue = {}
    
    def render_value(self, values, fmt, name=''):

        name = name + self.name + '-'

        #if len(value) != len(self.columns):
        #    raise ValueError, "Length of data must match number of columns"

        result = []

        if self.cssclass:
            tableattrs = {'tableclass': self.cssclass}
            result.append(fmt.div(1,**{'class' : self.cssclass}))
        else:
            tableattrs = {}

        result.append(fmt.table(1,attrs=tableattrs,caption=self.description))

        for column in self.columns:
            if not column.is_novalue():
                value = values[column.name]
            if column.is_hidden():
                result.append(column.render_value(value, fmt, name))
                continue

            result.append(fmt.table_row(1))
            
            if column.is_novalue():
                cellattrs={'colspan' : '2'}
            else:
                cellattrs = {}
                
            result.append(fmt.table_cell(1,attrs=cellattrs))

            result.append(fmt.strong(1))
            result.append(fmt.text(column.description))
            result.append(fmt.strong(0))
            result.append(fmt.table_cell(0))

            if not column.is_novalue():
                result.append(fmt.table_cell(1))
                result.append(column.render_value(value, fmt, name))
                result.append(fmt.table_cell(0))

            result.append(fmt.table_row(0))

        result.append(fmt.table(0))
        if self.cssclass:
            result.append(fmt.div(0))

        return "".join(result)

    def render_editform(self, values, fmt, name=''):
        #if len(value) != len(self.columns):
        #    raise ValueError, "Length of data must match number of columns"

        my_name = name + self.name + '-'

        result = []

        #if not name:
        #    result.append(fmt.rawHTML('''<form method="post">
        #<input type="hidden" name="action" value="form"'''))
        result.append(fmt.table(1))

        for column in self.columns:
            value = values.get(column.name, column.defaultvalue)
            # just emit form elements if column is hidden
        
            if column.is_hidden():
                attrs = {"rowstyle" : "display:none"}
            else:
                attrs = {}

            if column.is_novalue():
                cellattrs={'colspan' : '2'}
            else:
                cellattrs = {}
            # check if value was wrong
            error = isinstance(value, ErrorData)
            # XXX highligh table cell

            # render table
            result.append(fmt.table_row(1, attrs=attrs))
            
            result.append(fmt.table_cell(1,attrs=cellattrs))
            result.append(fmt.strong(1))
            result.append(fmt.text(column.description))
            result.append(fmt.strong(0))
            result.append(fmt.table_cell(0))
            
            if not column.is_novalue():
                result.append(fmt.table_cell(1))
                result.append(column.render_editform(value, fmt, my_name))
                result.append(fmt.table_cell(0))

            if column.description2:
                result.append(fmt.table_cell(1))
                result.append(fmt.text(column.description2))
                result.append(fmt.table_cell(0))
            
            result.append(fmt.table_row(0))

        result.append(fmt.table(0))

        #if not name:
        #    result.append(fmt.paragraph(1))
        #    result.append(fmt.rawHTML("""
#<input type="submit" value=" Save ">
#</form>"""))
        #     result.append(fmt.paragraph(0))

        return "".join(result)

    def get_value(self, form, msg, name=''):
        name = name + self.name + '-'
        result = {}
        for column in self.columns:
            result[column.name] = column.get_value(form, msg, name)
        return result


class TableUI(SetUI):

    defaultvalue = []
    
    def render_value(self, values, fmt, name=''):
        result = []

        my_name = name + self.name + '-'

        if self.cssclass:
            tableattrs = {'tableclass': self.cssclass}
            result.append(fmt.div(1,**{'class' : self.cssclass}))
        else:
            tableattrs = {}
        
        result.append(fmt.table(1, attrs=tableattrs, caption=self.description))

        result.append(fmt.table_row(1))
        for column in self.columns:
            if column.is_hidden():
                continue
            result.append(fmt.table_cell(1))
            result.append(fmt.strong(1))
            result.append(fmt.text(column.description))
            result.append(fmt.strong(0))
            result.append(fmt.table_cell(0))
        result.append(fmt.table_row(0))

        i = 0 
        for row in values:
            result.append(fmt.table_row(1))

            for column in self.columns:
                value = row[column.name]
                if not column.is_hidden():                    
                    result.append(fmt.table_cell(1))
                result.append(column.render_value(value, fmt,
                                                  '%s%s-' % (my_name, i)))
                if not column.is_hidden():
                    result.append(fmt.table_cell(0))

            result.append(fmt.table_row(0))
            i += 1
                             
        result.append(fmt.table(0))
        if self.cssclass:
            result.append(fmt.div(0))

        return "".join(result)

    def render_editform(self, values, fmt, name=''):
        result = []

        #if not name:
        #    result.append(fmt.rawHTML('''<form method="post">
        #<input type="hidden" name="action" value="savedata" />'''))

        my_name = name + self.name + '-'
        result.append(fmt.table(1))
        
        result.append(fmt.table_row(1))
        for column in self.columns:
            if column.is_hidden():
                attrs = {"style" : "visibility:hidden"}
            else:
                attrs = {}
            result.append(fmt.table_cell(1, attrs=attrs))
            result.append(fmt.strong(1))
            result.append(fmt.text(column.description))
            result.append(fmt.strong(0))
            result.append(fmt.table_cell(0))
        result.append(fmt.table_row(0))

        i = 0 
        for row in values:
            result.append(fmt.table_row(1))
            result.append(fmt.rawHTML(
                '<input type="hidden" name="%s%s" value="1">' % (my_name, i))) 
            for column in self.columns:                
                value = row[column.name]
                # check if value was wrong
                error = isinstance(value, ErrorData)
                # XXX highligh table cell
                
                if not column.is_hidden():
                    result.append(fmt.table_cell(1))
                    
                result.append(column.render_editform(value, fmt,
                                                     '%s%s-' % (my_name, i)))
                if not column.is_hidden():
                    result.append(fmt.table_cell(0))

            result.append(fmt.table_row(0))
            i += 1
                             
        result.append(fmt.table(0))

        #if not name:
        #    result.append(fmt.paragraph(1))
        #    result.append(fmt.rawHTML("""
#<input type="submit" value=" Save ">
#</form>"""))
        #    result.append(fmt.paragraph(0))

        return "".join(result)

    def get_value(self, form, msg, name=''):
        my_name = name + self.name + '-'
        i = 0
        result = []
        print "%s%s-" % (my_name, i)
        while form.has_key("%s%s" % (my_name, i)):
            print my_name, i
            row = {}
            for column in self.columns:
                row[column.name] = column.get_value(form, msg, 
                        "%s%s-" % (my_name, i))
            result.append(row)
            i += 1
        return result

class RepeatUI(TableUI):

    def __init__():
        pass

    def render_value(self, values, fmt, name=''):
        result = []
        my_name = name + self.name + '-'
         
        for nr, value in enumerate(values):
            result.append(self.mask.render_value(value, fmt,
                "%s%s-" % (my_name, i)))
        return ''.join(result)


    def render_editform(self, values, fmt, name=''):
        result = []
        my_name = name + self.name + '-'
         
        for nr, value in enumerate(values):
            result.append(self.mask.render_editform(value, fmt,
                "%s%s-" % (my_name, i)))
        return ''.join(result)

    def get_value(self, form, msg, name=''):
        my_name = name + self.name + '-'
        i = 0
        result = []
        while form.has_key("%s%s" % (my_name, i)):
            result.append(self.mask.get_value(form, msg, "%s%s-" % (my_name,
                i)))
            i += 1
        return result
            


            
